diff --git a/app/global.css b/app/global.css index 542236c77..5ab99f902 100644 --- a/app/global.css +++ b/app/global.css @@ -2,7 +2,6 @@ @tailwind components; @tailwind utilities; - @layer base { :root { --background: 40 14.3% 95.9%; @@ -72,7 +71,6 @@ .dark .bitcoin { --primary: 0 0% 100%; --icon: 0 0% 100%; - } } @@ -124,22 +122,42 @@ a[aria-label="Hiro Platform"] { padding: 0; } - a[aria-label="Hiro Platform"] div:hover { background-color: hsla(var(--muted-foreground) / 0.1); } -h1, h2, h3, h4, h5, h6, code, button, .step { +h1, +h2, +h3, +h4, +h5, +h6, +code, +button, +.step { font-family: var(--font-aeonikFono), sans-serif; } -a, h1 a, h2 a, h3 a, h4 a, h5 a, h6 a, option { +a, +h1 a, +h2 a, +h3 a, +h4 a, +h5 a, +h6 a, +option { font-family: var(--font-aeonikFono), sans-serif; } /* TODO: div.prose is for targeting the title, need to fix this approach */ /* TODO: div.prose .flex-1 span is for targeting the text inside the component */ -a, p, li, table, input, div.prose .w-0, div.prose .flex-1 span { +a, +p, +li, +table, +input, +div.prose .w-0, +div.prose .flex-1 span { font-family: var(--font-inter), sans-serif; } @@ -177,7 +195,14 @@ p { line-height: 1.75; } -body > div.guides > div > article > div.prose > figure:nth-child(37) > div > div { +body + > div.guides + > div + > article + > div.prose + > figure:nth-child(37) + > div + > div { background: hsl(var(--code)); } @@ -234,7 +259,15 @@ nav.container > *:nth-last-child(2):hover { border-bottom-color: hsl(var(--primary)); } -nav.container a, figure, pre, pre a, code, code a, th, code > span, .not-prose div { +nav.container a, +figure, +pre, +pre a, +code, +code a, +th, +code > span, +.not-prose div { font-family: var(--font-aeonikFono), sans-serif; } @@ -242,7 +275,6 @@ div.flex.flex-row.items-center.gap-2.border-b.bg-muted.px-4.py-1\.5 { border-bottom: none; } - a[aria-label="Hiro Platform"] div svg { width: 0.75rem; height: 0.75rem; @@ -252,21 +284,32 @@ nav a[href="/guides"] { text-decoration: underline; } -[data-radix-scroll-area-viewport] .flex.flex-col.gap-8.pb-10.pt-4.max-md\:px-4.md\:pr-3.md\:pt-10 { - gap: 0; +[data-radix-scroll-area-viewport] + .flex.flex-col.gap-8.pb-10.pt-4.max-md\:px-4.md\:pr-3.md\:pt-10 { + gap: 0; } /* Target the header toggle */ -body > div > header > nav > div.rounded-md.border.bg-background.p-1.text-sm.text-muted-foreground.max-md\:absolute.max-md\:left-\[50\%\].max-md\:translate-x-\[-50\%\] > a.rounded-md.px-2.py-1.transition-colors.hover\:text-accent-foreground.bg-background.text-accent-foreground { +body + > div + > header + > nav + > div.rounded-md.border.bg-background.p-1.text-sm.text-muted-foreground.max-md\:absolute.max-md\:left-\[50\%\].max-md\:translate-x-\[-50\%\] + > a.rounded-md.px-2.py-1.transition-colors.hover\:text-accent-foreground.bg-background.text-accent-foreground { background-color: hsl(var(--inverted)); color: hsl(var(--background)); } /* Target the search component */ -body > div > header > nav > div.flex.flex-1.flex-row.items-center.justify-end.md\:gap-2 > button.inline-flex.w-full.max-w-\[240px\].items-center.gap-2.rounded-full.border.bg-secondary\/50.p-1\.5.text-sm.text-muted-foreground.transition-colors.hover\:bg-accent.hover\:text-accent-foreground.max-md\:hidden { - background-color: hsl(var(--background)) +body + > div + > header + > nav + > div.flex.flex-1.flex-row.items-center.justify-end.md\:gap-2 + > button.inline-flex.w-full.max-w-\[240px\].items-center.gap-2.rounded-full.border.bg-secondary\/50.p-1\.5.text-sm.text-muted-foreground.transition-colors.hover\:bg-accent.hover\:text-accent-foreground.max-md\:hidden { + background-color: hsl(var(--background)); } p.first\:mt-0 { @@ -288,7 +331,9 @@ p.first\:mt-0:before { margin: 0; } -div.not-prose, div.prose-no-margin, div[role="tablist"] { +div.not-prose, +div.prose-no-margin, +div[role="tablist"] { background: hsl(var(--background)); } @@ -303,7 +348,8 @@ form.not-prose.flex.flex-col.gap-4.rounded-lg.border.bg-card.p-4 { } /* overrides the background of APIInfo and adjusts sticky header on scroll */ -div.prose-no-margin .sticky.top-24.z-\[2\].flex.flex-row.items-center.gap-2.rounded-lg.border.bg-card.p-3.md\:top-10 { +div.prose-no-margin + .sticky.top-24.z-\[2\].flex.flex-row.items-center.gap-2.rounded-lg.border.bg-card.p-3.md\:top-10 { background: hsl(var(--background)); top: 4.5rem; } @@ -318,21 +364,24 @@ div.prose-no-margin .sticky.top-24.z-\[2\].flex.flex-row.items-center.gap-2.roun } /* Style for the POST span */ -.sticky.top-24.z-\[2\].flex.flex-row.items-center.gap-2.rounded-lg.border.bg-card.p-3.md\:top-10 > span:first-child { +.sticky.top-24.z-\[2\].flex.flex-row.items-center.gap-2.rounded-lg.border.bg-card.p-3.md\:top-10 + > span:first-child { position: absolute; top: 0.75rem; left: 0.75rem; } /* Style for the button */ -.sticky.top-24.z-\[2\].flex.flex-row.items-center.gap-2.rounded-lg.border.bg-card.p-3.md\:top-10 > button { +.sticky.top-24.z-\[2\].flex.flex-row.items-center.gap-2.rounded-lg.border.bg-card.p-3.md\:top-10 + > button { position: absolute; top: 0.75rem; right: 0.75rem; } /* Target the inner div containing the endpoint spans */ -.sticky.top-24.z-\[2\].flex.flex-row.items-center.gap-2.rounded-lg.border.bg-card.p-3.md\:top-10 > div { +.sticky.top-24.z-\[2\].flex.flex-row.items-center.gap-2.rounded-lg.border.bg-card.p-3.md\:top-10 + > div { display: flex; flex-wrap: wrap; width: 100%; @@ -348,7 +397,10 @@ div.prose div.footer.not-prose { font-family: var(--font-aeonikFono), sans-serif !important; } -.steps > div:nth-child(2) > figure:nth-child(3) > div.flex.flex-row.items-center.gap-2.border-b.bg-muted.px-4.py-1\.5 { +.steps + > div:nth-child(2) + > figure:nth-child(3) + > div.flex.flex-row.items-center.gap-2.border-b.bg-muted.px-4.py-1\.5 { background: hsl(var(--background)) !important; } @@ -363,7 +415,7 @@ div.api-example div.max-xl\:hidden > div:nth-child(1) { background-color: hsl(var(--inverted)); } -.prose :where(a):not(:where([class~="not-prose"],[class~="not-prose"] *)) { +.prose :where(a):not(:where([class~="not-prose"], [class~="not-prose"] *)) { text-decoration-color: var(--secondary) !important; } @@ -381,8 +433,8 @@ div.api-example div.max-xl\:hidden > div:nth-child(1) { } .size-10 { - width: 2.5rem/* 20px */; - height: 2.5rem/* 20px */; + width: 2.5rem /* 20px */; + height: 2.5rem /* 20px */; } /* Add a deprecated strike through to sidebar links */ @@ -402,4 +454,43 @@ a[href="/stacks/api/fees/fee-rate"] { div.divide-y.divide-border.overflow-hidden.rounded-lg.border.bg-card { background: hsl(var(--background)); -} \ No newline at end of file +} + +div.clarity-playground { + --line-height: 21px; + + & > * { + box-sizing: border-box; + } + pre.nd-codeblock { + overflow: hidden; + + span.line { + display: inline; + height: var(--line-height); + line-height: var(--line-height); + border: none; + } + } + + code:has(textarea) { + display: block; + background: none; + border: none; + line-height: var(--line-height); + overflow: hidden; + } + + textarea { + position: absolute; + width: 100%; + top: 16px; + left: 16px; + resize: none; + outline: none; + + background: none; + color: transparent; + caret-color: white; + } +} diff --git a/components/code/clarinet-sdk.tsx b/components/code/clarinet-sdk.tsx deleted file mode 100644 index fc0a0543b..000000000 --- a/components/code/clarinet-sdk.tsx +++ /dev/null @@ -1,130 +0,0 @@ -"use client"; - -import React from "react"; -import { cn } from "@/utils/cn"; -import { Button } from "@/components/ui/button"; -import * as Base from "fumadocs-ui/components/codeblock"; - -import { initSimnet } from "@hirosystems/clarinet-sdk-browser"; -import { Cl } from "@stacks/transactions"; - -import { getHighlighter } from "shiki"; - -// TODO: WIP: testing out new Clarinet JS SDK browser lib -export const ClarinetSDK: React.FC = () => { - const [simnet, setSimnet] = React.useState(); - const [html, setHtml] = React.useState(); - const [evaluatedResponse, setEvaluatedResponse] = React.useState(); - - async function showMe() { - const simnet = await initSimnet(); - await simnet.initEmtpySession(); - - simnet.setEpoch("2.5"); - const result = - simnet.runSnippet(`(define-map Users uint {address: principal}) - (map-insert Users u1 { address: tx-sender }) - (map-get? Users u1) - `) as any; - - const highlighter = await getHighlighter({ - langs: ["bash", "ts", "tsx", "clarity"], - themes: ["github-light", "github-dark"], - }); - const res = highlighter.codeToHtml(Cl.prettyPrint(result, 2), { - lang: "clarity", - defaultColor: false, - themes: { - light: "github-light", - dark: "github-dark", - }, - transformers: [ - { - name: "remove-pre", - root: (root) => { - if (root.children[0].type !== "element") return; - - return { - type: "root", - children: root.children[0].children, - }; - }, - }, - ], - }); - setEvaluatedResponse(res); - } - - async function run() { - const simnet = await initSimnet(); - await simnet.initEmtpySession(); - - simnet.setEpoch("2.5"); - const result = - simnet.runSnippet(`(define-map Users uint {address: principal}) - (map-insert Users u1 { address: tx-sender }) - (map-get? Users u1) - `) as any; - console.log(Cl.prettyPrint(result, 2)); - setSimnet(simnet); - - const codeResponse = await fetch("/scripts/hello-world.clar"); - const code = await codeResponse.text(); - - const highlighter = await getHighlighter({ - langs: ["bash", "ts", "tsx", "clarity"], - themes: ["github-light", "github-dark"], - }); - - const html = highlighter.codeToHtml(code, { - lang: "clarity", - defaultColor: false, - themes: { - light: "github-light", - dark: "github-dark", - }, - transformers: [ - { - name: "remove-pre", - root: (root) => { - if (root.children[0].type !== "element") return; - - return { - type: "root", - children: root.children[0].children, - }; - }, - }, - ], - }); - setHtml(html); - } - - React.useEffect(() => { - run(); - }, []); - - return ( - <> - - - - - {evaluatedResponse ? ( - - - - ) : null} - - ); -}; diff --git a/components/code/clarity-playground.tsx b/components/code/clarity-playground.tsx new file mode 100644 index 000000000..f3ebca64c --- /dev/null +++ b/components/code/clarity-playground.tsx @@ -0,0 +1,125 @@ +"use client"; + +import React, { useEffect } from "react"; +import { cn } from "@/utils/cn"; +import { Button } from "@/components/ui/button"; +import * as Base from "fumadocs-ui/components/codeblock"; + +import { initSimnet } from "@hirosystems/clarinet-sdk-browser"; +import { Cl } from "@stacks/transactions"; + +import { + BundledLanguage, + BundledTheme, + getSingletonHighlighter, + HighlighterGeneric, +} from "shiki"; + +type ClarityPlaygroundProps = { + snippet: string; +}; + +let memoizedHighlighter: HighlighterGeneric; +async function getHighlighter() { + if (memoizedHighlighter) return memoizedHighlighter; + + memoizedHighlighter = await getSingletonHighlighter({ + langs: ["clarity"], + themes: ["github-light", "github-dark"], + }); + return memoizedHighlighter; +} + +async function highlight(snippet: string) { + const highlighter = await getHighlighter(); + return highlighter.codeToHtml(snippet, { + lang: "clarity", + defaultColor: false, + themes: { + light: "github-light", + dark: "github-dark", + }, + transformers: [ + { + name: "remove-pre", + root: (root) => { + if (root.children[0].type !== "element") return; + return { + type: "root", + children: root.children[0].children, + }; + }, + }, + ], + }); +} + +// TODO: WIP: testing out new Clarinet JS SDK browser lib +export const ClarityPlayground: React.FC = ({ + snippet, +}) => { + const [snippetValue, setSnippetValue] = React.useState(snippet); + const [snippetRows, setSnippetRows] = React.useState( + snippetValue.split("\n").length, + ); + const [snippetHTML, setSnippetHTML] = React.useState(""); + const [resultHTML, setResultHTML] = React.useState(); + + useEffect(() => { + highlight(snippetValue).then(setSnippetHTML); + setSnippetRows(snippetValue.split("\n").length); + }, [snippetValue]); + + async function run() { + const simnet = await initSimnet(); + await simnet.initEmtpySession(); + + simnet.setEpoch("2.5"); + const { result } = simnet.execute(snippetValue); + const resultString = Cl.prettyPrint(result, 2); + + highlight(resultString).then(setResultHTML); + } + + return ( + <> +
+ + + +
+          
+