Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: clarity-playground component #781

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 90 additions & 94 deletions components/code/clarinet-sdk.tsx
Original file line number Diff line number Diff line change
@@ -1,128 +1,124 @@
"use client";

import React from "react";
import React, { useEffect, useLayoutEffect } 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";
import {
BundledLanguage,
BundledTheme,
getSingletonHighlighter,
HighlighterGeneric,
} from "shiki";

// TODO: WIP: testing out new Clarinet JS SDK browser lib
export const ClarinetSDK: React.FC = () => {
const [simnet, setSimnet] = React.useState<any>();
const [html, setHtml] = React.useState<any>();
const [evaluatedResponse, setEvaluatedResponse] = React.useState<any>();

async function showMe() {
const simnet = await initSimnet();
await simnet.initEmtpySession();
type ClarityPlaygroundProps = {
snippet: string;
};

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,
};
},
let memoizedHighlighter: HighlighterGeneric<BundledLanguage, BundledTheme>;
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,
};
},
],
});
setEvaluatedResponse(res);
}
},
],
});
}

// TODO: WIP: testing out new Clarinet JS SDK browser lib
export const ClarityPlayground: React.FC<ClarityPlaygroundProps> = ({
snippet,
}) => {
const [snippetValue, setSnippetValue] = React.useState<string>(snippet);
const [snippetRows, setSnippetRows] = React.useState<number>(
snippetValue.split("\n").length,
);
const [snippetHTML, setSnippetHTML] = React.useState<string>("");
const [resultHTML, setResultHTML] = React.useState<string>();

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.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);
}
const { result } = simnet.execute(snippetValue);
const resultString = Cl.prettyPrint(result, 2);

React.useEffect(() => {
run();
}, []);
highlight(resultString).then(setResultHTML);
}

return (
<>
<div
className="w-full"
// style={{ height: 24 * snippetRows, lineHeight: "24px" }}
>
{/* className="absolute font-sm t-0 b-0 l-0 r-0" */}
<Base.CodeBlock style={{ lineHeight: "24px" }}>
<Base.Pre
dangerouslySetInnerHTML={{ __html: snippetHTML }}
></Base.Pre>
</Base.CodeBlock>

{/* className="absolute t-0 b-0 l-0 r-0 background-none p-2 font-sm font-mono opacity-50" */}
<textarea
className="background-none p-2 font-sm font-mono display-block w-full"
style={{ lineHeight: "24px" }}
rows={snippetRows}
value={snippetValue}
onChange={(e) => setSnippetValue(e.target.value)}
/>
</div>

<Button
className={cn(
"px-5 py-2 text-sm leading-5 rounded-full font-semibold z-10",
"bg-neutral-900 text-white",
"dark:bg-white dark:text-neutral-900",
"hover:bg-neutral-900/90 dark:hover:bg-gray-100/90"
"hover:bg-neutral-900/90 dark:hover:bg-gray-100/90",
)}
onClick={showMe}
onClick={run}
>
Run
</Button>
<Base.CodeBlock>
<Base.Pre dangerouslySetInnerHTML={{ __html: html }} />
</Base.CodeBlock>
{evaluatedResponse ? (
<Base.CodeBlock allowCopy={false}>
<Base.Pre dangerouslySetInnerHTML={{ __html: evaluatedResponse }} />

{resultHTML ? (
<Base.CodeBlock>
<Base.Pre dangerouslySetInnerHTML={{ __html: resultHTML }} />
</Base.CodeBlock>
) : null}
</>
Expand Down
18 changes: 12 additions & 6 deletions components/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,17 @@ const itemVariants = cva(
true: "bg-accent text-accent-foreground",
},
},
}
},
);

const appId = process.env.NEXT_PUBLIC_ALGOLIA_APP_ID;
const apiKey = process.env.NEXT_PUBLIC_ALGOLIA_API_KEY;
const indexName = process.env.NEXT_PUBLIC_ALGOLIA_INDEX;

if (!appId || !apiKey || !indexName) throw new Error("Algolia credentials");

const client = algo(appId, apiKey);

const index = client.initIndex(indexName);
const index =
appId && apiKey && indexName
? algo(appId, apiKey).initIndex(indexName)
: null;

export default function CustomSearchDialog(props: SharedProps): JSX.Element {
const defaultTag = useMode() ?? "stacks";
Expand All @@ -38,6 +37,13 @@ export default function CustomSearchDialog(props: SharedProps): JSX.Element {
setTag(defaultTag);
}, [defaultTag]);

if (!index) {
console.warn(
"Algolia search is disabled. Set up the environment variables to use it.",
);
return <div />;
}

Comment on lines +40 to +46
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unbrelated to this PR, just did it to help with local development

return (
<SearchDialog
index={index}
Expand Down
24 changes: 19 additions & 5 deletions content/docs/stacks/clarity/basic-arithmetic.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ title: Basic arithmetic
description: Brief overview of arithmetic operations in Clarity and their importance in smart contract development.
---

import { ClarityPlayground } from "../../../../components/code/clarinet-sdk";

Smart contracts often need to perform calculations, whether it's for token balances, voting weights, or complex financial operations. Understanding Clarity's arithmetic functions is crucial for implementing these features efficiently and securely.

## Why these functions matter

Clarity's arithmetic functions are designed with blockchain-specific considerations in mind:

1. Overflow protection: Unlike some languages, Clarity prevents integer overflow by default, enhancing contract security.
Expand All @@ -22,7 +25,8 @@ Clarity's arithmetic functions are designed with blockchain-specific considerati

**When**: Use when you need to increase values, combine quantities, or perform any additive calculation.

**Best practices**:
**Best practices**:

- Consider overflow protection
- Use with uint for non-negative values like token amounts

Expand All @@ -42,6 +46,13 @@ Clarity's arithmetic functions are designed with blockchain-specific considerati
)
```

export const snippet = `(define-public (add (a int) (b int))
(ok (+ a b))
)
(add 1 2)`;

<ClarityPlayground snippet={snippet} />

### 2. Subtraction (-)

**What**: Subtracts integers from the first argument.
Expand All @@ -50,7 +61,8 @@ Clarity's arithmetic functions are designed with blockchain-specific considerati

**When**: Use when you need to decrease values, calculate differences, or perform any subtractive operation.

**Best practices**:
**Best practices**:

- Guard against underflow
- Consider using uint for values that shouldn't go negative

Expand All @@ -75,15 +87,16 @@ Clarity's arithmetic functions are designed with blockchain-specific considerati
)
```

### 3. Multiplication (*)
### 3. Multiplication (\*)

**What**: Multiplies two or more integers.

**Why**: Important for calculations involving scaling, rates, or proportions.

**When**: Use when you need to scale values, calculate rates, or perform any multiplicative operation.

**Best practices**:
**Best practices**:

- Consider overflow protection
- Use with uint for non-negative values like token amounts

Expand All @@ -109,7 +122,8 @@ Clarity's arithmetic functions are designed with blockchain-specific considerati

**When**: Use when you need to divide values, calculate rates, or perform any division operation.

**Best practices**:
**Best practices**:

- Guard against division by zero
- Consider using uint for non-negative values like token amounts

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
},
"dependencies": {
"@code-hike/mdx": "^0.9.0",
"@hirosystems/clarinet-sdk-browser": "^2.7.0-beta2",
"@hirosystems/clarinet-sdk-browser": "^2.9.0",
"@next/env": "^14.0.0",
"@next/third-parties": "^14.2.4",
"@radix-ui/react-avatar": "^1.0.4",
Expand Down
Loading