@ngrok/mantle 0.66.7 → 0.66.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/alert-dialog.d.ts +8 -8
- package/dist/alert.d.ts +1 -1
- package/dist/{button-CKL-3sIr.d.ts → button-DQO0IuzC.d.ts} +7 -7
- package/dist/button.d.ts +2 -2
- package/dist/checkbox.d.ts +1 -1
- package/dist/code-block.d.ts +1 -1
- package/dist/code-block.js.map +1 -1
- package/dist/command.d.ts +12 -12
- package/dist/data-table.d.ts +1 -1
- package/dist/{dialog-BuD_JQf_.d.ts → dialog-BAZ_LYI4.d.ts} +2 -2
- package/dist/dialog.d.ts +1 -1
- package/dist/{icon-button-2r6S3HVA.d.ts → icon-button-ikEU0Ctp.d.ts} +2 -2
- package/dist/{index-DWqhfw9n.d.ts → index-aG53Gdqx.d.ts} +4 -4
- package/dist/input.d.ts +1 -1
- package/dist/mantle.css +63 -0
- package/dist/sheet.d.ts +1 -1
- package/dist/split-button.d.ts +2 -2
- package/dist/theme.d.ts +87 -17
- package/dist/theme.js +1 -1
- package/dist/theme.js.map +1 -1
- package/package.json +4 -4
- package/dist/style.css +0 -62
package/dist/alert-dialog.d.ts
CHANGED
|
@@ -147,9 +147,9 @@ declare const AlertDialog: {
|
|
|
147
147
|
* ```
|
|
148
148
|
*/
|
|
149
149
|
readonly Action: react.ForwardRefExoticComponent<(Omit<react.ClassAttributes<HTMLButtonElement> & react.ButtonHTMLAttributes<HTMLButtonElement> & Partial<DeepNonNullable<class_variance_authority0.VariantProps<(props?: ({
|
|
150
|
-
appearance?: "
|
|
150
|
+
appearance?: "filled" | "link" | "ghost" | "outlined" | null | undefined;
|
|
151
151
|
isLoading?: boolean | null | undefined;
|
|
152
|
-
priority?: "
|
|
152
|
+
priority?: "danger" | "neutral" | "default" | null | undefined;
|
|
153
153
|
} & class_variance_authority_types0.ClassProp) | undefined) => string>>> & {
|
|
154
154
|
icon?: ReactNode;
|
|
155
155
|
iconPlacement?: "start" | "end";
|
|
@@ -157,9 +157,9 @@ declare const AlertDialog: {
|
|
|
157
157
|
asChild: true;
|
|
158
158
|
type?: ComponentProps<"button">["type"];
|
|
159
159
|
}, "ref"> | Omit<react.ClassAttributes<HTMLButtonElement> & react.ButtonHTMLAttributes<HTMLButtonElement> & Partial<DeepNonNullable<class_variance_authority0.VariantProps<(props?: ({
|
|
160
|
-
appearance?: "
|
|
160
|
+
appearance?: "filled" | "link" | "ghost" | "outlined" | null | undefined;
|
|
161
161
|
isLoading?: boolean | null | undefined;
|
|
162
|
-
priority?: "
|
|
162
|
+
priority?: "danger" | "neutral" | "default" | null | undefined;
|
|
163
163
|
} & class_variance_authority_types0.ClassProp) | undefined) => string>>> & {
|
|
164
164
|
icon?: ReactNode;
|
|
165
165
|
iconPlacement?: "start" | "end";
|
|
@@ -204,9 +204,9 @@ declare const AlertDialog: {
|
|
|
204
204
|
* ```
|
|
205
205
|
*/
|
|
206
206
|
readonly Cancel: react.ForwardRefExoticComponent<(Omit<react.ClassAttributes<HTMLButtonElement> & react.ButtonHTMLAttributes<HTMLButtonElement> & Partial<DeepNonNullable<class_variance_authority0.VariantProps<(props?: ({
|
|
207
|
-
appearance?: "
|
|
207
|
+
appearance?: "filled" | "link" | "ghost" | "outlined" | null | undefined;
|
|
208
208
|
isLoading?: boolean | null | undefined;
|
|
209
|
-
priority?: "
|
|
209
|
+
priority?: "danger" | "neutral" | "default" | null | undefined;
|
|
210
210
|
} & class_variance_authority_types0.ClassProp) | undefined) => string>>> & {
|
|
211
211
|
icon?: ReactNode;
|
|
212
212
|
iconPlacement?: "start" | "end";
|
|
@@ -214,9 +214,9 @@ declare const AlertDialog: {
|
|
|
214
214
|
asChild: true;
|
|
215
215
|
type?: ComponentProps<"button">["type"];
|
|
216
216
|
}, "ref"> | Omit<react.ClassAttributes<HTMLButtonElement> & react.ButtonHTMLAttributes<HTMLButtonElement> & Partial<DeepNonNullable<class_variance_authority0.VariantProps<(props?: ({
|
|
217
|
-
appearance?: "
|
|
217
|
+
appearance?: "filled" | "link" | "ghost" | "outlined" | null | undefined;
|
|
218
218
|
isLoading?: boolean | null | undefined;
|
|
219
|
-
priority?: "
|
|
219
|
+
priority?: "danger" | "neutral" | "default" | null | undefined;
|
|
220
220
|
} & class_variance_authority_types0.ClassProp) | undefined) => string>>> & {
|
|
221
221
|
icon?: ReactNode;
|
|
222
222
|
iconPlacement?: "start" | "end";
|
package/dist/alert.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as SvgAttributes } from "./types-Dh4BVhXC.js";
|
|
2
2
|
import { t as WithAsChild } from "./as-child-XMVTepJu.js";
|
|
3
|
-
import { n as IconButtonProps } from "./icon-button-
|
|
3
|
+
import { n as IconButtonProps } from "./icon-button-ikEU0Ctp.js";
|
|
4
4
|
import * as react from "react";
|
|
5
5
|
import { ComponentProps, HTMLAttributes, ReactNode } from "react";
|
|
6
6
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
@@ -7,9 +7,9 @@ import * as class_variance_authority_types0 from "class-variance-authority/types
|
|
|
7
7
|
|
|
8
8
|
//#region src/components/button/button.d.ts
|
|
9
9
|
declare const buttonVariants: (props?: ({
|
|
10
|
-
appearance?: "
|
|
10
|
+
appearance?: "filled" | "link" | "ghost" | "outlined" | null | undefined;
|
|
11
11
|
isLoading?: boolean | null | undefined;
|
|
12
|
-
priority?: "
|
|
12
|
+
priority?: "danger" | "neutral" | "default" | null | undefined;
|
|
13
13
|
} & class_variance_authority_types0.ClassProp) | undefined) => string;
|
|
14
14
|
type ButtonVariants = VariantProps$1<typeof buttonVariants>;
|
|
15
15
|
/**
|
|
@@ -91,9 +91,9 @@ type ButtonProps = ComponentProps<"button"> & ButtonVariants & {
|
|
|
91
91
|
* ```
|
|
92
92
|
*/
|
|
93
93
|
declare const Button: react.ForwardRefExoticComponent<(Omit<react.ClassAttributes<HTMLButtonElement> & react.ButtonHTMLAttributes<HTMLButtonElement> & Partial<DeepNonNullable<class_variance_authority0.VariantProps<(props?: ({
|
|
94
|
-
appearance?: "
|
|
94
|
+
appearance?: "filled" | "link" | "ghost" | "outlined" | null | undefined;
|
|
95
95
|
isLoading?: boolean | null | undefined;
|
|
96
|
-
priority?: "
|
|
96
|
+
priority?: "danger" | "neutral" | "default" | null | undefined;
|
|
97
97
|
} & class_variance_authority_types0.ClassProp) | undefined) => string>>> & {
|
|
98
98
|
/**
|
|
99
99
|
* An icon to render inside the button. If the `state` is `"pending"`, then
|
|
@@ -137,9 +137,9 @@ declare const Button: react.ForwardRefExoticComponent<(Omit<react.ClassAttribute
|
|
|
137
137
|
*/
|
|
138
138
|
type?: ComponentProps<"button">["type"];
|
|
139
139
|
}, "ref"> | Omit<react.ClassAttributes<HTMLButtonElement> & react.ButtonHTMLAttributes<HTMLButtonElement> & Partial<DeepNonNullable<class_variance_authority0.VariantProps<(props?: ({
|
|
140
|
-
appearance?: "
|
|
140
|
+
appearance?: "filled" | "link" | "ghost" | "outlined" | null | undefined;
|
|
141
141
|
isLoading?: boolean | null | undefined;
|
|
142
|
-
priority?: "
|
|
142
|
+
priority?: "danger" | "neutral" | "default" | null | undefined;
|
|
143
143
|
} & class_variance_authority_types0.ClassProp) | undefined) => string>>> & {
|
|
144
144
|
/**
|
|
145
145
|
* An icon to render inside the button. If the `state` is `"pending"`, then
|
|
@@ -172,4 +172,4 @@ declare const Button: react.ForwardRefExoticComponent<(Omit<react.ClassAttribute
|
|
|
172
172
|
}, "ref">) & react.RefAttributes<HTMLButtonElement>>;
|
|
173
173
|
//#endregion
|
|
174
174
|
export { ButtonProps as n, Button as t };
|
|
175
|
-
//# sourceMappingURL=button-
|
|
175
|
+
//# sourceMappingURL=button-DQO0IuzC.d.ts.map
|
package/dist/button.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as IconButtonProps, t as IconButton } from "./icon-button-
|
|
2
|
-
import { n as ButtonProps, t as Button } from "./button-
|
|
1
|
+
import { n as IconButtonProps, t as IconButton } from "./icon-button-ikEU0Ctp.js";
|
|
2
|
+
import { n as ButtonProps, t as Button } from "./button-DQO0IuzC.js";
|
|
3
3
|
import { n as ButtonGroupProps, t as ButtonGroup } from "./index-ViSCOUrU.js";
|
|
4
4
|
export { Button, ButtonGroup, ButtonGroupProps, ButtonProps, IconButton, IconButtonProps };
|
package/dist/checkbox.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ type CheckedState = boolean | "indeterminate";
|
|
|
19
19
|
* </form>
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
-
declare const Checkbox: react.ForwardRefExoticComponent<Omit<Omit<react.DetailedHTMLProps<react.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "defaultChecked" | "
|
|
22
|
+
declare const Checkbox: react.ForwardRefExoticComponent<Omit<Omit<react.DetailedHTMLProps<react.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "defaultChecked" | "checked" | "type"> & WithValidation & {
|
|
23
23
|
/**
|
|
24
24
|
* The controlled checked state of the checkbox. Must be used in conjunction with onChange.
|
|
25
25
|
*/
|
package/dist/code-block.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ declare const isSupportedLanguage: (value: unknown) => value is SupportedLanguag
|
|
|
27
27
|
* Formats a language name into a class name that Prism.js can understand.
|
|
28
28
|
* @default "language-sh"
|
|
29
29
|
*/
|
|
30
|
-
declare function formatLanguageClassName(language?: SupportedLanguage | undefined): "language-
|
|
30
|
+
declare function formatLanguageClassName(language?: SupportedLanguage | undefined): "language-text" | "language-ruby" | "language-plaintext" | "language-html" | "language-go" | "language-py" | "language-jsx" | "language-bash" | "language-cs" | "language-csharp" | "language-css" | "language-dotnet" | "language-java" | "language-javascript" | "language-js" | "language-json" | "language-markup" | "language-plain" | "language-python" | "language-rb" | "language-rust" | "language-sh" | "language-shell" | "language-ts" | "language-tsx" | "language-txt" | "language-typescript" | "language-xml" | "language-yaml" | "language-yml";
|
|
31
31
|
//#endregion
|
|
32
32
|
//#region src/components/code-block/indentation.d.ts
|
|
33
33
|
declare const indentations: readonly ["tabs", "spaces"];
|
package/dist/code-block.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-block.js","names":["startsWithNonWhitespace","Highlighter","clsx","MantleIcon"],"sources":["../src/components/code-block/escape-html.ts","../src/components/code-block/indentation.ts","../src/components/code-block/fmt-code.ts","../src/components/code-block/normalize-indentation.ts","../src/components/code-block/supported-languages.ts","../src/components/code-block/code-block.tsx","../src/components/code-block/parse-metastring.ts"],"sourcesContent":["/**\n * Escapes special HTML characters in a string to their corresponding\n * HTML entities, preventing issues like unintended HTML rendering or\n * cross-site scripting (XSS) when injecting raw strings into the DOM\n * using `dangerouslySetInnerHTML`.\n *\n * Characters escaped:\n * - \\& => `&`;\n * - \\< => `<`;\n * - \\> => `>`;\n * - \\\" => `"`;\n * - \\' => `'`;\n *\n * @param {string} value The raw string to be escaped.\n *\n * @example\n * escapeHtml('<div>Hello & \"world\"</div>');\n * // Returns: '<div>Hello & "world"</div>'\n */\nfunction escapeHtml(value: string): string {\n\tlet firstSpecialCharIndex = -1;\n\tfor (let i = 0; i < value.length; i++) {\n\t\tconst character = value[i];\n\t\tif (\n\t\t\tcharacter === \"&\" ||\n\t\t\tcharacter === \"<\" ||\n\t\t\tcharacter === \">\" ||\n\t\t\tcharacter === '\"' ||\n\t\t\tcharacter === \"'\"\n\t\t) {\n\t\t\tfirstSpecialCharIndex = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (firstSpecialCharIndex === -1) {\n\t\treturn value;\n\t}\n\n\tlet escaped = value.slice(0, firstSpecialCharIndex);\n\tfor (let i = firstSpecialCharIndex; i < value.length; i++) {\n\t\tconst character = value[i];\n\t\tswitch (character) {\n\t\t\tcase \"&\":\n\t\t\t\tescaped += \"&\";\n\t\t\t\tbreak;\n\t\t\tcase \"<\":\n\t\t\t\tescaped += \"<\";\n\t\t\t\tbreak;\n\t\t\tcase \">\":\n\t\t\t\tescaped += \">\";\n\t\t\t\tbreak;\n\t\t\tcase '\"':\n\t\t\t\tescaped += \""\";\n\t\t\t\tbreak;\n\t\t\tcase \"'\":\n\t\t\t\tescaped += \"'\";\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tescaped += character;\n\t\t}\n\t}\n\treturn escaped;\n}\n\nexport {\n\t//,\n\tescapeHtml,\n};\n","import type { SupportedLanguage } from \"./supported-languages.js\";\n\nconst indentations = [\"tabs\", \"spaces\"] as const;\ntype Indentation = (typeof indentations)[number];\n\n/**\n * Type Predicate: checks if the given value is a valid indentation type.\n */\nfunction isIndentation(input: unknown): input is Indentation {\n\treturn indentations.includes(input as Indentation);\n}\n\n/**\n * Infers the indentation type based on the language and preferred indentation.\n *\n * @param language - The language to check.\n * @param preferredIndentation - The preferred indentation type (overrides what is detected).\n */\nfunction inferIndentation(\n\tlanguage: SupportedLanguage,\n\tpreferredIndentation: Indentation | undefined,\n) {\n\t// if the user has a preferred indentation, use that regardless of the language\n\tif (preferredIndentation) {\n\t\treturn preferredIndentation;\n\t}\n\n\tif (isTabIndentedLanguage(language)) {\n\t\treturn \"tabs\";\n\t}\n\n\tif (isSpaceIndentedLanguage(language)) {\n\t\treturn \"spaces\";\n\t}\n\n\treturn \"spaces\";\n}\n\nexport {\n\t//,\n\tindentations,\n\tinferIndentation,\n\tisIndentation,\n};\n\nexport type {\n\t//,\n\tIndentation,\n};\n\n/**\n * Languages that require or strongly prefer tabs\n */\nconst tabIndentedLanguages = [\n\t\"csharp\",\n\t\"css\",\n\t\"go\",\n\t\"html\",\n\t\"java\",\n\t\"javascript\",\n\t\"js\",\n\t\"jsx\",\n\t\"ts\",\n\t\"tsx\",\n\t\"typescript\",\n\t\"xml\",\n] as const satisfies SupportedLanguage[];\n\nconst tabIndentedLanguageSet = new Set<string>(tabIndentedLanguages);\n\n/**\n * Languages that require or strongly prefer spaces\n */\nconst spaceIndentedLanguages = [\n\t\"python\",\n\t\"py\",\n\t\"yaml\",\n\t\"yml\",\n\t\"ruby\",\n\t\"rb\",\n] as const satisfies SupportedLanguage[];\n\nconst spaceIndentedLanguageSet = new Set<string>(spaceIndentedLanguages);\n\ntype TabIndentedLanguage = (typeof tabIndentedLanguages)[number];\ntype SpaceIndentedLanguage = (typeof spaceIndentedLanguages)[number];\n\n/**\n * Type Predicate: checks if the given value is a required/preferred tab-indented language.\n */\nfunction isTabIndentedLanguage(value: SupportedLanguage): value is TabIndentedLanguage {\n\treturn tabIndentedLanguageSet.has(value);\n}\n\n/**\n * Type Predicate: checks if the given value is a required/preferred space-indented language.\n */\nfunction isSpaceIndentedLanguage(value: SupportedLanguage): value is SpaceIndentedLanguage {\n\treturn spaceIndentedLanguageSet.has(value);\n}\n","type Primitive = string | number | boolean | undefined | null;\n\n/**\n * Tagged template literal to format code blocks and normalize leading indentation\n */\nfunction fmtCode(strings: TemplateStringsArray, ...values: Primitive[]): string {\n\tif (!isTemplateStringsArray(strings) || !Array.isArray(values)) {\n\t\tthrow new Error(\n\t\t\t\"It looks like you tried to call `fmtCode` as a function. Make sure to use it as a tagged template.\\n\\tExample: fmtCode`SELECT * FROM users`, not fmtCode('SELECT * FROM users')\",\n\t\t);\n\t}\n\n\tconst text = String.raw({ raw: strings }, ...values);\n\n\t// fine the minimum indentation of the code block\n\tconst minIndent = findMinIndent(text);\n\tconst lines = text.trim().split(\"\\n\");\n\tconst normalizedLines = new Array<string>(lines.length);\n\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst line = lines[i];\n\t\tif (line == null) {\n\t\t\tcontinue;\n\t\t}\n\t\tnormalizedLines[i] = startsWithNonWhitespace(line) ? line : line.slice(minIndent);\n\t}\n\n\treturn normalizedLines.join(\"\\n\");\n}\n\nexport {\n\t//,\n\tfmtCode,\n\tfindMinIndent,\n};\n\n/**\n * Find the shortest indentation of a multiline string\n */\nfunction findMinIndent(value: string): number {\n\tlet minIndent = Number.POSITIVE_INFINITY;\n\tlet indent = 0;\n\tlet atLineStart = true;\n\n\tfor (let i = 0; i < value.length; i++) {\n\t\tconst char = value[i];\n\n\t\tif (atLineStart) {\n\t\t\tif (char === \" \" || char === \"\\t\") {\n\t\t\t\tindent += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (char === \"\\n\" || char === \"\\r\") {\n\t\t\t\tindent = 0;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (indent < minIndent) {\n\t\t\t\tminIndent = indent;\n\t\t\t\tif (minIndent === 0) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tatLineStart = false;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (char === \"\\n\" || char === \"\\r\") {\n\t\t\tatLineStart = true;\n\t\t\tindent = 0;\n\t\t}\n\t}\n\n\treturn minIndent === Number.POSITIVE_INFINITY ? 0 : minIndent;\n}\n\n/**\n * Type guard to check if a value is a `TemplateStringsArray`\n */\nfunction isTemplateStringsArray(strings: unknown): strings is TemplateStringsArray {\n\treturn Array.isArray(strings) && \"raw\" in strings && Array.isArray(strings.raw);\n}\n\nfunction startsWithNonWhitespace(line: string): boolean {\n\tconst firstCharacter = line[0];\n\treturn firstCharacter != null && firstCharacter !== \" \" && firstCharacter !== \"\\t\";\n}\n","import type { Indentation } from \"./indentation.js\";\nimport { findMinIndent } from \"./fmt-code.js\";\n\ntype Options = {\n\t/**\n\t * The indentation type to use. Can be either \"tabs\" or \"spaces\".\n\t * @default \"spaces\"\n\t */\n\tindentation?: Indentation;\n};\n\n/**\n * Trim any leading and trailing whitespace/empty lines, convert leading\n * indentation to the given options.indentation\n */\nfunction normalizeIndentation(value: string, options?: Options): string {\n\tconst indentation = options?.indentation ?? \"spaces\";\n\tconst normalizedLineEndings = value.replace(/\\r\\n?/g, \"\\n\");\n\tconst trimmed = normalizedLineEndings.trim();\n\n\tif (trimmed === \"\") {\n\t\treturn \"\";\n\t}\n\n\tconst minIndent = findMinIndent(normalizedLineEndings);\n\tconst lines = trimmed.split(\"\\n\");\n\tconst normalizedLines = new Array<string>(lines.length);\n\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst line = lines[i];\n\t\tif (line == null) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst dedentedLine = startsWithNonWhitespace(line) ? line : line.slice(minIndent);\n\t\tnormalizedLines[i] = normalizeLeadingIndentation(dedentedLine, indentation);\n\t}\n\n\treturn normalizedLines.join(\"\\n\");\n}\n\nexport {\n\t//,\n\tnormalizeIndentation,\n};\n\n/**\n * Rewrites only the leading indentation of a non-empty line into the requested\n * indentation style, leaving the rest of the line untouched.\n */\nfunction normalizeLeadingIndentation(line: string, indentation: Indentation): string {\n\tlet indentEnd = 0;\n\twhile (indentEnd < line.length) {\n\t\tconst character = line[indentEnd];\n\t\tif (character !== \" \" && character !== \"\\t\") {\n\t\t\tbreak;\n\t\t}\n\t\tindentEnd += 1;\n\t}\n\n\tif (indentEnd === 0 || indentEnd === line.length) {\n\t\treturn line;\n\t}\n\n\tconst leadingWhitespace = line.slice(0, indentEnd);\n\tconst normalizedLeadingWhitespace =\n\t\tindentation === \"spaces\"\n\t\t\t? leadingWhitespace.replace(/\\t/g, \" \")\n\t\t\t: leadingWhitespace.replace(/ {2}/g, \"\\t\");\n\n\treturn normalizedLeadingWhitespace + line.slice(indentEnd);\n}\n\n/**\n * Returns true when a line begins with visible content instead of indentation.\n */\nfunction startsWithNonWhitespace(line: string): boolean {\n\tconst firstCharacter = line[0];\n\treturn firstCharacter != null && firstCharacter !== \" \" && firstCharacter !== \"\\t\";\n}\n","/**\n * List of supported languages for syntax highlighting.\n * @private\n */\nexport const supportedLanguages = [\n\t\"bash\",\n\t\"cs\",\n\t\"csharp\",\n\t\"css\",\n\t\"dotnet\",\n\t\"go\",\n\t\"html\",\n\t\"java\",\n\t\"javascript\",\n\t\"js\",\n\t\"json\",\n\t\"jsx\",\n\t\"markup\",\n\t\"plain\",\n\t\"plaintext\",\n\t\"py\",\n\t\"python\",\n\t\"rb\",\n\t\"ruby\",\n\t\"rust\",\n\t\"sh\",\n\t\"shell\",\n\t\"text\",\n\t\"ts\",\n\t\"tsx\",\n\t\"txt\",\n\t\"typescript\",\n\t\"xml\",\n\t\"yaml\",\n\t\"yml\",\n] as const;\n\nconst supportedLanguageSet = new Set<string>(supportedLanguages);\n\n/**\n * Supported languages for syntax highlighting.\n */\ntype SupportedLanguage = (typeof supportedLanguages)[number];\n\n/**\n * Parses a markdown code block (```) language class into a SupportedLanguage.\n * Defaults to \"sh\" if no supported language is found.\n */\nfunction parseLanguage(\n\tvalue: `language-${string}` | `lang-${string}` | (string & {}) | undefined,\n): SupportedLanguage {\n\tif (!value) {\n\t\treturn \"sh\";\n\t}\n\n\t// remove leading \"language-\" and \"lang-\" prefixes\n\t// find first '-' and slice from there\n\tconst maybeLanguage = value.trim().slice(value.indexOf(\"-\") + 1);\n\n\treturn isSupportedLanguage(maybeLanguage) ? maybeLanguage : \"sh\";\n}\n\n/**\n * Type Predicate: checks if an arbitrary value is a supported syntax highlighting language.\n */\nconst isSupportedLanguage = (value: unknown): value is SupportedLanguage => {\n\treturn typeof value === \"string\" && supportedLanguageSet.has(value);\n};\n\n/**\n * A class name for a language that Prism.js can understand.\n */\ntype LanguageClass = `language-${SupportedLanguage}`;\n\n/**\n * Formats a language name into a class name that Prism.js can understand.\n * @default \"language-sh\"\n */\nfunction formatLanguageClassName(language: SupportedLanguage | undefined = \"sh\") {\n\tconst lang = language ?? \"sh\";\n\tconst className: LanguageClass = `language-${lang}`;\n\treturn className;\n}\n\nexport { isSupportedLanguage, parseLanguage, formatLanguageClassName };\nexport type { SupportedLanguage };\n","\"use client\";\n\nimport \"./syntax-highlight.css\";\nimport { CaretDownIcon } from \"@phosphor-icons/react/CaretDown\";\nimport { CheckIcon } from \"@phosphor-icons/react/Check\";\nimport { CopyIcon } from \"@phosphor-icons/react/Copy\";\nimport { FileTextIcon } from \"@phosphor-icons/react/FileText\";\nimport { TerminalIcon } from \"@phosphor-icons/react/Terminal\";\nimport clsx from \"clsx\";\nimport type {\n\tComponentProps,\n\tComponentRef,\n\tDispatch,\n\tHTMLAttributes,\n\tReactNode,\n\tSetStateAction,\n} from \"react\";\nimport {\n\tcreateContext,\n\tforwardRef,\n\tuseContext,\n\tuseEffect,\n\tuseId,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport assert from \"tiny-invariant\";\nimport { useCopyToClipboard } from \"../../hooks/use-copy-to-clipboard.js\";\nimport type { WithAsChild } from \"../../types/as-child.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { Icon as MantleIcon } from \"../icon/icon.js\";\nimport type { SvgAttributes } from \"../icon/types.js\";\nimport { TrafficPolicyFileIcon } from \"../icons/traffic-policy-file.js\";\nimport { Slot } from \"../slot/index.js\";\nimport { escapeHtml } from \"./escape-html.js\";\nimport { Highlighter } from \"./highlighter.js\";\nimport { type Indentation, inferIndentation } from \"./indentation.js\";\nimport type { LineRange } from \"./line-numbers.js\";\nimport { normalizeIndentation } from \"./normalize-indentation.js\";\nimport type { Mode } from \"./parse-metastring.js\";\nimport type { SupportedLanguage } from \"./supported-languages.js\";\nimport { formatLanguageClassName, supportedLanguages } from \"./supported-languages.js\";\n\nconst MAX_HIGHLIGHT_CACHE_ENTRIES = 100;\nconst highlightedCodeCache = new Map<string, string>();\n\n/**\n * Builds a stable cache key for highlighted code using both the language and\n * normalized code contents.\n */\nfunction getHighlightedCodeCacheKey(language: SupportedLanguage, value: string): string {\n\treturn `${language}\\u0000${value}`;\n}\n\n/**\n * Returns highlighted HTML from the shared LRU cache when available, otherwise\n * highlights the code and stores the result for future reuse.\n */\nfunction getHighlightedCode(\n\tlanguage: SupportedLanguage,\n\tvalue: string,\n\tgrammar: Prism.Grammar,\n): string {\n\tconst cacheKey = getHighlightedCodeCacheKey(language, value);\n\tconst cached = highlightedCodeCache.get(cacheKey);\n\n\tif (cached != null) {\n\t\thighlightedCodeCache.delete(cacheKey);\n\t\thighlightedCodeCache.set(cacheKey, cached);\n\t\treturn cached;\n\t}\n\n\tconst highlighted = Highlighter.highlight(value, grammar, language);\n\thighlightedCodeCache.set(cacheKey, highlighted);\n\n\tif (highlightedCodeCache.size > MAX_HIGHLIGHT_CACHE_ENTRIES) {\n\t\tconst oldestKey = highlightedCodeCache.keys().next().value;\n\t\tif (oldestKey != null) {\n\t\t\thighlightedCodeCache.delete(oldestKey);\n\t\t}\n\t}\n\n\treturn highlighted;\n}\n\n/**\n * TODO(cody):\n * - fix line numbers, maybe try grid instead of :before and flex?\n * - fix line hightlighting\n * - fix line wrapping? horizontal scrolling has problems w/ line highlighting :(\n */\n\ntype CodeBlockContextType = {\n\tcodeId: string | undefined;\n\tcopyText: string;\n\thasCodeExpander: boolean;\n\tisCodeExpanded: boolean;\n\tregisterCodeId: (id: string) => void;\n\tsetCopyText: (newCopyText: string) => void;\n\tsetHasCodeExpander: (value: boolean) => void;\n\tsetIsCodeExpanded: Dispatch<SetStateAction<boolean>>;\n\tunregisterCodeId: (id: string) => void;\n};\n\nconst CodeBlockContext = createContext<CodeBlockContextType>({\n\tcodeId: undefined,\n\tcopyText: \"\",\n\thasCodeExpander: false,\n\tisCodeExpanded: false,\n\tregisterCodeId: () => {},\n\tsetCopyText: () => {},\n\tsetHasCodeExpander: () => {},\n\tsetIsCodeExpanded: () => {},\n\tunregisterCodeId: () => {},\n});\n\n/**\n * Code blocks render and apply syntax highlighting to blocks of code.\n * This is the root component for all code block components.\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblockroot\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst Root = forwardRef<ComponentRef<\"div\">, Omit<ComponentProps<\"div\">, \"align\"> & WithAsChild>(\n\t({ asChild = false, className, ...props }, ref) => {\n\t\tconst [copyText, setCopyText] = useState(\"\");\n\t\tconst [hasCodeExpander, setHasCodeExpander] = useState(false);\n\t\tconst [isCodeExpanded, setIsCodeExpanded] = useState(false);\n\t\tconst [codeId, setCodeId] = useState<string | undefined>(undefined);\n\n\t\tconst context: CodeBlockContextType = useMemo(\n\t\t\t() =>\n\t\t\t\t({\n\t\t\t\t\tcodeId,\n\t\t\t\t\tcopyText,\n\t\t\t\t\thasCodeExpander,\n\t\t\t\t\tisCodeExpanded,\n\t\t\t\t\tregisterCodeId: (id) => {\n\t\t\t\t\t\tsetCodeId((old) => {\n\t\t\t\t\t\t\tassert(old == null, \"You can only render a single CodeBlockCode within a CodeBlock.\");\n\t\t\t\t\t\t\treturn id;\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t\tsetCopyText,\n\t\t\t\t\tsetHasCodeExpander,\n\t\t\t\t\tsetIsCodeExpanded,\n\t\t\t\t\tunregisterCodeId: (id) => {\n\t\t\t\t\t\tsetCodeId((old) => {\n\t\t\t\t\t\t\tassert(old === id, \"You can only render a single CodeBlockCode within a CodeBlock.\");\n\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t}) as const,\n\t\t\t[codeId, copyText, hasCodeExpander, isCodeExpanded],\n\t\t);\n\n\t\tconst Component = asChild ? Slot : \"div\";\n\n\t\treturn (\n\t\t\t<CodeBlockContext.Provider value={context}>\n\t\t\t\t<Component\n\t\t\t\t\tdata-slot=\"code-block\"\n\t\t\t\t\tclassName={cx(\n\t\t\t\t\t\t\"text-mono overflow-hidden rounded-md border border-gray-300 bg-gray-50 font-mono\",\n\t\t\t\t\t\t\"[&_svg]:shrink-0\",\n\t\t\t\t\t\tclassName,\n\t\t\t\t\t)}\n\t\t\t\t\tref={ref}\n\t\t\t\t\t{...props}\n\t\t\t\t/>\n\t\t\t</CodeBlockContext.Provider>\n\t\t);\n\t},\n);\nRoot.displayName = \"CodeBlock\";\n\n/**\n * The body of the `CodeBlock`. This is where the `CodeBlock.Code` and optional\n * `CodeBlock.CopyButton` is rendered.\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblockbody\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst Body = forwardRef<ComponentRef<\"div\">, ComponentProps<\"div\"> & WithAsChild>(\n\t({ asChild = false, className, ...props }, ref) => {\n\t\tconst Component = asChild ? Slot : \"div\";\n\n\t\treturn <Component className={cx(\"relative\", className)} ref={ref} {...props} />;\n\t},\n);\nBody.displayName = \"CodeBlockBody\";\n\ntype CodeBlockCodeProps = Omit<ComponentProps<\"pre\">, \"children\"> & {\n\t/**\n\t * The code to display in the code block. Should be code formatted as a string. This code will be passed to our syntax highlighter.\n\t */\n\tvalue: string;\n\t/**\n\t * @todo not implemented yet\n\t */\n\thighlightLines?: (LineRange | number)[];\n\t/**\n\t * The type of indentation to use. Can be either \"tabs\" or \"spaces\".\n\t * @default inferred from the given language, fallback to `spaces`\n\t */\n\tindentation?: Indentation;\n\t/**\n\t * The language of the code block. This will be used to determine how to syntax highlight the code.\n\t * @default `\"text\"`.\n\t */\n\tlanguage?: SupportedLanguage;\n\t/**\n\t * @todo not implemented yet\n\t */\n\tshowLineNumbers?: boolean;\n};\n\n/**\n * The `CodeBlock` content. This is where the code is rendered and syntax highlighted.\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblockcode\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code\n * language=\"sh\"\n * value={fmtCode`ffmpeg -i multichannel.mxf -map 0:v:0 -map 0:a:0 -map 0:a:0 -c:a:0 ac3 -b:a:0 640k -ac:a:1 2 -c:a:1 aac -b:2 128k out.mp4`}\n * />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst Code = forwardRef<ComponentRef<\"pre\">, CodeBlockCodeProps>(\n\t(\n\t\t{\n\t\t\tclassName,\n\t\t\thighlightLines: _unusedHighlightLines, // not implemented yet\n\t\t\tindentation: propIndentation,\n\t\t\tlanguage = \"text\",\n\t\t\tshowLineNumbers: _unusedShowLineNumbers, // not implemented yet\n\t\t\tstyle,\n\t\t\ttabIndex,\n\t\t\tvalue,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst id = useId();\n\t\tconst { hasCodeExpander, isCodeExpanded, registerCodeId, setCopyText, unregisterCodeId } =\n\t\t\tuseContext(CodeBlockContext);\n\t\tconst indentation = inferIndentation(language, propIndentation);\n\n\t\t// trim any leading and trailing whitespace/empty lines, convert leading tabs to spaces\n\t\tconst normalizedAndTrimmedValue = useMemo(\n\t\t\t() => normalizeIndentation(value, { indentation }),\n\t\t\t[value, indentation],\n\t\t);\n\t\tconst escapedNormalizedAndTrimmedValue = useMemo(\n\t\t\t() => escapeHtml(normalizedAndTrimmedValue),\n\t\t\t[normalizedAndTrimmedValue],\n\t\t);\n\t\tconst [highlightedCodeInnerHtml, setHighlightedCodeInnerHtml] = useState(\n\t\t\t() =>\n\t\t\t\t// initialize the <code> inner html with escaped HTML since we are using\n\t\t\t\t// dangerouslySetInnerHTML to set the inner html of the <code> element\n\t\t\t\t// and use Prism.js to \"highlight\" the code in a useEffect (client-side only)\n\t\t\t\tescapedNormalizedAndTrimmedValue,\n\t\t);\n\n\t\tuseEffect(() => {\n\t\t\tconst grammar = Highlighter.languages[language];\n\t\t\tassert(\n\t\t\t\tgrammar,\n\t\t\t\t`CodeBlock does not support the language \"${language}\". The syntax highlighter does not have a grammar for this language. The supported languages are: ${supportedLanguages.join(\", \")}.`,\n\t\t\t);\n\t\t\tconst newHighlightedCodeInnerHtml = getHighlightedCode(\n\t\t\t\tlanguage,\n\t\t\t\tnormalizedAndTrimmedValue,\n\t\t\t\tgrammar,\n\t\t\t);\n\t\t\tsetHighlightedCodeInnerHtml(newHighlightedCodeInnerHtml);\n\t\t}, [normalizedAndTrimmedValue, language]);\n\n\t\tuseEffect(() => {\n\t\t\tsetCopyText(normalizedAndTrimmedValue);\n\t\t}, [normalizedAndTrimmedValue, setCopyText]);\n\n\t\tuseEffect(() => {\n\t\t\tregisterCodeId(id);\n\n\t\t\treturn () => {\n\t\t\t\tunregisterCodeId(id);\n\t\t\t};\n\t\t}, [id, registerCodeId, unregisterCodeId]);\n\n\t\tconst languageClassName = formatLanguageClassName(language);\n\n\t\treturn (\n\t\t\t<pre\n\t\t\t\taria-expanded={hasCodeExpander ? isCodeExpanded : undefined}\n\t\t\t\tclassName={cx(\n\t\t\t\t\t\"scrollbar overflow-x-auto overflow-y-hidden p-4 pr-14\",\n\t\t\t\t\t\"text-mono m-0 font-mono\",\n\t\t\t\t\t\"aria-collapsed:max-h-[13.6rem]\",\n\t\t\t\t\tlanguageClassName, // place it last because prism does weird stuff client side, causes hydration mismatches\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tdata-lang={language}\n\t\t\t\tid={id}\n\t\t\t\tref={ref}\n\t\t\t\tstyle={{\n\t\t\t\t\t...style,\n\t\t\t\t\ttabSize: 2,\n\t\t\t\t\tMozTabSize: 2,\n\t\t\t\t}}\n\t\t\t\t// prism.js adds a tabindex of 0 to the pre element by default (unless it's set)\n\t\t\t\t// this is unnecessary, we do not want this automatic behavior!\n\t\t\t\ttabIndex={tabIndex ?? -1}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<code\n\t\t\t\t\tclassName={clsx(\"text-size-inherit\", languageClassName)}\n\t\t\t\t\tdangerouslySetInnerHTML={{\n\t\t\t\t\t\t__html: highlightedCodeInnerHtml,\n\t\t\t\t\t}}\n\t\t\t\t\t// we need to suppress the hydration warning because we are setting the innerHTML of the code block\n\t\t\t\t\t// and using Prism.js to \"highlight\" the code in a useEffect (client-side only), which does different things on the client and server\n\t\t\t\t\tsuppressHydrationWarning\n\t\t\t\t/>\n\t\t\t</pre>\n\t\t);\n\t},\n);\nCode.displayName = \"CodeBlockCode\";\n\n/**\n * The (optional) header slot of the `CodeBlock`. This is where things like the\n * `CodeBlock.Icon` and `CodeBlock.Title` are rendered.\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblockheader\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst Header = forwardRef<ComponentRef<\"div\">, ComponentProps<\"div\"> & WithAsChild>(\n\t({ asChild = false, className, ...props }, ref) => {\n\t\tconst Component = asChild ? Slot : \"div\";\n\n\t\treturn (\n\t\t\t<Component\n\t\t\t\tclassName={cx(\n\t\t\t\t\t\"flex items-center gap-1 border-b border-gray-300 bg-gray-100 px-4 py-2 text-gray-700\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tref={ref}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nHeader.displayName = \"CodeBlockHeader\";\n\n/**\n * The (optional) title of the `CodeBlock`. Default renders as an h3 element,\n * use asChild to render something else.\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblocktitle\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst Title = forwardRef<\n\tHTMLHeadingElement,\n\tHTMLAttributes<HTMLHeadingElement> & { asChild?: boolean }\n>(({ asChild = false, className, ...props }, ref) => {\n\tconst Component = asChild ? Slot : \"h3\";\n\n\treturn (\n\t\t<Component\n\t\t\tref={ref}\n\t\t\tclassName={cx(\"text-mono m-0 font-mono font-normal\", className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n});\nTitle.displayName = \"CodeBlockTitle\";\n\ntype CodeBlockCopyButtonProps = Omit<ComponentProps<\"button\">, \"children\" | \"type\"> &\n\tWithAsChild & {\n\t\t/**\n\t\t * Callback fired when the copy button is clicked, passes the copied text as an argument.\n\t\t */\n\t\tonCopy?: (value: string) => void;\n\t\t/**\n\t\t * Callback fired when an error occurs during copying.\n\t\t */\n\t\tonCopyError?: (error: unknown) => void;\n\t};\n\n/**\n * The (optional) copy button of the `CodeBlock`. Render this as a child of the\n * `CodeBlock.Body` to allow users to copy the code block contents to their\n * clipboard.\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblockcopybutton\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst CopyButton = forwardRef<ComponentRef<\"button\">, CodeBlockCopyButtonProps>(\n\t({ asChild = false, className, onCopy, onCopyError, onClick, ...props }, ref) => {\n\t\tconst { copyText } = useContext(CodeBlockContext);\n\t\tconst [, copyToClipboard] = useCopyToClipboard();\n\t\tconst [wasCopied, setWasCopied] = useState(false);\n\t\tconst timeoutHandle = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n\t\tuseEffect(() => {\n\t\t\treturn () => {\n\t\t\t\tif (timeoutHandle.current != null) {\n\t\t\t\t\tclearTimeout(timeoutHandle.current);\n\t\t\t\t}\n\t\t\t};\n\t\t}, []);\n\n\t\tconst Component = asChild ? Slot : \"button\";\n\n\t\treturn (\n\t\t\t<Component\n\t\t\t\ttype=\"button\"\n\t\t\t\tclassName={cx(\n\t\t\t\t\t\"focus-visible:border-accent-600 focus-visible:ring-focus-accent absolute right-2.5 top-2.5 z-10 flex size-7 items-center justify-center rounded border border-gray-300 bg-gray-50 shadow-[-1rem_0_0.75rem_-0.375rem_var(--color-gray-50),1rem_0_0_-0.25rem_var(--color-gray-50)] hover:border-gray-400 hover:bg-gray-200 focus-visible:outline-hidden focus-visible:ring-4\",\n\t\t\t\t\twasCopied &&\n\t\t\t\t\t\t\"bg-filled-success text-on-filled hover:bg-filled-success focus:bg-filled-success focus-visible:border-success-600 focus-visible:ring-focus-success w-auto gap-1 border-transparent pl-2 pr-1.5 hover:border-transparent\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tref={ref}\n\t\t\t\tonClick={async (event) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tonClick?.(event);\n\t\t\t\t\t\tif (event.defaultPrevented) {\n\t\t\t\t\t\t\t// Clear any existing timeout\n\t\t\t\t\t\t\tif (timeoutHandle.current != null) {\n\t\t\t\t\t\t\t\tclearTimeout(timeoutHandle.current);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tawait copyToClipboard(copyText);\n\t\t\t\t\t\tonCopy?.(copyText);\n\t\t\t\t\t\tsetWasCopied(true);\n\n\t\t\t\t\t\t// Clear any existing timeout\n\t\t\t\t\t\tif (timeoutHandle.current != null) {\n\t\t\t\t\t\t\tclearTimeout(timeoutHandle.current);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Reset the copied state after a short delay\n\t\t\t\t\t\ttimeoutHandle.current = setTimeout(() => {\n\t\t\t\t\t\t\tsetWasCopied(false);\n\t\t\t\t\t\t}, 2000);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tonCopyError?.(error);\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<span className=\"sr-only\">Copy code</span>\n\t\t\t\t{wasCopied ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\tCopied\n\t\t\t\t\t\t<MantleIcon svg={<CheckIcon weight=\"bold\" />} className=\"size-4\" />\n\t\t\t\t\t</>\n\t\t\t\t) : (\n\t\t\t\t\t<MantleIcon svg={<CopyIcon />} className=\"-ml-px\" />\n\t\t\t\t)}\n\t\t\t</Component>\n\t\t);\n\t},\n);\nCopyButton.displayName = \"CodeBlockCopyButton\";\n\ntype CodeBlockExpanderButtonProps = Omit<\n\tComponentProps<\"button\">,\n\t\"children\" | \"aria-controls\" | \"aria-expanded\"\n> &\n\tWithAsChild;\n\n/**\n * The (optional) expander button of the `CodeBlock`. Render this as a child of the\n * `CodeBlock.Body` to allow users to expand/collapse the code block contents.\n *\n * @note If this component is preset, the `CodeBlock` will automatically know\n * that it should be collapsible. Don't use this component if you don't want\n * the code block to be collapsible.\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblockexpanderbutton\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst ExpanderButton = forwardRef<ComponentRef<\"button\">, CodeBlockExpanderButtonProps>(\n\t({ asChild = false, className, onClick, ...props }, ref) => {\n\t\tconst { codeId, isCodeExpanded, setIsCodeExpanded, setHasCodeExpander } =\n\t\t\tuseContext(CodeBlockContext);\n\n\t\tuseEffect(() => {\n\t\t\tsetHasCodeExpander(true);\n\n\t\t\treturn () => {\n\t\t\t\tsetHasCodeExpander(false);\n\t\t\t};\n\t\t}, [setHasCodeExpander]);\n\n\t\tconst Component = asChild ? Slot : \"button\";\n\n\t\treturn (\n\t\t\t<Component\n\t\t\t\t{...props}\n\t\t\t\taria-controls={codeId}\n\t\t\t\taria-expanded={isCodeExpanded}\n\t\t\t\tclassName={cx(\n\t\t\t\t\t\"flex w-full items-center justify-center gap-0.5 border-t border-gray-300 bg-gray-50 px-4 py-2 font-sans text-gray-700 hover:bg-gray-100\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tref={ref}\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={(event) => {\n\t\t\t\t\tsetIsCodeExpanded((prev) => !prev);\n\t\t\t\t\tonClick?.(event);\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{isCodeExpanded ? \"Show less\" : \"Show more\"}{\" \"}\n\t\t\t\t<MantleIcon\n\t\t\t\t\tsvg={<CaretDownIcon weight=\"bold\" />}\n\t\t\t\t\tclassName={cx(\"size-4\", isCodeExpanded && \"rotate-180\", \"transition-all duration-150\")}\n\t\t\t\t/>\n\t\t\t</Component>\n\t\t);\n\t},\n);\nExpanderButton.displayName = \"CodeBlockExpanderButton\";\n\ntype CodeBlockIconProps = Omit<SvgAttributes, \"children\"> &\n\t(\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * A custom icon to display in the code block header.\n\t\t\t\t * (Pass only one of `svg` or `preset`.)\n\t\t\t\t */\n\t\t\t\tsvg: ReactNode;\n\t\t\t\t/**\n\t\t\t\t * A preset icon to display in the code block header.\n\t\t\t\t * (Pass only one of `svg` or `preset`.)\n\t\t\t\t */\n\t\t\t\tpreset?: undefined | never;\n\t\t }\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * A custom icon to display in the code block header.\n\t\t\t\t * (Pass only one of `svg` or `preset`.)\n\t\t\t\t */\n\t\t\t\tsvg?: undefined | never;\n\t\t\t\t/**\n\t\t\t\t * A preset icon to display in the code block header.\n\t\t\t\t * (Pass only one of `svg` or `preset`.)\n\t\t\t\t */\n\t\t\t\tpreset: Mode;\n\t\t }\n\t);\n\n/**\n * A small icon that represents the type of code block being displayed,\n * rendered as an SVG next to the code block title in the code block header.\n *\n * You can pass in a custom SVG component or use one of the presets\n * (pass only one of `svg` or `preset`).\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblockicon\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nfunction CodeBlockIconComponent({\n\tclassName,\n\tpreset,\n\tsvg: _svgProp,\n\t...props\n}: CodeBlockIconProps) {\n\tlet svg = _svgProp;\n\tif (preset != null) {\n\t\tswitch (preset) {\n\t\t\tcase \"file\":\n\t\t\t\tsvg = <FileTextIcon weight=\"fill\" />;\n\t\t\t\tbreak;\n\t\t\tcase \"cli\":\n\t\t\t\tsvg = <TerminalIcon weight=\"fill\" />;\n\t\t\t\tbreak;\n\t\t\tcase \"traffic-policy\":\n\t\t\t\tsvg = <TrafficPolicyFileIcon />;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn <MantleIcon className={className} svg={svg} {...props} />;\n}\nCodeBlockIconComponent.displayName = \"CodeBlockIcon\";\n\n/**\n * Code blocks render and apply syntax highlighting to blocks of code.\n *\n * @see https://mantle.ngrok.com/components/code-block\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst CodeBlock = {\n\t/**\n\t * Code blocks render and apply syntax highlighting to blocks of code.\n\t * This is the root component for all code block components.\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblockroot\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Header>\n\t * <CodeBlock.Icon preset=\"file\" />\n\t * <CodeBlock.Title>…</CodeBlock.Title>\n\t * </CodeBlock.Header>\n\t * <CodeBlock.Body>\n\t * <CodeBlock.CopyButton />\n\t * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n\t * </CodeBlock.Body>\n\t * <CodeBlock.ExpanderButton />\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tRoot,\n\t/**\n\t * The body of the `CodeBlock`. This is where the `CodeBlock.Code` and optional\n\t * `CodeBlock.CopyButton` is rendered.\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblockbody\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Body>\n\t * <CodeBlock.CopyButton />\n\t * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n\t * </CodeBlock.Body>\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tBody,\n\t/**\n\t * The `CodeBlock` content. This is where the code is rendered and syntax highlighted.\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblockcode\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Body>\n\t * <CodeBlock.Code\n\t * language=\"sh\"\n\t * value={fmtCode`ffmpeg -i multichannel.mxf -map 0:v:0 -map 0:a:0`}\n\t * />\n\t * </CodeBlock.Body>\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tCode,\n\t/**\n\t * The (optional) copy button of the `CodeBlock`. Render this as a child of the\n\t * `CodeBlock.Body` to allow users to copy the code block contents to their\n\t * clipboard.\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblockcopybutton\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Body>\n\t * <CodeBlock.CopyButton />\n\t * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n\t * </CodeBlock.Body>\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tCopyButton,\n\t/**\n\t * The (optional) expander button of the `CodeBlock`. Render this as a child of the\n\t * `CodeBlock.Body` to allow users to expand/collapse the code block contents.\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblockexpanderbutton\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Body>\n\t * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n\t * </CodeBlock.Body>\n\t * <CodeBlock.ExpanderButton />\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tExpanderButton,\n\t/**\n\t * The (optional) header slot of the `CodeBlock`. This is where things like the\n\t * `CodeBlock.Icon` and `CodeBlock.Title` are rendered.\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblockheader\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Header>\n\t * <CodeBlock.Icon preset=\"file\" />\n\t * <CodeBlock.Title>…</CodeBlock.Title>\n\t * </CodeBlock.Header>\n\t * <CodeBlock.Body>\n\t * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n\t * </CodeBlock.Body>\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tHeader,\n\t/**\n\t * A small icon that represents the type of code block being displayed,\n\t * rendered as an SVG next to the code block title in the code block header.\n\t *\n\t * You can pass in a custom SVG component or use one of the presets\n\t * (pass only one of `svg` or `preset`).\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblockicon\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Header>\n\t * <CodeBlock.Icon preset=\"file\" />\n\t * <CodeBlock.Title>…</CodeBlock.Title>\n\t * </CodeBlock.Header>\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tIcon: CodeBlockIconComponent,\n\t/**\n\t * The (optional) title of the `CodeBlock`. Default renders as an h3 element,\n\t * use asChild to render something else.\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblocktitle\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Header>\n\t * <CodeBlock.Icon preset=\"file\" />\n\t * <CodeBlock.Title>example.js</CodeBlock.Title>\n\t * </CodeBlock.Header>\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tTitle,\n} as const;\n\nexport {\n\t//,\n\tCodeBlock,\n};\n","import { parseBooleanish } from \"../../types/booleanish.js\";\nimport { type Indentation, isIndentation } from \"./indentation.js\";\n\nconst modes = [\n\t//,\n\t\"cli\",\n\t\"file\",\n\t\"traffic-policy\",\n] as const;\ntype Mode = (typeof modes)[number];\n\ntype MetaInput = {\n\tcollapsible?: boolean | undefined;\n\tdisableCopy?: boolean | undefined;\n\tindentation?: Indentation | undefined;\n\tmode?: Mode | undefined;\n\ttitle?: string | undefined;\n};\n\ntype Meta = {\n\tcollapsible: boolean;\n\tdisableCopy: boolean;\n\tindentation?: Indentation | undefined;\n\tmode?: Mode | undefined;\n\ttitle?: string | undefined;\n};\n\nconst defaultMeta = {\n\tcollapsible: false,\n\tdisableCopy: false,\n\tindentation: undefined,\n\tmode: undefined,\n\ttitle: undefined,\n} as const satisfies Meta;\n\ntype DefaultMeta = typeof defaultMeta;\n\n/**\n * Parses a markdown code block (```) metastring into a meta object.\n * Defaults to DefaultMeta if no metastring given or if metastring is invalid.\n * Useful for parsing the metastring from a markdown code block to pass into the\n * CodeBlock components as props.\n */\nfunction parseMetastring(input: string | undefined): Meta {\n\tconst metastring = input?.trim() ?? \"\";\n\tif (!metastring) {\n\t\treturn defaultMeta;\n\t}\n\n\tconst metaJson: Record<string, unknown> = {};\n\tconst tokens = tokenizeMetastring(metastring);\n\tfor (const token of tokens) {\n\t\tconst separatorIndex = token.indexOf(\"=\");\n\t\tconst key = separatorIndex === -1 ? token : token.slice(0, separatorIndex);\n\t\tconst value = separatorIndex === -1 ? undefined : token.slice(separatorIndex + 1);\n\n\t\tif (!key) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst normalizedValue = normalizeValue(value);\n\t\tmetaJson[key] = normalizedValue ?? true;\n\t}\n\n\ttry {\n\t\tconst parsed = parseMetaJson(metaJson);\n\n\t\t// return the parsed meta object, with default values filled in\n\t\treturn {\n\t\t\t...defaultMeta,\n\t\t\t...parsed,\n\t\t};\n\t} catch {\n\t\treturn defaultMeta;\n\t}\n}\n\nexport {\n\t//,\n\tdefaultMeta,\n\tparseMetastring,\n};\nexport type {\n\t//,\n\tMeta,\n\tMetaInput,\n\tMode,\n\tDefaultMeta,\n};\n\n/**\n * Remove leading and trailing `\"` quotes around value\n * @private\n *\n * @example\n * ```tsx\n * const normalized = normalizeValue('\"hello world\"');\n * // Returns: \"hello world\"\n *\n * const unchanged = normalizeValue('hello');\n * // Returns: \"hello\"\n * ```\n */\nexport function normalizeValue(value: string | undefined) {\n\treturn value?.trim().replace(/^\"(.*)\"$/, \"$1\");\n}\n\n/**\n * Splits a metastring into an array of tokens that can be parsed into a meta object.\n * Should allow for quotes and spaces in tokens\n * @private\n *\n * @example\n * ```tsx\n * const tokens = tokenizeMetastring('title=\"My File\" collapsible mode=cli');\n * // Returns: ['title=\"My File\"', 'collapsible', 'mode=cli']\n *\n * const simpleTokens = tokenizeMetastring('collapsible disableCopy');\n * // Returns: ['collapsible', 'disableCopy']\n * ```\n */\nexport function tokenizeMetastring(value: string | undefined): string[] {\n\tconst input = value?.trim() ?? \"\";\n\tconst result: string[] = [];\n\n\tlet currentString = \"\";\n\tlet inQuotes = false;\n\n\tfor (const char of input) {\n\t\tif (char === \" \" && !inQuotes) {\n\t\t\tif (currentString) {\n\t\t\t\tresult.push(currentString);\n\t\t\t\tcurrentString = \"\";\n\t\t\t}\n\t\t} else if (char === '\"') {\n\t\t\tinQuotes = !inQuotes;\n\t\t\tcurrentString += char;\n\t\t} else {\n\t\t\tcurrentString += char;\n\t\t}\n\t}\n\n\tif (currentString) {\n\t\tresult.push(currentString);\n\t}\n\n\treturn result;\n}\n\n/**\n * Type Predicate: checks if the given value is a valid mode.\n * @private\n */\nfunction isMode(input: unknown): input is Mode {\n\treturn input === \"cli\" || input === \"file\" || input === \"traffic-policy\";\n}\n\n/**\n * Parses a meta JSON object into a Meta object.\n * @private\n */\nfunction parseMetaJson(input: Record<string, unknown>): Meta {\n\tconst {\n\t\tcollapsible = defaultMeta.collapsible,\n\t\tdisableCopy = defaultMeta.disableCopy,\n\t\tindentation = defaultMeta.indentation,\n\t\tmode = defaultMeta.mode,\n\t\ttitle = defaultMeta.title,\n\t} = input;\n\n\treturn {\n\t\tcollapsible:\n\t\t\ttypeof collapsible === \"string\" || typeof collapsible === \"boolean\"\n\t\t\t\t? parseBooleanish(collapsible)\n\t\t\t\t: defaultMeta.collapsible,\n\t\tdisableCopy:\n\t\t\ttypeof disableCopy === \"string\" || typeof disableCopy === \"boolean\"\n\t\t\t\t? parseBooleanish(disableCopy)\n\t\t\t\t: defaultMeta.disableCopy,\n\t\tindentation: isIndentation(indentation) ? indentation : defaultMeta.indentation,\n\t\tmode: isMode(mode) ? mode : defaultMeta.mode,\n\t\ttitle: typeof title === \"string\" ? title.trim() : defaultMeta.title,\n\t};\n}\n"],"mappings":"u7CAmBA,SAAS,EAAW,EAAuB,CAC1C,IAAI,EAAwB,GAC5B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACtC,IAAM,EAAY,EAAM,GACxB,GACC,IAAc,KACd,IAAc,KACd,IAAc,KACd,IAAc,KACd,IAAc,IACb,CACD,EAAwB,EACxB,OAIF,GAAI,IAA0B,GAC7B,OAAO,EAGR,IAAI,EAAU,EAAM,MAAM,EAAG,EAAsB,CACnD,IAAK,IAAI,EAAI,EAAuB,EAAI,EAAM,OAAQ,IAAK,CAC1D,IAAM,EAAY,EAAM,GACxB,OAAQ,EAAR,CACC,IAAK,IACJ,GAAW,QACX,MACD,IAAK,IACJ,GAAW,OACX,MACD,IAAK,IACJ,GAAW,OACX,MACD,IAAK,IACJ,GAAW,SACX,MACD,IAAK,IACJ,GAAW,QACX,MACD,QACC,GAAW,GAGd,OAAO,EC5DR,MAAM,EAAe,CAAC,OAAQ,SAAS,CAMvC,SAAS,EAAc,EAAsC,CAC5D,OAAO,EAAa,SAAS,EAAqB,CASnD,SAAS,EACR,EACA,EACC,CAcD,OAZI,IAIA,EAAsB,EAAS,CAC3B,QAGJ,EAAwB,EAAS,CAC7B,WAoCT,MAAM,EAAyB,IAAI,IAfN,CAC5B,SACA,MACA,KACA,OACA,OACA,aACA,KACA,MACA,KACA,MACA,aACA,MACA,CAEmE,CAc9D,EAA2B,IAAI,IATN,CAC9B,SACA,KACA,OACA,MACA,OACA,KACA,CAEuE,CAQxE,SAAS,EAAsB,EAAwD,CACtF,OAAO,EAAuB,IAAI,EAAM,CAMzC,SAAS,EAAwB,EAA0D,CAC1F,OAAO,EAAyB,IAAI,EAAM,CC7F3C,SAAS,EAAQ,EAA+B,GAAG,EAA6B,CAC/E,GAAI,CAAC,GAAuB,EAAQ,EAAI,CAAC,MAAM,QAAQ,EAAO,CAC7D,MAAU,MACT,iLACA,CAGF,IAAM,EAAO,OAAO,IAAI,CAAE,IAAK,EAAS,CAAE,GAAG,EAAO,CAG9C,EAAY,EAAc,EAAK,CAC/B,EAAQ,EAAK,MAAM,CAAC,MAAM;EAAK,CAC/B,EAAsB,MAAc,EAAM,OAAO,CAEvD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACtC,IAAM,EAAO,EAAM,GACf,GAAQ,OAGZ,EAAgB,GAAKA,EAAwB,EAAK,CAAG,EAAO,EAAK,MAAM,EAAU,EAGlF,OAAO,EAAgB,KAAK;EAAK,CAYlC,SAAS,EAAc,EAAuB,CAC7C,IAAI,EAAY,IACZ,EAAS,EACT,EAAc,GAElB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACtC,IAAM,EAAO,EAAM,GAEnB,GAAI,EAAa,CAChB,GAAI,IAAS,KAAO,IAAS,IAAM,CAClC,GAAU,EACV,SAED,GAAI,IAAS;GAAQ,IAAS,KAAM,CACnC,EAAS,EACT,SAGD,GAAI,EAAS,IACZ,EAAY,EACR,IAAc,GACjB,MAAO,GAGT,EAAc,GACd,UAGG,IAAS;GAAQ,IAAS,QAC7B,EAAc,GACd,EAAS,GAIX,OAAO,IAAc,IAA2B,EAAI,EAMrD,SAAS,GAAuB,EAAmD,CAClF,OAAO,MAAM,QAAQ,EAAQ,EAAI,QAAS,GAAW,MAAM,QAAQ,EAAQ,IAAI,CAGhF,SAASA,EAAwB,EAAuB,CACvD,IAAM,EAAiB,EAAK,GAC5B,OAAO,GAAkB,MAAQ,IAAmB,KAAO,IAAmB,ICtE/E,SAAS,EAAqB,EAAe,EAA2B,CACvE,IAAM,EAAc,GAAS,aAAe,SACtC,EAAwB,EAAM,QAAQ,SAAU;EAAK,CACrD,EAAU,EAAsB,MAAM,CAE5C,GAAI,IAAY,GACf,MAAO,GAGR,IAAM,EAAY,EAAc,EAAsB,CAChD,EAAQ,EAAQ,MAAM;EAAK,CAC3B,EAAsB,MAAc,EAAM,OAAO,CAEvD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACtC,IAAM,EAAO,EAAM,GACf,GAAQ,OAIZ,EAAgB,GAAK,GADA,GAAwB,EAAK,CAAG,EAAO,EAAK,MAAM,EAAU,CAClB,EAAY,EAG5E,OAAO,EAAgB,KAAK;EAAK,CAYlC,SAAS,GAA4B,EAAc,EAAkC,CACpF,IAAI,EAAY,EAChB,KAAO,EAAY,EAAK,QAAQ,CAC/B,IAAM,EAAY,EAAK,GACvB,GAAI,IAAc,KAAO,IAAc,IACtC,MAED,GAAa,EAGd,GAAI,IAAc,GAAK,IAAc,EAAK,OACzC,OAAO,EAGR,IAAM,EAAoB,EAAK,MAAM,EAAG,EAAU,CAMlD,OAJC,IAAgB,SACb,EAAkB,QAAQ,MAAO,KAAK,CACtC,EAAkB,QAAQ,QAAS,IAAK,EAEP,EAAK,MAAM,EAAU,CAM3D,SAAS,GAAwB,EAAuB,CACvD,IAAM,EAAiB,EAAK,GAC5B,OAAO,GAAkB,MAAQ,IAAmB,KAAO,IAAmB,ICzE/E,MAAa,EAAqB,yKA+BjC,CAEK,EAAuB,IAAI,IAAY,EAAmB,CAWhE,SAAS,EACR,EACoB,CACpB,GAAI,CAAC,EACJ,MAAO,KAKR,IAAM,EAAgB,EAAM,MAAM,CAAC,MAAM,EAAM,QAAQ,IAAI,CAAG,EAAE,CAEhE,OAAO,EAAoB,EAAc,CAAG,EAAgB,KAM7D,MAAM,EAAuB,GACrB,OAAO,GAAU,UAAY,EAAqB,IAAI,EAAM,CAYpE,SAAS,EAAwB,EAA0C,KAAM,CAGhF,MADiC,YADpB,GAAY,OCnC1B,MACM,EAAuB,IAAI,IAMjC,SAAS,GAA2B,EAA6B,EAAuB,CACvF,MAAO,GAAG,EAAS,QAAQ,IAO5B,SAAS,GACR,EACA,EACA,EACS,CACT,IAAM,EAAW,GAA2B,EAAU,EAAM,CACtD,EAAS,EAAqB,IAAI,EAAS,CAEjD,GAAI,GAAU,KAGb,OAFA,EAAqB,OAAO,EAAS,CACrC,EAAqB,IAAI,EAAU,EAAO,CACnC,EAGR,IAAM,EAAcC,EAAY,UAAU,EAAO,EAAS,EAAS,CAGnE,GAFA,EAAqB,IAAI,EAAU,EAAY,CAE3C,EAAqB,KAAO,IAA6B,CAC5D,IAAM,EAAY,EAAqB,MAAM,CAAC,MAAM,CAAC,MACjD,GAAa,MAChB,EAAqB,OAAO,EAAU,CAIxC,OAAO,EAsBR,MAAM,EAAmB,EAAoC,CAC5D,OAAQ,IAAA,GACR,SAAU,GACV,gBAAiB,GACjB,eAAgB,GAChB,mBAAsB,GACtB,gBAAmB,GACnB,uBAA0B,GAC1B,sBAAyB,GACzB,qBAAwB,GACxB,CAAC,CAuBI,EAAO,GACX,CAAE,UAAU,GAAO,YAAW,GAAG,GAAS,IAAQ,CAClD,GAAM,CAAC,EAAU,GAAe,EAAS,GAAG,CACtC,CAAC,EAAiB,GAAsB,EAAS,GAAM,CACvD,CAAC,EAAgB,GAAqB,EAAS,GAAM,CACrD,CAAC,EAAQ,GAAa,EAA6B,IAAA,GAAU,CAE7D,EAAgC,OAEnC,CACA,SACA,WACA,kBACA,iBACA,eAAiB,GAAO,CACvB,EAAW,IACV,EAAO,GAAO,KAAM,iEAAiE,CAC9E,GACN,EAEH,cACA,qBACA,oBACA,iBAAmB,GAAO,CACzB,EAAW,GAAQ,CAClB,EAAO,IAAQ,EAAI,iEAAiE,EAEnF,EAEH,EACF,CAAC,EAAQ,EAAU,EAAiB,EAAe,CACnD,CAEK,EAAY,EAAU,EAAO,MAEnC,OACC,EAAC,EAAiB,SAAlB,CAA2B,MAAO,WACjC,EAAC,EAAD,CACC,YAAU,aACV,UAAW,EACV,mFACA,mBACA,EACA,CACI,MACL,GAAI,EACH,CAAA,CACyB,CAAA,EAG9B,CACD,EAAK,YAAc,YAuBnB,MAAM,EAAO,GACX,CAAE,UAAU,GAAO,YAAW,GAAG,GAAS,IAGnC,EAFW,EAAU,EAAO,MAE5B,CAAW,UAAW,EAAG,WAAY,EAAU,CAAO,MAAK,GAAI,EAAS,CAAA,CAEhF,CACD,EAAK,YAAc,gBAkDnB,MAAM,EAAO,GAEX,CACC,YACA,eAAgB,EAChB,YAAa,EACb,WAAW,OACX,gBAAiB,EACjB,QACA,WACA,QACA,GAAG,GAEJ,IACI,CACJ,IAAM,EAAK,GAAO,CACZ,CAAE,kBAAiB,iBAAgB,iBAAgB,cAAa,oBACrE,EAAW,EAAiB,CACvB,EAAc,EAAiB,EAAU,EAAgB,CAGzD,EAA4B,MAC3B,EAAqB,EAAO,CAAE,cAAa,CAAC,CAClD,CAAC,EAAO,EAAY,CACpB,CACK,EAAmC,MAClC,EAAW,EAA0B,CAC3C,CAAC,EAA0B,CAC3B,CACK,CAAC,EAA0B,GAA+B,MAK9D,EACD,CAED,MAAgB,CACf,IAAM,EAAUA,EAAY,UAAU,GACtC,EACC,EACA,4CAA4C,EAAS,oGAAoG,EAAmB,KAAK,KAAK,CAAC,GACvL,CAMD,EALoC,GACnC,EACA,EACA,EACA,CACuD,EACtD,CAAC,EAA2B,EAAS,CAAC,CAEzC,MAAgB,CACf,EAAY,EAA0B,EACpC,CAAC,EAA2B,EAAY,CAAC,CAE5C,OACC,EAAe,EAAG,KAEL,CACZ,EAAiB,EAAG,GAEnB,CAAC,EAAI,EAAgB,EAAiB,CAAC,CAE1C,IAAM,EAAoB,EAAwB,EAAS,CAE3D,OACC,EAAC,MAAD,CACC,gBAAe,EAAkB,EAAiB,IAAA,GAClD,UAAW,EACV,wDACA,0BACA,iCACA,EACA,EACA,CACD,YAAW,EACP,KACC,MACL,MAAO,CACN,GAAG,EACH,QAAS,EACT,WAAY,EACZ,CAGD,SAAU,GAAY,GACtB,GAAI,WAEJ,EAAC,OAAD,CACC,UAAWC,EAAK,oBAAqB,EAAkB,CACvD,wBAAyB,CACxB,OAAQ,EACR,CAGD,yBAAA,GACC,CAAA,CACG,CAAA,EAGR,CACD,EAAK,YAAc,gBAuBnB,MAAM,EAAS,GACb,CAAE,UAAU,GAAO,YAAW,GAAG,GAAS,IAIzC,EAHiB,EAAU,EAAO,MAGlC,CACC,UAAW,EACV,uFACA,EACA,CACI,MACL,GAAI,EACH,CAAA,CAGJ,CACD,EAAO,YAAc,kBAuBrB,MAAM,EAAQ,GAGX,CAAE,UAAU,GAAO,YAAW,GAAG,GAAS,IAI3C,EAHiB,EAAU,EAAO,KAGlC,CACM,MACL,UAAW,EAAG,sCAAuC,EAAU,CAC/D,GAAI,EACH,CAAA,CAEF,CACF,EAAM,YAAc,iBAoCpB,MAAM,EAAa,GACjB,CAAE,UAAU,GAAO,YAAW,SAAQ,cAAa,UAAS,GAAG,GAAS,IAAQ,CAChF,GAAM,CAAE,YAAa,EAAW,EAAiB,CAC3C,EAAG,GAAmB,GAAoB,CAC1C,CAAC,EAAW,GAAgB,EAAS,GAAM,CAC3C,EAAgB,EAAkD,IAAA,GAAU,CAYlF,OAVA,UACc,CACR,EAAc,SAAW,MAC5B,aAAa,EAAc,QAAQ,EAGnC,EAAE,CAAC,CAKL,EAHiB,EAAU,EAAO,SAGlC,CACC,KAAK,SACL,UAAW,EACV,6WACA,GACC,0NACD,EACA,CACI,MACL,QAAS,KAAO,IAAU,CACzB,GAAI,CAEH,GADA,IAAU,EAAM,CACZ,EAAM,iBAAkB,CAEvB,EAAc,SAAW,MAC5B,aAAa,EAAc,QAAQ,CAEpC,OAGD,MAAM,EAAgB,EAAS,CAC/B,IAAS,EAAS,CAClB,EAAa,GAAK,CAGd,EAAc,SAAW,MAC5B,aAAa,EAAc,QAAQ,CAIpC,EAAc,QAAU,eAAiB,CACxC,EAAa,GAAM,EACjB,IAAK,OACA,EAAO,CACf,IAAc,EAAM,GAGtB,GAAI,WArCL,CAuCC,EAAC,OAAD,CAAM,UAAU,mBAAU,YAAgB,CAAA,CACzC,EACA,EAAA,EAAA,CAAA,SAAA,CAAE,SAED,EAACC,EAAD,CAAY,IAAK,EAAC,EAAD,CAAW,OAAO,OAAS,CAAA,CAAE,UAAU,SAAW,CAAA,CACjE,CAAA,CAAA,CAEH,EAACA,EAAD,CAAY,IAAK,EAAC,EAAD,EAAY,CAAA,CAAE,UAAU,SAAW,CAAA,CAE1C,IAGd,CACD,EAAW,YAAc,sBAiCzB,MAAM,EAAiB,GACrB,CAAE,UAAU,GAAO,YAAW,UAAS,GAAG,GAAS,IAAQ,CAC3D,GAAM,CAAE,SAAQ,iBAAgB,oBAAmB,sBAClD,EAAW,EAAiB,CAY7B,OAVA,OACC,EAAmB,GAAK,KAEX,CACZ,EAAmB,GAAM,GAExB,CAAC,EAAmB,CAAC,CAKvB,EAHiB,EAAU,EAAO,SAGlC,CACC,GAAI,EACJ,gBAAe,EACf,gBAAe,EACf,UAAW,EACV,0IACA,EACA,CACI,MACL,KAAK,SACL,QAAU,GAAU,CACnB,EAAmB,GAAS,CAAC,EAAK,CAClC,IAAU,EAAM,WAZlB,CAeE,EAAiB,YAAc,YAAa,IAC7C,EAACA,EAAD,CACC,IAAK,EAAC,EAAD,CAAe,OAAO,OAAS,CAAA,CACpC,UAAW,EAAG,SAAU,GAAkB,aAAc,8BAA8B,CACrF,CAAA,CACS,IAGd,CACD,EAAe,YAAc,0BAsD7B,SAAS,EAAuB,CAC/B,YACA,SACA,IAAK,EACL,GAAG,GACmB,CACtB,IAAI,EAAM,EACV,GAAI,GAAU,KACb,OAAQ,EAAR,CACC,IAAK,OACJ,EAAM,EAAC,EAAD,CAAc,OAAO,OAAS,CAAA,CACpC,MACD,IAAK,MACJ,EAAM,EAAC,EAAD,CAAc,OAAO,OAAS,CAAA,CACpC,MACD,IAAK,iBACJ,EAAM,EAAC,EAAD,EAAyB,CAAA,CAC/B,MAIH,OAAO,EAACA,EAAD,CAAuB,YAAgB,MAAK,GAAI,EAAS,CAAA,CAEjE,EAAuB,YAAc,gBAsBrC,MAAM,GAAY,CAsBjB,OAiBA,OAkBA,OAkBA,aAiBA,iBAoBA,SAoBA,KAAM,EAiBN,QACA,CC/0BK,EAAc,CACnB,YAAa,GACb,YAAa,GACb,YAAa,IAAA,GACb,KAAM,IAAA,GACN,MAAO,IAAA,GACP,CAUD,SAAS,GAAgB,EAAiC,CACzD,IAAM,EAAa,GAAO,MAAM,EAAI,GACpC,GAAI,CAAC,EACJ,OAAO,EAGR,IAAM,EAAoC,EAAE,CACtC,EAAS,GAAmB,EAAW,CAC7C,IAAK,IAAM,KAAS,EAAQ,CAC3B,IAAM,EAAiB,EAAM,QAAQ,IAAI,CACnC,EAAM,IAAmB,GAAK,EAAQ,EAAM,MAAM,EAAG,EAAe,CACpE,EAAQ,IAAmB,GAAK,IAAA,GAAY,EAAM,MAAM,EAAiB,EAAE,CAE5E,IAKL,EAAS,GADe,GAAe,EAAM,EACV,IAGpC,GAAI,CACH,IAAM,EAAS,EAAc,EAAS,CAGtC,MAAO,CACN,GAAG,EACH,GAAG,EACH,MACM,CACP,OAAO,GA8BT,SAAgB,GAAe,EAA2B,CACzD,OAAO,GAAO,MAAM,CAAC,QAAQ,WAAY,KAAK,CAiB/C,SAAgB,GAAmB,EAAqC,CACvE,IAAM,EAAQ,GAAO,MAAM,EAAI,GACzB,EAAmB,EAAE,CAEvB,EAAgB,GAChB,EAAW,GAEf,IAAK,IAAM,KAAQ,EACd,IAAS,KAAO,CAAC,EAGnB,KADA,EAAO,KAAK,EAAc,CACV,KAEP,IAAS,MACnB,EAAW,CAAC,GAGZ,GAAiB,GAQnB,OAJI,GACH,EAAO,KAAK,EAAc,CAGpB,EAOR,SAAS,GAAO,EAA+B,CAC9C,OAAO,IAAU,OAAS,IAAU,QAAU,IAAU,iBAOzD,SAAS,EAAc,EAAsC,CAC5D,GAAM,CACL,cAAc,EAAY,YAC1B,cAAc,EAAY,YAC1B,cAAc,EAAY,YAC1B,OAAO,EAAY,KACnB,QAAQ,EAAY,OACjB,EAEJ,MAAO,CACN,YACC,OAAO,GAAgB,UAAY,OAAO,GAAgB,UACvD,EAAgB,EAAY,CAC5B,EAAY,YAChB,YACC,OAAO,GAAgB,UAAY,OAAO,GAAgB,UACvD,EAAgB,EAAY,CAC5B,EAAY,YAChB,YAAa,EAAc,EAAY,CAAG,EAAc,EAAY,YACpE,KAAM,GAAO,EAAK,CAAG,EAAO,EAAY,KACxC,MAAO,OAAO,GAAU,SAAW,EAAM,MAAM,CAAG,EAAY,MAC9D"}
|
|
1
|
+
{"version":3,"file":"code-block.js","names":["startsWithNonWhitespace","Highlighter","clsx","MantleIcon"],"sources":["../src/components/code-block/escape-html.ts","../src/components/code-block/indentation.ts","../src/components/code-block/fmt-code.ts","../src/components/code-block/normalize-indentation.ts","../src/components/code-block/supported-languages.ts","../src/components/code-block/code-block.tsx","../src/components/code-block/parse-metastring.ts"],"sourcesContent":["/**\n * Escapes special HTML characters in a string to their corresponding\n * HTML entities, preventing issues like unintended HTML rendering or\n * cross-site scripting (XSS) when injecting raw strings into the DOM\n * using `dangerouslySetInnerHTML`.\n *\n * Characters escaped:\n * - \\& => `&`;\n * - \\< => `<`;\n * - \\> => `>`;\n * - \\\" => `"`;\n * - \\' => `'`;\n *\n * @param {string} value The raw string to be escaped.\n *\n * @example\n * escapeHtml('<div>Hello & \"world\"</div>');\n * // Returns: '<div>Hello & "world"</div>'\n */\nfunction escapeHtml(value: string): string {\n\tlet firstSpecialCharIndex = -1;\n\tfor (let i = 0; i < value.length; i++) {\n\t\tconst character = value[i];\n\t\tif (\n\t\t\tcharacter === \"&\" ||\n\t\t\tcharacter === \"<\" ||\n\t\t\tcharacter === \">\" ||\n\t\t\tcharacter === '\"' ||\n\t\t\tcharacter === \"'\"\n\t\t) {\n\t\t\tfirstSpecialCharIndex = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (firstSpecialCharIndex === -1) {\n\t\treturn value;\n\t}\n\n\tlet escaped = value.slice(0, firstSpecialCharIndex);\n\tfor (let i = firstSpecialCharIndex; i < value.length; i++) {\n\t\tconst character = value[i];\n\t\tswitch (character) {\n\t\t\tcase \"&\":\n\t\t\t\tescaped += \"&\";\n\t\t\t\tbreak;\n\t\t\tcase \"<\":\n\t\t\t\tescaped += \"<\";\n\t\t\t\tbreak;\n\t\t\tcase \">\":\n\t\t\t\tescaped += \">\";\n\t\t\t\tbreak;\n\t\t\tcase '\"':\n\t\t\t\tescaped += \""\";\n\t\t\t\tbreak;\n\t\t\tcase \"'\":\n\t\t\t\tescaped += \"'\";\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tescaped += character;\n\t\t}\n\t}\n\treturn escaped;\n}\n\nexport {\n\t//,\n\tescapeHtml,\n};\n","import type { SupportedLanguage } from \"./supported-languages.js\";\n\nconst indentations = [\"tabs\", \"spaces\"] as const;\ntype Indentation = (typeof indentations)[number];\n\n/**\n * Type Predicate: checks if the given value is a valid indentation type.\n */\nfunction isIndentation(input: unknown): input is Indentation {\n\treturn indentations.includes(input as Indentation);\n}\n\n/**\n * Infers the indentation type based on the language and preferred indentation.\n *\n * @param language - The language to check.\n * @param preferredIndentation - The preferred indentation type (overrides what is detected).\n */\nfunction inferIndentation(\n\tlanguage: SupportedLanguage,\n\tpreferredIndentation: Indentation | undefined,\n) {\n\t// if the user has a preferred indentation, use that regardless of the language\n\tif (preferredIndentation) {\n\t\treturn preferredIndentation;\n\t}\n\n\tif (isTabIndentedLanguage(language)) {\n\t\treturn \"tabs\";\n\t}\n\n\tif (isSpaceIndentedLanguage(language)) {\n\t\treturn \"spaces\";\n\t}\n\n\treturn \"spaces\";\n}\n\nexport {\n\t//,\n\tindentations,\n\tinferIndentation,\n\tisIndentation,\n};\n\nexport type {\n\t//,\n\tIndentation,\n};\n\n/**\n * Languages that require or strongly prefer tabs\n */\nconst tabIndentedLanguages = [\n\t\"csharp\",\n\t\"css\",\n\t\"go\",\n\t\"html\",\n\t\"java\",\n\t\"javascript\",\n\t\"js\",\n\t\"jsx\",\n\t\"ts\",\n\t\"tsx\",\n\t\"typescript\",\n\t\"xml\",\n] as const satisfies SupportedLanguage[];\n\nconst tabIndentedLanguageSet = new Set<string>(tabIndentedLanguages);\n\n/**\n * Languages that require or strongly prefer spaces\n */\nconst spaceIndentedLanguages = [\n\t\"python\",\n\t\"py\",\n\t\"yaml\",\n\t\"yml\",\n\t\"ruby\",\n\t\"rb\",\n] as const satisfies SupportedLanguage[];\n\nconst spaceIndentedLanguageSet = new Set<string>(spaceIndentedLanguages);\n\ntype TabIndentedLanguage = (typeof tabIndentedLanguages)[number];\ntype SpaceIndentedLanguage = (typeof spaceIndentedLanguages)[number];\n\n/**\n * Type Predicate: checks if the given value is a required/preferred tab-indented language.\n */\nfunction isTabIndentedLanguage(value: SupportedLanguage): value is TabIndentedLanguage {\n\treturn tabIndentedLanguageSet.has(value);\n}\n\n/**\n * Type Predicate: checks if the given value is a required/preferred space-indented language.\n */\nfunction isSpaceIndentedLanguage(value: SupportedLanguage): value is SpaceIndentedLanguage {\n\treturn spaceIndentedLanguageSet.has(value);\n}\n","type Primitive = string | number | boolean | undefined | null;\n\n/**\n * Tagged template literal to format code blocks and normalize leading indentation\n */\nfunction fmtCode(strings: TemplateStringsArray, ...values: Primitive[]): string {\n\tif (!isTemplateStringsArray(strings) || !Array.isArray(values)) {\n\t\tthrow new Error(\n\t\t\t\"It looks like you tried to call `fmtCode` as a function. Make sure to use it as a tagged template.\\n\\tExample: fmtCode`SELECT * FROM users`, not fmtCode('SELECT * FROM users')\",\n\t\t);\n\t}\n\n\tconst text = String.raw({ raw: strings }, ...values);\n\n\t// fine the minimum indentation of the code block\n\tconst minIndent = findMinIndent(text);\n\tconst lines = text.trim().split(\"\\n\");\n\tconst normalizedLines = new Array<string>(lines.length);\n\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst line = lines[i];\n\t\tif (line == null) {\n\t\t\tcontinue;\n\t\t}\n\t\tnormalizedLines[i] = startsWithNonWhitespace(line) ? line : line.slice(minIndent);\n\t}\n\n\treturn normalizedLines.join(\"\\n\");\n}\n\nexport {\n\t//,\n\tfmtCode,\n\tfindMinIndent,\n};\n\n/**\n * Find the shortest indentation of a multiline string\n */\nfunction findMinIndent(value: string): number {\n\tlet minIndent = Number.POSITIVE_INFINITY;\n\tlet indent = 0;\n\tlet atLineStart = true;\n\n\tfor (let i = 0; i < value.length; i++) {\n\t\tconst char = value[i];\n\n\t\tif (atLineStart) {\n\t\t\tif (char === \" \" || char === \"\\t\") {\n\t\t\t\tindent += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (char === \"\\n\" || char === \"\\r\") {\n\t\t\t\tindent = 0;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (indent < minIndent) {\n\t\t\t\tminIndent = indent;\n\t\t\t\tif (minIndent === 0) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tatLineStart = false;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (char === \"\\n\" || char === \"\\r\") {\n\t\t\tatLineStart = true;\n\t\t\tindent = 0;\n\t\t}\n\t}\n\n\treturn minIndent === Number.POSITIVE_INFINITY ? 0 : minIndent;\n}\n\n/**\n * Type guard to check if a value is a `TemplateStringsArray`\n */\nfunction isTemplateStringsArray(strings: unknown): strings is TemplateStringsArray {\n\treturn Array.isArray(strings) && \"raw\" in strings && Array.isArray(strings.raw);\n}\n\nfunction startsWithNonWhitespace(line: string): boolean {\n\tconst firstCharacter = line[0];\n\treturn firstCharacter != null && firstCharacter !== \" \" && firstCharacter !== \"\\t\";\n}\n","import type { Indentation } from \"./indentation.js\";\nimport { findMinIndent } from \"./fmt-code.js\";\n\ntype Options = {\n\t/**\n\t * The indentation type to use. Can be either \"tabs\" or \"spaces\".\n\t * @default \"spaces\"\n\t */\n\tindentation?: Indentation;\n};\n\n/**\n * Trim any leading and trailing whitespace/empty lines, convert leading\n * indentation to the given options.indentation\n */\nfunction normalizeIndentation(value: string, options?: Options): string {\n\tconst indentation = options?.indentation ?? \"spaces\";\n\tconst normalizedLineEndings = value.replace(/\\r\\n?/g, \"\\n\");\n\tconst trimmed = normalizedLineEndings.trim();\n\n\tif (trimmed === \"\") {\n\t\treturn \"\";\n\t}\n\n\tconst minIndent = findMinIndent(normalizedLineEndings);\n\tconst lines = trimmed.split(\"\\n\");\n\tconst normalizedLines = new Array<string>(lines.length);\n\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst line = lines[i];\n\t\tif (line == null) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst dedentedLine = startsWithNonWhitespace(line) ? line : line.slice(minIndent);\n\t\tnormalizedLines[i] = normalizeLeadingIndentation(dedentedLine, indentation);\n\t}\n\n\treturn normalizedLines.join(\"\\n\");\n}\n\nexport {\n\t//,\n\tnormalizeIndentation,\n};\n\n/**\n * Rewrites only the leading indentation of a non-empty line into the requested\n * indentation style, leaving the rest of the line untouched.\n */\nfunction normalizeLeadingIndentation(line: string, indentation: Indentation): string {\n\tlet indentEnd = 0;\n\twhile (indentEnd < line.length) {\n\t\tconst character = line[indentEnd];\n\t\tif (character !== \" \" && character !== \"\\t\") {\n\t\t\tbreak;\n\t\t}\n\t\tindentEnd += 1;\n\t}\n\n\tif (indentEnd === 0 || indentEnd === line.length) {\n\t\treturn line;\n\t}\n\n\tconst leadingWhitespace = line.slice(0, indentEnd);\n\tconst normalizedLeadingWhitespace =\n\t\tindentation === \"spaces\"\n\t\t\t? leadingWhitespace.replace(/\\t/g, \" \")\n\t\t\t: leadingWhitespace.replace(/ {2}/g, \"\\t\");\n\n\treturn normalizedLeadingWhitespace + line.slice(indentEnd);\n}\n\n/**\n * Returns true when a line begins with visible content instead of indentation.\n */\nfunction startsWithNonWhitespace(line: string): boolean {\n\tconst firstCharacter = line[0];\n\treturn firstCharacter != null && firstCharacter !== \" \" && firstCharacter !== \"\\t\";\n}\n","/**\n * List of supported languages for syntax highlighting.\n * @private\n */\nexport const supportedLanguages = [\n\t\"bash\",\n\t\"cs\",\n\t\"csharp\",\n\t\"css\",\n\t\"dotnet\",\n\t\"go\",\n\t\"html\",\n\t\"java\",\n\t\"javascript\",\n\t\"js\",\n\t\"json\",\n\t\"jsx\",\n\t\"markup\",\n\t\"plain\",\n\t\"plaintext\",\n\t\"py\",\n\t\"python\",\n\t\"rb\",\n\t\"ruby\",\n\t\"rust\",\n\t\"sh\",\n\t\"shell\",\n\t\"text\",\n\t\"ts\",\n\t\"tsx\",\n\t\"txt\",\n\t\"typescript\",\n\t\"xml\",\n\t\"yaml\",\n\t\"yml\",\n] as const;\n\nconst supportedLanguageSet = new Set<string>(supportedLanguages);\n\n/**\n * Supported languages for syntax highlighting.\n */\ntype SupportedLanguage = (typeof supportedLanguages)[number];\n\n/**\n * Parses a markdown code block (```) language class into a SupportedLanguage.\n * Defaults to \"sh\" if no supported language is found.\n */\nfunction parseLanguage(\n\tvalue: `language-${string}` | `lang-${string}` | (string & {}) | undefined,\n): SupportedLanguage {\n\tif (!value) {\n\t\treturn \"sh\";\n\t}\n\n\t// remove leading \"language-\" and \"lang-\" prefixes\n\t// find first '-' and slice from there\n\tconst maybeLanguage = value.trim().slice(value.indexOf(\"-\") + 1);\n\n\treturn isSupportedLanguage(maybeLanguage) ? maybeLanguage : \"sh\";\n}\n\n/**\n * Type Predicate: checks if an arbitrary value is a supported syntax highlighting language.\n */\nconst isSupportedLanguage = (value: unknown): value is SupportedLanguage => {\n\treturn typeof value === \"string\" && supportedLanguageSet.has(value);\n};\n\n/**\n * A class name for a language that Prism.js can understand.\n */\ntype LanguageClass = `language-${SupportedLanguage}`;\n\n/**\n * Formats a language name into a class name that Prism.js can understand.\n * @default \"language-sh\"\n */\nfunction formatLanguageClassName(language: SupportedLanguage | undefined = \"sh\") {\n\tconst lang = language ?? \"sh\";\n\tconst className: LanguageClass = `language-${lang}`;\n\treturn className;\n}\n\nexport { isSupportedLanguage, parseLanguage, formatLanguageClassName };\nexport type { SupportedLanguage };\n","\"use client\";\n\nimport { CaretDownIcon } from \"@phosphor-icons/react/CaretDown\";\nimport { CheckIcon } from \"@phosphor-icons/react/Check\";\nimport { CopyIcon } from \"@phosphor-icons/react/Copy\";\nimport { FileTextIcon } from \"@phosphor-icons/react/FileText\";\nimport { TerminalIcon } from \"@phosphor-icons/react/Terminal\";\nimport clsx from \"clsx\";\nimport type {\n\tComponentProps,\n\tComponentRef,\n\tDispatch,\n\tHTMLAttributes,\n\tReactNode,\n\tSetStateAction,\n} from \"react\";\nimport {\n\tcreateContext,\n\tforwardRef,\n\tuseContext,\n\tuseEffect,\n\tuseId,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport assert from \"tiny-invariant\";\nimport { useCopyToClipboard } from \"../../hooks/use-copy-to-clipboard.js\";\nimport type { WithAsChild } from \"../../types/as-child.js\";\nimport { cx } from \"../../utils/cx/cx.js\";\nimport { Icon as MantleIcon } from \"../icon/icon.js\";\nimport type { SvgAttributes } from \"../icon/types.js\";\nimport { TrafficPolicyFileIcon } from \"../icons/traffic-policy-file.js\";\nimport { Slot } from \"../slot/index.js\";\nimport { escapeHtml } from \"./escape-html.js\";\nimport { Highlighter } from \"./highlighter.js\";\nimport { type Indentation, inferIndentation } from \"./indentation.js\";\nimport type { LineRange } from \"./line-numbers.js\";\nimport { normalizeIndentation } from \"./normalize-indentation.js\";\nimport type { Mode } from \"./parse-metastring.js\";\nimport type { SupportedLanguage } from \"./supported-languages.js\";\nimport { formatLanguageClassName, supportedLanguages } from \"./supported-languages.js\";\n\nconst MAX_HIGHLIGHT_CACHE_ENTRIES = 100;\nconst highlightedCodeCache = new Map<string, string>();\n\n/**\n * Builds a stable cache key for highlighted code using both the language and\n * normalized code contents.\n */\nfunction getHighlightedCodeCacheKey(language: SupportedLanguage, value: string): string {\n\treturn `${language}\\u0000${value}`;\n}\n\n/**\n * Returns highlighted HTML from the shared LRU cache when available, otherwise\n * highlights the code and stores the result for future reuse.\n */\nfunction getHighlightedCode(\n\tlanguage: SupportedLanguage,\n\tvalue: string,\n\tgrammar: Prism.Grammar,\n): string {\n\tconst cacheKey = getHighlightedCodeCacheKey(language, value);\n\tconst cached = highlightedCodeCache.get(cacheKey);\n\n\tif (cached != null) {\n\t\thighlightedCodeCache.delete(cacheKey);\n\t\thighlightedCodeCache.set(cacheKey, cached);\n\t\treturn cached;\n\t}\n\n\tconst highlighted = Highlighter.highlight(value, grammar, language);\n\thighlightedCodeCache.set(cacheKey, highlighted);\n\n\tif (highlightedCodeCache.size > MAX_HIGHLIGHT_CACHE_ENTRIES) {\n\t\tconst oldestKey = highlightedCodeCache.keys().next().value;\n\t\tif (oldestKey != null) {\n\t\t\thighlightedCodeCache.delete(oldestKey);\n\t\t}\n\t}\n\n\treturn highlighted;\n}\n\n/**\n * TODO(cody):\n * - fix line numbers, maybe try grid instead of :before and flex?\n * - fix line hightlighting\n * - fix line wrapping? horizontal scrolling has problems w/ line highlighting :(\n */\n\ntype CodeBlockContextType = {\n\tcodeId: string | undefined;\n\tcopyText: string;\n\thasCodeExpander: boolean;\n\tisCodeExpanded: boolean;\n\tregisterCodeId: (id: string) => void;\n\tsetCopyText: (newCopyText: string) => void;\n\tsetHasCodeExpander: (value: boolean) => void;\n\tsetIsCodeExpanded: Dispatch<SetStateAction<boolean>>;\n\tunregisterCodeId: (id: string) => void;\n};\n\nconst CodeBlockContext = createContext<CodeBlockContextType>({\n\tcodeId: undefined,\n\tcopyText: \"\",\n\thasCodeExpander: false,\n\tisCodeExpanded: false,\n\tregisterCodeId: () => {},\n\tsetCopyText: () => {},\n\tsetHasCodeExpander: () => {},\n\tsetIsCodeExpanded: () => {},\n\tunregisterCodeId: () => {},\n});\n\n/**\n * Code blocks render and apply syntax highlighting to blocks of code.\n * This is the root component for all code block components.\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblockroot\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst Root = forwardRef<ComponentRef<\"div\">, Omit<ComponentProps<\"div\">, \"align\"> & WithAsChild>(\n\t({ asChild = false, className, ...props }, ref) => {\n\t\tconst [copyText, setCopyText] = useState(\"\");\n\t\tconst [hasCodeExpander, setHasCodeExpander] = useState(false);\n\t\tconst [isCodeExpanded, setIsCodeExpanded] = useState(false);\n\t\tconst [codeId, setCodeId] = useState<string | undefined>(undefined);\n\n\t\tconst context: CodeBlockContextType = useMemo(\n\t\t\t() =>\n\t\t\t\t({\n\t\t\t\t\tcodeId,\n\t\t\t\t\tcopyText,\n\t\t\t\t\thasCodeExpander,\n\t\t\t\t\tisCodeExpanded,\n\t\t\t\t\tregisterCodeId: (id) => {\n\t\t\t\t\t\tsetCodeId((old) => {\n\t\t\t\t\t\t\tassert(old == null, \"You can only render a single CodeBlockCode within a CodeBlock.\");\n\t\t\t\t\t\t\treturn id;\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t\tsetCopyText,\n\t\t\t\t\tsetHasCodeExpander,\n\t\t\t\t\tsetIsCodeExpanded,\n\t\t\t\t\tunregisterCodeId: (id) => {\n\t\t\t\t\t\tsetCodeId((old) => {\n\t\t\t\t\t\t\tassert(old === id, \"You can only render a single CodeBlockCode within a CodeBlock.\");\n\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t}) as const,\n\t\t\t[codeId, copyText, hasCodeExpander, isCodeExpanded],\n\t\t);\n\n\t\tconst Component = asChild ? Slot : \"div\";\n\n\t\treturn (\n\t\t\t<CodeBlockContext.Provider value={context}>\n\t\t\t\t<Component\n\t\t\t\t\tdata-slot=\"code-block\"\n\t\t\t\t\tclassName={cx(\n\t\t\t\t\t\t\"text-mono overflow-hidden rounded-md border border-gray-300 bg-gray-50 font-mono\",\n\t\t\t\t\t\t\"[&_svg]:shrink-0\",\n\t\t\t\t\t\tclassName,\n\t\t\t\t\t)}\n\t\t\t\t\tref={ref}\n\t\t\t\t\t{...props}\n\t\t\t\t/>\n\t\t\t</CodeBlockContext.Provider>\n\t\t);\n\t},\n);\nRoot.displayName = \"CodeBlock\";\n\n/**\n * The body of the `CodeBlock`. This is where the `CodeBlock.Code` and optional\n * `CodeBlock.CopyButton` is rendered.\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblockbody\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst Body = forwardRef<ComponentRef<\"div\">, ComponentProps<\"div\"> & WithAsChild>(\n\t({ asChild = false, className, ...props }, ref) => {\n\t\tconst Component = asChild ? Slot : \"div\";\n\n\t\treturn <Component className={cx(\"relative\", className)} ref={ref} {...props} />;\n\t},\n);\nBody.displayName = \"CodeBlockBody\";\n\ntype CodeBlockCodeProps = Omit<ComponentProps<\"pre\">, \"children\"> & {\n\t/**\n\t * The code to display in the code block. Should be code formatted as a string. This code will be passed to our syntax highlighter.\n\t */\n\tvalue: string;\n\t/**\n\t * @todo not implemented yet\n\t */\n\thighlightLines?: (LineRange | number)[];\n\t/**\n\t * The type of indentation to use. Can be either \"tabs\" or \"spaces\".\n\t * @default inferred from the given language, fallback to `spaces`\n\t */\n\tindentation?: Indentation;\n\t/**\n\t * The language of the code block. This will be used to determine how to syntax highlight the code.\n\t * @default `\"text\"`.\n\t */\n\tlanguage?: SupportedLanguage;\n\t/**\n\t * @todo not implemented yet\n\t */\n\tshowLineNumbers?: boolean;\n};\n\n/**\n * The `CodeBlock` content. This is where the code is rendered and syntax highlighted.\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblockcode\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code\n * language=\"sh\"\n * value={fmtCode`ffmpeg -i multichannel.mxf -map 0:v:0 -map 0:a:0 -map 0:a:0 -c:a:0 ac3 -b:a:0 640k -ac:a:1 2 -c:a:1 aac -b:2 128k out.mp4`}\n * />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst Code = forwardRef<ComponentRef<\"pre\">, CodeBlockCodeProps>(\n\t(\n\t\t{\n\t\t\tclassName,\n\t\t\thighlightLines: _unusedHighlightLines, // not implemented yet\n\t\t\tindentation: propIndentation,\n\t\t\tlanguage = \"text\",\n\t\t\tshowLineNumbers: _unusedShowLineNumbers, // not implemented yet\n\t\t\tstyle,\n\t\t\ttabIndex,\n\t\t\tvalue,\n\t\t\t...props\n\t\t},\n\t\tref,\n\t) => {\n\t\tconst id = useId();\n\t\tconst { hasCodeExpander, isCodeExpanded, registerCodeId, setCopyText, unregisterCodeId } =\n\t\t\tuseContext(CodeBlockContext);\n\t\tconst indentation = inferIndentation(language, propIndentation);\n\n\t\t// trim any leading and trailing whitespace/empty lines, convert leading tabs to spaces\n\t\tconst normalizedAndTrimmedValue = useMemo(\n\t\t\t() => normalizeIndentation(value, { indentation }),\n\t\t\t[value, indentation],\n\t\t);\n\t\tconst escapedNormalizedAndTrimmedValue = useMemo(\n\t\t\t() => escapeHtml(normalizedAndTrimmedValue),\n\t\t\t[normalizedAndTrimmedValue],\n\t\t);\n\t\tconst [highlightedCodeInnerHtml, setHighlightedCodeInnerHtml] = useState(\n\t\t\t() =>\n\t\t\t\t// initialize the <code> inner html with escaped HTML since we are using\n\t\t\t\t// dangerouslySetInnerHTML to set the inner html of the <code> element\n\t\t\t\t// and use Prism.js to \"highlight\" the code in a useEffect (client-side only)\n\t\t\t\tescapedNormalizedAndTrimmedValue,\n\t\t);\n\n\t\tuseEffect(() => {\n\t\t\tconst grammar = Highlighter.languages[language];\n\t\t\tassert(\n\t\t\t\tgrammar,\n\t\t\t\t`CodeBlock does not support the language \"${language}\". The syntax highlighter does not have a grammar for this language. The supported languages are: ${supportedLanguages.join(\", \")}.`,\n\t\t\t);\n\t\t\tconst newHighlightedCodeInnerHtml = getHighlightedCode(\n\t\t\t\tlanguage,\n\t\t\t\tnormalizedAndTrimmedValue,\n\t\t\t\tgrammar,\n\t\t\t);\n\t\t\tsetHighlightedCodeInnerHtml(newHighlightedCodeInnerHtml);\n\t\t}, [normalizedAndTrimmedValue, language]);\n\n\t\tuseEffect(() => {\n\t\t\tsetCopyText(normalizedAndTrimmedValue);\n\t\t}, [normalizedAndTrimmedValue, setCopyText]);\n\n\t\tuseEffect(() => {\n\t\t\tregisterCodeId(id);\n\n\t\t\treturn () => {\n\t\t\t\tunregisterCodeId(id);\n\t\t\t};\n\t\t}, [id, registerCodeId, unregisterCodeId]);\n\n\t\tconst languageClassName = formatLanguageClassName(language);\n\n\t\treturn (\n\t\t\t<pre\n\t\t\t\taria-expanded={hasCodeExpander ? isCodeExpanded : undefined}\n\t\t\t\tclassName={cx(\n\t\t\t\t\t\"scrollbar overflow-x-auto overflow-y-hidden p-4 pr-14\",\n\t\t\t\t\t\"text-mono m-0 font-mono\",\n\t\t\t\t\t\"aria-collapsed:max-h-[13.6rem]\",\n\t\t\t\t\tlanguageClassName, // place it last because prism does weird stuff client side, causes hydration mismatches\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tdata-lang={language}\n\t\t\t\tid={id}\n\t\t\t\tref={ref}\n\t\t\t\tstyle={{\n\t\t\t\t\t...style,\n\t\t\t\t\ttabSize: 2,\n\t\t\t\t\tMozTabSize: 2,\n\t\t\t\t}}\n\t\t\t\t// prism.js adds a tabindex of 0 to the pre element by default (unless it's set)\n\t\t\t\t// this is unnecessary, we do not want this automatic behavior!\n\t\t\t\ttabIndex={tabIndex ?? -1}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<code\n\t\t\t\t\tclassName={clsx(\"text-size-inherit\", languageClassName)}\n\t\t\t\t\tdangerouslySetInnerHTML={{\n\t\t\t\t\t\t__html: highlightedCodeInnerHtml,\n\t\t\t\t\t}}\n\t\t\t\t\t// we need to suppress the hydration warning because we are setting the innerHTML of the code block\n\t\t\t\t\t// and using Prism.js to \"highlight\" the code in a useEffect (client-side only), which does different things on the client and server\n\t\t\t\t\tsuppressHydrationWarning\n\t\t\t\t/>\n\t\t\t</pre>\n\t\t);\n\t},\n);\nCode.displayName = \"CodeBlockCode\";\n\n/**\n * The (optional) header slot of the `CodeBlock`. This is where things like the\n * `CodeBlock.Icon` and `CodeBlock.Title` are rendered.\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblockheader\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst Header = forwardRef<ComponentRef<\"div\">, ComponentProps<\"div\"> & WithAsChild>(\n\t({ asChild = false, className, ...props }, ref) => {\n\t\tconst Component = asChild ? Slot : \"div\";\n\n\t\treturn (\n\t\t\t<Component\n\t\t\t\tclassName={cx(\n\t\t\t\t\t\"flex items-center gap-1 border-b border-gray-300 bg-gray-100 px-4 py-2 text-gray-700\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tref={ref}\n\t\t\t\t{...props}\n\t\t\t/>\n\t\t);\n\t},\n);\nHeader.displayName = \"CodeBlockHeader\";\n\n/**\n * The (optional) title of the `CodeBlock`. Default renders as an h3 element,\n * use asChild to render something else.\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblocktitle\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst Title = forwardRef<\n\tHTMLHeadingElement,\n\tHTMLAttributes<HTMLHeadingElement> & { asChild?: boolean }\n>(({ asChild = false, className, ...props }, ref) => {\n\tconst Component = asChild ? Slot : \"h3\";\n\n\treturn (\n\t\t<Component\n\t\t\tref={ref}\n\t\t\tclassName={cx(\"text-mono m-0 font-mono font-normal\", className)}\n\t\t\t{...props}\n\t\t/>\n\t);\n});\nTitle.displayName = \"CodeBlockTitle\";\n\ntype CodeBlockCopyButtonProps = Omit<ComponentProps<\"button\">, \"children\" | \"type\"> &\n\tWithAsChild & {\n\t\t/**\n\t\t * Callback fired when the copy button is clicked, passes the copied text as an argument.\n\t\t */\n\t\tonCopy?: (value: string) => void;\n\t\t/**\n\t\t * Callback fired when an error occurs during copying.\n\t\t */\n\t\tonCopyError?: (error: unknown) => void;\n\t};\n\n/**\n * The (optional) copy button of the `CodeBlock`. Render this as a child of the\n * `CodeBlock.Body` to allow users to copy the code block contents to their\n * clipboard.\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblockcopybutton\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst CopyButton = forwardRef<ComponentRef<\"button\">, CodeBlockCopyButtonProps>(\n\t({ asChild = false, className, onCopy, onCopyError, onClick, ...props }, ref) => {\n\t\tconst { copyText } = useContext(CodeBlockContext);\n\t\tconst [, copyToClipboard] = useCopyToClipboard();\n\t\tconst [wasCopied, setWasCopied] = useState(false);\n\t\tconst timeoutHandle = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n\t\tuseEffect(() => {\n\t\t\treturn () => {\n\t\t\t\tif (timeoutHandle.current != null) {\n\t\t\t\t\tclearTimeout(timeoutHandle.current);\n\t\t\t\t}\n\t\t\t};\n\t\t}, []);\n\n\t\tconst Component = asChild ? Slot : \"button\";\n\n\t\treturn (\n\t\t\t<Component\n\t\t\t\ttype=\"button\"\n\t\t\t\tclassName={cx(\n\t\t\t\t\t\"focus-visible:border-accent-600 focus-visible:ring-focus-accent absolute right-2.5 top-2.5 z-10 flex size-7 items-center justify-center rounded border border-gray-300 bg-gray-50 shadow-[-1rem_0_0.75rem_-0.375rem_var(--color-gray-50),1rem_0_0_-0.25rem_var(--color-gray-50)] hover:border-gray-400 hover:bg-gray-200 focus-visible:outline-hidden focus-visible:ring-4\",\n\t\t\t\t\twasCopied &&\n\t\t\t\t\t\t\"bg-filled-success text-on-filled hover:bg-filled-success focus:bg-filled-success focus-visible:border-success-600 focus-visible:ring-focus-success w-auto gap-1 border-transparent pl-2 pr-1.5 hover:border-transparent\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tref={ref}\n\t\t\t\tonClick={async (event) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tonClick?.(event);\n\t\t\t\t\t\tif (event.defaultPrevented) {\n\t\t\t\t\t\t\t// Clear any existing timeout\n\t\t\t\t\t\t\tif (timeoutHandle.current != null) {\n\t\t\t\t\t\t\t\tclearTimeout(timeoutHandle.current);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tawait copyToClipboard(copyText);\n\t\t\t\t\t\tonCopy?.(copyText);\n\t\t\t\t\t\tsetWasCopied(true);\n\n\t\t\t\t\t\t// Clear any existing timeout\n\t\t\t\t\t\tif (timeoutHandle.current != null) {\n\t\t\t\t\t\t\tclearTimeout(timeoutHandle.current);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Reset the copied state after a short delay\n\t\t\t\t\t\ttimeoutHandle.current = setTimeout(() => {\n\t\t\t\t\t\t\tsetWasCopied(false);\n\t\t\t\t\t\t}, 2000);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tonCopyError?.(error);\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<span className=\"sr-only\">Copy code</span>\n\t\t\t\t{wasCopied ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\tCopied\n\t\t\t\t\t\t<MantleIcon svg={<CheckIcon weight=\"bold\" />} className=\"size-4\" />\n\t\t\t\t\t</>\n\t\t\t\t) : (\n\t\t\t\t\t<MantleIcon svg={<CopyIcon />} className=\"-ml-px\" />\n\t\t\t\t)}\n\t\t\t</Component>\n\t\t);\n\t},\n);\nCopyButton.displayName = \"CodeBlockCopyButton\";\n\ntype CodeBlockExpanderButtonProps = Omit<\n\tComponentProps<\"button\">,\n\t\"children\" | \"aria-controls\" | \"aria-expanded\"\n> &\n\tWithAsChild;\n\n/**\n * The (optional) expander button of the `CodeBlock`. Render this as a child of the\n * `CodeBlock.Body` to allow users to expand/collapse the code block contents.\n *\n * @note If this component is preset, the `CodeBlock` will automatically know\n * that it should be collapsible. Don't use this component if you don't want\n * the code block to be collapsible.\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblockexpanderbutton\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst ExpanderButton = forwardRef<ComponentRef<\"button\">, CodeBlockExpanderButtonProps>(\n\t({ asChild = false, className, onClick, ...props }, ref) => {\n\t\tconst { codeId, isCodeExpanded, setIsCodeExpanded, setHasCodeExpander } =\n\t\t\tuseContext(CodeBlockContext);\n\n\t\tuseEffect(() => {\n\t\t\tsetHasCodeExpander(true);\n\n\t\t\treturn () => {\n\t\t\t\tsetHasCodeExpander(false);\n\t\t\t};\n\t\t}, [setHasCodeExpander]);\n\n\t\tconst Component = asChild ? Slot : \"button\";\n\n\t\treturn (\n\t\t\t<Component\n\t\t\t\t{...props}\n\t\t\t\taria-controls={codeId}\n\t\t\t\taria-expanded={isCodeExpanded}\n\t\t\t\tclassName={cx(\n\t\t\t\t\t\"flex w-full items-center justify-center gap-0.5 border-t border-gray-300 bg-gray-50 px-4 py-2 font-sans text-gray-700 hover:bg-gray-100\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tref={ref}\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={(event) => {\n\t\t\t\t\tsetIsCodeExpanded((prev) => !prev);\n\t\t\t\t\tonClick?.(event);\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{isCodeExpanded ? \"Show less\" : \"Show more\"}{\" \"}\n\t\t\t\t<MantleIcon\n\t\t\t\t\tsvg={<CaretDownIcon weight=\"bold\" />}\n\t\t\t\t\tclassName={cx(\"size-4\", isCodeExpanded && \"rotate-180\", \"transition-all duration-150\")}\n\t\t\t\t/>\n\t\t\t</Component>\n\t\t);\n\t},\n);\nExpanderButton.displayName = \"CodeBlockExpanderButton\";\n\ntype CodeBlockIconProps = Omit<SvgAttributes, \"children\"> &\n\t(\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * A custom icon to display in the code block header.\n\t\t\t\t * (Pass only one of `svg` or `preset`.)\n\t\t\t\t */\n\t\t\t\tsvg: ReactNode;\n\t\t\t\t/**\n\t\t\t\t * A preset icon to display in the code block header.\n\t\t\t\t * (Pass only one of `svg` or `preset`.)\n\t\t\t\t */\n\t\t\t\tpreset?: undefined | never;\n\t\t }\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * A custom icon to display in the code block header.\n\t\t\t\t * (Pass only one of `svg` or `preset`.)\n\t\t\t\t */\n\t\t\t\tsvg?: undefined | never;\n\t\t\t\t/**\n\t\t\t\t * A preset icon to display in the code block header.\n\t\t\t\t * (Pass only one of `svg` or `preset`.)\n\t\t\t\t */\n\t\t\t\tpreset: Mode;\n\t\t }\n\t);\n\n/**\n * A small icon that represents the type of code block being displayed,\n * rendered as an SVG next to the code block title in the code block header.\n *\n * You can pass in a custom SVG component or use one of the presets\n * (pass only one of `svg` or `preset`).\n *\n * @see https://mantle.ngrok.com/components/code-block#codeblockicon\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nfunction CodeBlockIconComponent({\n\tclassName,\n\tpreset,\n\tsvg: _svgProp,\n\t...props\n}: CodeBlockIconProps) {\n\tlet svg = _svgProp;\n\tif (preset != null) {\n\t\tswitch (preset) {\n\t\t\tcase \"file\":\n\t\t\t\tsvg = <FileTextIcon weight=\"fill\" />;\n\t\t\t\tbreak;\n\t\t\tcase \"cli\":\n\t\t\t\tsvg = <TerminalIcon weight=\"fill\" />;\n\t\t\t\tbreak;\n\t\t\tcase \"traffic-policy\":\n\t\t\t\tsvg = <TrafficPolicyFileIcon />;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn <MantleIcon className={className} svg={svg} {...props} />;\n}\nCodeBlockIconComponent.displayName = \"CodeBlockIcon\";\n\n/**\n * Code blocks render and apply syntax highlighting to blocks of code.\n *\n * @see https://mantle.ngrok.com/components/code-block\n *\n * @example\n * ```tsx\n * <CodeBlock.Root>\n * <CodeBlock.Header>\n * <CodeBlock.Icon preset=\"file\" />\n * <CodeBlock.Title>…</CodeBlock.Title>\n * </CodeBlock.Header>\n * <CodeBlock.Body>\n * <CodeBlock.CopyButton />\n * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n * </CodeBlock.Body>\n * <CodeBlock.ExpanderButton />\n * </CodeBlock.Root>\n * ```\n */\nconst CodeBlock = {\n\t/**\n\t * Code blocks render and apply syntax highlighting to blocks of code.\n\t * This is the root component for all code block components.\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblockroot\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Header>\n\t * <CodeBlock.Icon preset=\"file\" />\n\t * <CodeBlock.Title>…</CodeBlock.Title>\n\t * </CodeBlock.Header>\n\t * <CodeBlock.Body>\n\t * <CodeBlock.CopyButton />\n\t * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n\t * </CodeBlock.Body>\n\t * <CodeBlock.ExpanderButton />\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tRoot,\n\t/**\n\t * The body of the `CodeBlock`. This is where the `CodeBlock.Code` and optional\n\t * `CodeBlock.CopyButton` is rendered.\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblockbody\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Body>\n\t * <CodeBlock.CopyButton />\n\t * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n\t * </CodeBlock.Body>\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tBody,\n\t/**\n\t * The `CodeBlock` content. This is where the code is rendered and syntax highlighted.\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblockcode\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Body>\n\t * <CodeBlock.Code\n\t * language=\"sh\"\n\t * value={fmtCode`ffmpeg -i multichannel.mxf -map 0:v:0 -map 0:a:0`}\n\t * />\n\t * </CodeBlock.Body>\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tCode,\n\t/**\n\t * The (optional) copy button of the `CodeBlock`. Render this as a child of the\n\t * `CodeBlock.Body` to allow users to copy the code block contents to their\n\t * clipboard.\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblockcopybutton\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Body>\n\t * <CodeBlock.CopyButton />\n\t * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n\t * </CodeBlock.Body>\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tCopyButton,\n\t/**\n\t * The (optional) expander button of the `CodeBlock`. Render this as a child of the\n\t * `CodeBlock.Body` to allow users to expand/collapse the code block contents.\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblockexpanderbutton\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Body>\n\t * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n\t * </CodeBlock.Body>\n\t * <CodeBlock.ExpanderButton />\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tExpanderButton,\n\t/**\n\t * The (optional) header slot of the `CodeBlock`. This is where things like the\n\t * `CodeBlock.Icon` and `CodeBlock.Title` are rendered.\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblockheader\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Header>\n\t * <CodeBlock.Icon preset=\"file\" />\n\t * <CodeBlock.Title>…</CodeBlock.Title>\n\t * </CodeBlock.Header>\n\t * <CodeBlock.Body>\n\t * <CodeBlock.Code language=\"…\" value={fmtCode\\`…\\`} />\n\t * </CodeBlock.Body>\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tHeader,\n\t/**\n\t * A small icon that represents the type of code block being displayed,\n\t * rendered as an SVG next to the code block title in the code block header.\n\t *\n\t * You can pass in a custom SVG component or use one of the presets\n\t * (pass only one of `svg` or `preset`).\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblockicon\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Header>\n\t * <CodeBlock.Icon preset=\"file\" />\n\t * <CodeBlock.Title>…</CodeBlock.Title>\n\t * </CodeBlock.Header>\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tIcon: CodeBlockIconComponent,\n\t/**\n\t * The (optional) title of the `CodeBlock`. Default renders as an h3 element,\n\t * use asChild to render something else.\n\t *\n\t * @see https://mantle.ngrok.com/components/code-block#codeblocktitle\n\t *\n\t * @example\n\t * ```tsx\n\t * <CodeBlock.Root>\n\t * <CodeBlock.Header>\n\t * <CodeBlock.Icon preset=\"file\" />\n\t * <CodeBlock.Title>example.js</CodeBlock.Title>\n\t * </CodeBlock.Header>\n\t * </CodeBlock.Root>\n\t * ```\n\t */\n\tTitle,\n} as const;\n\nexport {\n\t//,\n\tCodeBlock,\n};\n","import { parseBooleanish } from \"../../types/booleanish.js\";\nimport { type Indentation, isIndentation } from \"./indentation.js\";\n\nconst modes = [\n\t//,\n\t\"cli\",\n\t\"file\",\n\t\"traffic-policy\",\n] as const;\ntype Mode = (typeof modes)[number];\n\ntype MetaInput = {\n\tcollapsible?: boolean | undefined;\n\tdisableCopy?: boolean | undefined;\n\tindentation?: Indentation | undefined;\n\tmode?: Mode | undefined;\n\ttitle?: string | undefined;\n};\n\ntype Meta = {\n\tcollapsible: boolean;\n\tdisableCopy: boolean;\n\tindentation?: Indentation | undefined;\n\tmode?: Mode | undefined;\n\ttitle?: string | undefined;\n};\n\nconst defaultMeta = {\n\tcollapsible: false,\n\tdisableCopy: false,\n\tindentation: undefined,\n\tmode: undefined,\n\ttitle: undefined,\n} as const satisfies Meta;\n\ntype DefaultMeta = typeof defaultMeta;\n\n/**\n * Parses a markdown code block (```) metastring into a meta object.\n * Defaults to DefaultMeta if no metastring given or if metastring is invalid.\n * Useful for parsing the metastring from a markdown code block to pass into the\n * CodeBlock components as props.\n */\nfunction parseMetastring(input: string | undefined): Meta {\n\tconst metastring = input?.trim() ?? \"\";\n\tif (!metastring) {\n\t\treturn defaultMeta;\n\t}\n\n\tconst metaJson: Record<string, unknown> = {};\n\tconst tokens = tokenizeMetastring(metastring);\n\tfor (const token of tokens) {\n\t\tconst separatorIndex = token.indexOf(\"=\");\n\t\tconst key = separatorIndex === -1 ? token : token.slice(0, separatorIndex);\n\t\tconst value = separatorIndex === -1 ? undefined : token.slice(separatorIndex + 1);\n\n\t\tif (!key) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst normalizedValue = normalizeValue(value);\n\t\tmetaJson[key] = normalizedValue ?? true;\n\t}\n\n\ttry {\n\t\tconst parsed = parseMetaJson(metaJson);\n\n\t\t// return the parsed meta object, with default values filled in\n\t\treturn {\n\t\t\t...defaultMeta,\n\t\t\t...parsed,\n\t\t};\n\t} catch {\n\t\treturn defaultMeta;\n\t}\n}\n\nexport {\n\t//,\n\tdefaultMeta,\n\tparseMetastring,\n};\nexport type {\n\t//,\n\tMeta,\n\tMetaInput,\n\tMode,\n\tDefaultMeta,\n};\n\n/**\n * Remove leading and trailing `\"` quotes around value\n * @private\n *\n * @example\n * ```tsx\n * const normalized = normalizeValue('\"hello world\"');\n * // Returns: \"hello world\"\n *\n * const unchanged = normalizeValue('hello');\n * // Returns: \"hello\"\n * ```\n */\nexport function normalizeValue(value: string | undefined) {\n\treturn value?.trim().replace(/^\"(.*)\"$/, \"$1\");\n}\n\n/**\n * Splits a metastring into an array of tokens that can be parsed into a meta object.\n * Should allow for quotes and spaces in tokens\n * @private\n *\n * @example\n * ```tsx\n * const tokens = tokenizeMetastring('title=\"My File\" collapsible mode=cli');\n * // Returns: ['title=\"My File\"', 'collapsible', 'mode=cli']\n *\n * const simpleTokens = tokenizeMetastring('collapsible disableCopy');\n * // Returns: ['collapsible', 'disableCopy']\n * ```\n */\nexport function tokenizeMetastring(value: string | undefined): string[] {\n\tconst input = value?.trim() ?? \"\";\n\tconst result: string[] = [];\n\n\tlet currentString = \"\";\n\tlet inQuotes = false;\n\n\tfor (const char of input) {\n\t\tif (char === \" \" && !inQuotes) {\n\t\t\tif (currentString) {\n\t\t\t\tresult.push(currentString);\n\t\t\t\tcurrentString = \"\";\n\t\t\t}\n\t\t} else if (char === '\"') {\n\t\t\tinQuotes = !inQuotes;\n\t\t\tcurrentString += char;\n\t\t} else {\n\t\t\tcurrentString += char;\n\t\t}\n\t}\n\n\tif (currentString) {\n\t\tresult.push(currentString);\n\t}\n\n\treturn result;\n}\n\n/**\n * Type Predicate: checks if the given value is a valid mode.\n * @private\n */\nfunction isMode(input: unknown): input is Mode {\n\treturn input === \"cli\" || input === \"file\" || input === \"traffic-policy\";\n}\n\n/**\n * Parses a meta JSON object into a Meta object.\n * @private\n */\nfunction parseMetaJson(input: Record<string, unknown>): Meta {\n\tconst {\n\t\tcollapsible = defaultMeta.collapsible,\n\t\tdisableCopy = defaultMeta.disableCopy,\n\t\tindentation = defaultMeta.indentation,\n\t\tmode = defaultMeta.mode,\n\t\ttitle = defaultMeta.title,\n\t} = input;\n\n\treturn {\n\t\tcollapsible:\n\t\t\ttypeof collapsible === \"string\" || typeof collapsible === \"boolean\"\n\t\t\t\t? parseBooleanish(collapsible)\n\t\t\t\t: defaultMeta.collapsible,\n\t\tdisableCopy:\n\t\t\ttypeof disableCopy === \"string\" || typeof disableCopy === \"boolean\"\n\t\t\t\t? parseBooleanish(disableCopy)\n\t\t\t\t: defaultMeta.disableCopy,\n\t\tindentation: isIndentation(indentation) ? indentation : defaultMeta.indentation,\n\t\tmode: isMode(mode) ? mode : defaultMeta.mode,\n\t\ttitle: typeof title === \"string\" ? title.trim() : defaultMeta.title,\n\t};\n}\n"],"mappings":"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"}
|
package/dist/command.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as WithAsChild } from "./as-child-XMVTepJu.js";
|
|
2
|
-
import { t as Dialog } from "./dialog-
|
|
2
|
+
import { t as Dialog } from "./dialog-BAZ_LYI4.js";
|
|
3
3
|
import * as react from "react";
|
|
4
4
|
import { ComponentProps, ComponentPropsWithoutRef } from "react";
|
|
5
5
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
@@ -85,11 +85,11 @@ declare const Command: {
|
|
|
85
85
|
*/
|
|
86
86
|
readonly Root: react.ForwardRefExoticComponent<Omit<{
|
|
87
87
|
children?: React.ReactNode;
|
|
88
|
-
} & Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof react.HTMLAttributes<HTMLDivElement
|
|
88
|
+
} & Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof react.HTMLAttributes<HTMLDivElement>> & {
|
|
89
89
|
ref?: React.Ref<HTMLDivElement>;
|
|
90
90
|
} & {
|
|
91
91
|
asChild?: boolean;
|
|
92
|
-
}, keyof react.HTMLAttributes<HTMLDivElement> | "asChild"
|
|
92
|
+
}, "key" | keyof react.HTMLAttributes<HTMLDivElement> | "asChild"> & {
|
|
93
93
|
label?: string;
|
|
94
94
|
shouldFilter?: boolean;
|
|
95
95
|
filter?: (value: string, search: string, keywords?: string[]) => number;
|
|
@@ -142,7 +142,7 @@ declare const Command: {
|
|
|
142
142
|
ref?: React.Ref<HTMLInputElement>;
|
|
143
143
|
} & {
|
|
144
144
|
asChild?: boolean;
|
|
145
|
-
}, "
|
|
145
|
+
}, "key" | "asChild" | keyof react.InputHTMLAttributes<HTMLInputElement>>, "value" | "onChange" | "type"> & {
|
|
146
146
|
value?: string;
|
|
147
147
|
onValueChange?: (search: string) => void;
|
|
148
148
|
} & react.RefAttributes<HTMLInputElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
@@ -159,11 +159,11 @@ declare const Command: {
|
|
|
159
159
|
*/
|
|
160
160
|
readonly List: react.ForwardRefExoticComponent<Omit<{
|
|
161
161
|
children?: React.ReactNode;
|
|
162
|
-
} & Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof react.HTMLAttributes<HTMLDivElement
|
|
162
|
+
} & Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof react.HTMLAttributes<HTMLDivElement>> & {
|
|
163
163
|
ref?: React.Ref<HTMLDivElement>;
|
|
164
164
|
} & {
|
|
165
165
|
asChild?: boolean;
|
|
166
|
-
}, keyof react.HTMLAttributes<HTMLDivElement> | "asChild"
|
|
166
|
+
}, "key" | keyof react.HTMLAttributes<HTMLDivElement> | "asChild"> & {
|
|
167
167
|
label?: string;
|
|
168
168
|
} & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
169
169
|
/**
|
|
@@ -178,11 +178,11 @@ declare const Command: {
|
|
|
178
178
|
*/
|
|
179
179
|
readonly Empty: react.ForwardRefExoticComponent<Omit<{
|
|
180
180
|
children?: React.ReactNode;
|
|
181
|
-
} & Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof react.HTMLAttributes<HTMLDivElement
|
|
181
|
+
} & Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof react.HTMLAttributes<HTMLDivElement>> & {
|
|
182
182
|
ref?: React.Ref<HTMLDivElement>;
|
|
183
183
|
} & {
|
|
184
184
|
asChild?: boolean;
|
|
185
|
-
}, keyof react.HTMLAttributes<HTMLDivElement> | "asChild"
|
|
185
|
+
}, "key" | keyof react.HTMLAttributes<HTMLDivElement> | "asChild"> & react.RefAttributes<HTMLDivElement>, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
186
186
|
/**
|
|
187
187
|
* The group component for the Command component.
|
|
188
188
|
*
|
|
@@ -199,11 +199,11 @@ declare const Command: {
|
|
|
199
199
|
*/
|
|
200
200
|
readonly Group: react.ForwardRefExoticComponent<Omit<{
|
|
201
201
|
children?: React.ReactNode;
|
|
202
|
-
} & Omit<Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof react.HTMLAttributes<HTMLDivElement
|
|
202
|
+
} & Omit<Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof react.HTMLAttributes<HTMLDivElement>> & {
|
|
203
203
|
ref?: React.Ref<HTMLDivElement>;
|
|
204
204
|
} & {
|
|
205
205
|
asChild?: boolean;
|
|
206
|
-
}, keyof react.HTMLAttributes<HTMLDivElement> | "asChild"
|
|
206
|
+
}, "key" | keyof react.HTMLAttributes<HTMLDivElement> | "asChild">, "value" | "heading"> & {
|
|
207
207
|
heading?: React.ReactNode;
|
|
208
208
|
value?: string;
|
|
209
209
|
forceMount?: boolean;
|
|
@@ -222,11 +222,11 @@ declare const Command: {
|
|
|
222
222
|
*/
|
|
223
223
|
readonly Item: react.ForwardRefExoticComponent<Omit<{
|
|
224
224
|
children?: React.ReactNode;
|
|
225
|
-
} & Omit<Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof react.HTMLAttributes<HTMLDivElement
|
|
225
|
+
} & Omit<Pick<Pick<react.DetailedHTMLProps<react.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof react.HTMLAttributes<HTMLDivElement>> & {
|
|
226
226
|
ref?: React.Ref<HTMLDivElement>;
|
|
227
227
|
} & {
|
|
228
228
|
asChild?: boolean;
|
|
229
|
-
}, keyof react.HTMLAttributes<HTMLDivElement> | "asChild"
|
|
229
|
+
}, "key" | keyof react.HTMLAttributes<HTMLDivElement> | "asChild">, "disabled" | "value" | "onSelect"> & {
|
|
230
230
|
disabled?: boolean;
|
|
231
231
|
onSelect?: (value: string) => void;
|
|
232
232
|
value?: string;
|
package/dist/data-table.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as Root } from "./primitive-tuHqhoRE.js";
|
|
2
|
-
import { n as IconButtonProps } from "./icon-button-
|
|
2
|
+
import { n as IconButtonProps } from "./icon-button-ikEU0Ctp.js";
|
|
3
3
|
import * as react from "react";
|
|
4
4
|
import { ComponentProps } from "react";
|
|
5
5
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
@@ -419,4 +419,4 @@ declare const Dialog: {
|
|
|
419
419
|
};
|
|
420
420
|
//#endregion
|
|
421
421
|
export { Dialog as t };
|
|
422
|
-
//# sourceMappingURL=dialog-
|
|
422
|
+
//# sourceMappingURL=dialog-BAZ_LYI4.d.ts.map
|
package/dist/dialog.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import * as class_variance_authority_types0 from "class-variance-authority/types
|
|
|
8
8
|
declare const iconButtonVariants: (props?: ({
|
|
9
9
|
appearance?: "ghost" | "outlined" | null | undefined;
|
|
10
10
|
isLoading?: boolean | null | undefined;
|
|
11
|
-
size?: "
|
|
11
|
+
size?: "md" | "sm" | "xs" | null | undefined;
|
|
12
12
|
} & class_variance_authority_types0.ClassProp) | undefined) => string;
|
|
13
13
|
type IconButtonVariants = VariantProps<typeof iconButtonVariants>;
|
|
14
14
|
/**
|
|
@@ -95,4 +95,4 @@ type IconButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & WithAsChild & I
|
|
|
95
95
|
declare const IconButton: react.ForwardRefExoticComponent<IconButtonProps & react.RefAttributes<HTMLButtonElement>>;
|
|
96
96
|
//#endregion
|
|
97
97
|
export { IconButtonProps as n, IconButton as t };
|
|
98
|
-
//# sourceMappingURL=icon-button-
|
|
98
|
+
//# sourceMappingURL=icon-button-ikEU0Ctp.d.ts.map
|
|
@@ -23,7 +23,7 @@ type InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "
|
|
|
23
23
|
* />
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
|
-
declare const Input: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "
|
|
26
|
+
declare const Input: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & WithAutoComplete & WithInputType & WithValidation & {
|
|
27
27
|
children?: react.ReactNode | undefined;
|
|
28
28
|
} & react.RefAttributes<HTMLInputElement>>;
|
|
29
29
|
type InputCaptureProps = Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & BaseProps;
|
|
@@ -41,7 +41,7 @@ type InputCaptureProps = Omit<InputHTMLAttributes<HTMLInputElement>, "autoComple
|
|
|
41
41
|
* </Input>
|
|
42
42
|
* ```
|
|
43
43
|
*/
|
|
44
|
-
declare const InputCapture: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "
|
|
44
|
+
declare const InputCapture: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & WithAutoComplete & WithInputType & WithValidation & react.RefAttributes<HTMLInputElement>>;
|
|
45
45
|
//#endregion
|
|
46
46
|
//#region src/components/input/password-input.d.ts
|
|
47
47
|
type PasswordInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & WithValidation & WithAutoComplete & {
|
|
@@ -70,7 +70,7 @@ type PasswordInputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "autoCompl
|
|
|
70
70
|
* />
|
|
71
71
|
* ```
|
|
72
72
|
*/
|
|
73
|
-
declare const PasswordInput: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "
|
|
73
|
+
declare const PasswordInput: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "autoComplete" | "type"> & WithValidation & WithAutoComplete & {
|
|
74
74
|
/**
|
|
75
75
|
* Callback for when the visibility of the password value changes.
|
|
76
76
|
*/
|
|
@@ -100,4 +100,4 @@ declare const PasswordInput: react.ForwardRefExoticComponent<Omit<InputHTMLAttri
|
|
|
100
100
|
declare function isInput(value: unknown): value is HTMLInputElement;
|
|
101
101
|
//#endregion
|
|
102
102
|
export { InputCapture as a, Input as i, PasswordInput as n, InputCaptureProps as o, PasswordInputProps as r, InputProps as s, isInput as t };
|
|
103
|
-
//# sourceMappingURL=index-
|
|
103
|
+
//# sourceMappingURL=index-aG53Gdqx.d.ts.map
|
package/dist/input.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { a as WithInputType, i as WithAutoComplete, n as InputType, o as WithValidation, r as Validation, t as AutoComplete } from "./types-DgXUgkpc.js";
|
|
2
|
-
import { a as InputCapture, i as Input, n as PasswordInput, o as InputCaptureProps, r as PasswordInputProps, s as InputProps, t as isInput } from "./index-
|
|
2
|
+
import { a as InputCapture, i as Input, n as PasswordInput, o as InputCaptureProps, r as PasswordInputProps, s as InputProps, t as isInput } from "./index-aG53Gdqx.js";
|
|
3
3
|
export { AutoComplete, Input, InputCapture, InputCaptureProps, InputProps, InputType, PasswordInput, PasswordInputProps, Validation, WithAutoComplete, WithInputType, WithValidation, isInput };
|
package/dist/mantle.css
CHANGED
|
@@ -533,3 +533,66 @@ MARK: UTILITIES
|
|
|
533
533
|
@utility animation-duration-* {
|
|
534
534
|
animation-duration: --value([length]);
|
|
535
535
|
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
MARK: SYNTAX HIGHLIGHT TOKEN STYLES
|
|
539
|
+
*/
|
|
540
|
+
.token.comment,
|
|
541
|
+
.token.prolog,
|
|
542
|
+
.token.doctype,
|
|
543
|
+
.token.cdata {
|
|
544
|
+
color: var(--color-gray-500);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.token.property,
|
|
548
|
+
.token.tag,
|
|
549
|
+
.token.boolean,
|
|
550
|
+
.token.number,
|
|
551
|
+
.token.constant,
|
|
552
|
+
.token.symbol,
|
|
553
|
+
.token.deleted {
|
|
554
|
+
color: var(--color-indigo-600);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
.token.attr-name,
|
|
558
|
+
.token.string,
|
|
559
|
+
.token.char,
|
|
560
|
+
.token.builtin,
|
|
561
|
+
.token.inserted {
|
|
562
|
+
color: var(--color-green-600);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.token.operator,
|
|
566
|
+
.token.entity,
|
|
567
|
+
.token.url,
|
|
568
|
+
.language-css .token.string,
|
|
569
|
+
.style .token.string {
|
|
570
|
+
color: var(--color-green-600);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.token.selector,
|
|
574
|
+
.token.atrule,
|
|
575
|
+
.token.attr-value,
|
|
576
|
+
.token.keyword {
|
|
577
|
+
color: var(--color-blue-600);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.token.function,
|
|
581
|
+
.token.class-name {
|
|
582
|
+
color: var(--color-blue-600);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.token.regex,
|
|
586
|
+
.token.important,
|
|
587
|
+
.token.variable {
|
|
588
|
+
color: var(--color-yellow-600);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.token.important,
|
|
592
|
+
.token.bold {
|
|
593
|
+
font-weight: bold;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.token.italic {
|
|
597
|
+
font-style: italic;
|
|
598
|
+
}
|
package/dist/sheet.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as Root } from "./primitive-tuHqhoRE.js";
|
|
2
|
-
import { n as IconButtonProps } from "./icon-button-
|
|
2
|
+
import { n as IconButtonProps } from "./icon-button-ikEU0Ctp.js";
|
|
3
3
|
import * as react from "react";
|
|
4
4
|
import { HTMLAttributes } from "react";
|
|
5
5
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
package/dist/split-button.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as IconButton } from "./icon-button-
|
|
2
|
-
import { t as Button } from "./button-
|
|
1
|
+
import { t as IconButton } from "./icon-button-ikEU0Ctp.js";
|
|
2
|
+
import { t as Button } from "./button-DQO0IuzC.js";
|
|
3
3
|
import { t as DropdownMenu } from "./dropdown-menu-D_ZoY1AH.js";
|
|
4
4
|
import * as react from "react";
|
|
5
5
|
import { ComponentProps, ReactNode } from "react";
|
package/dist/theme.d.ts
CHANGED
|
@@ -2,8 +2,60 @@ import { a as isResolvedTheme, c as themes, i as Theme, n as $theme, o as isThem
|
|
|
2
2
|
import { PropsWithChildren } from "react";
|
|
3
3
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
4
|
|
|
5
|
-
//#region src/components/theme/mantle-
|
|
6
|
-
|
|
5
|
+
//#region src/components/theme/mantle-style-sheets.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Browser-accessible URLs for mantle's three lazy-loaded theme stylesheets.
|
|
8
|
+
*
|
|
9
|
+
* Use {@link mantleStyleSheetUrls} to create this object from Vite `?url` imports.
|
|
10
|
+
*/
|
|
11
|
+
type MantleThemeCssUrls = {
|
|
12
|
+
/**
|
|
13
|
+
* Browser-accessible URL for `mantle-dark.css`.
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* // in vite app
|
|
17
|
+
* import darkCssUrl from "@ngrok/mantle/mantle-dark.css?url"
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
darkCssUrl: string;
|
|
21
|
+
/**
|
|
22
|
+
* Browser-accessible URL for `mantle-light-high-contrast.css`.
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* // in vite app
|
|
26
|
+
* import lightHighContrastCssUrl from "@ngrok/mantle/mantle-light-high-contrast.css?url"
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
lightHighContrastCssUrl: string;
|
|
30
|
+
/**
|
|
31
|
+
* Browser-accessible URL for `mantle-dark-high-contrast.css`.
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* // in vite app
|
|
35
|
+
* import darkHighContrastCssUrl from "@ngrok/mantle/mantle-dark-high-contrast.css?url"
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
darkHighContrastCssUrl: string;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Collects the three Vite `?url` imports for mantle's theme stylesheets into a typed object
|
|
42
|
+
* that can be spread directly into `<MantleStyleSheets>`.
|
|
43
|
+
*
|
|
44
|
+
* Call this once at the top of your app entry (e.g. `root.tsx`) and spread the result:
|
|
45
|
+
*
|
|
46
|
+
* ```ts
|
|
47
|
+
* import darkCssUrl from "@ngrok/mantle/mantle-dark.css?url";
|
|
48
|
+
* import darkHighContrastCssUrl from "@ngrok/mantle/mantle-dark-high-contrast.css?url";
|
|
49
|
+
* import lightHighContrastCssUrl from "@ngrok/mantle/mantle-light-high-contrast.css?url";
|
|
50
|
+
*
|
|
51
|
+
* const themeUrls = mantleStyleSheetUrls({ darkCssUrl, lightHighContrastCssUrl, darkHighContrastCssUrl });
|
|
52
|
+
*
|
|
53
|
+
* // In JSX:
|
|
54
|
+
* <MantleStyleSheets {...themeUrls} nonce={nonce} ssrCookie={ssrCookie} />
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
declare function mantleStyleSheetUrls(urls: MantleThemeCssUrls): MantleThemeCssUrls;
|
|
58
|
+
type MantleStyleSheetsProps = MantleThemeCssUrls & {
|
|
7
59
|
/**
|
|
8
60
|
* Force a specific resolved theme's stylesheet to load unconditionally (`media="all"`),
|
|
9
61
|
* regardless of the user's OS preference. Use this when your app is locked to a single
|
|
@@ -14,7 +66,7 @@ type MantleStylesheetsProps = {
|
|
|
14
66
|
*
|
|
15
67
|
* @example
|
|
16
68
|
* // Dark-only app — always load dark CSS eagerly
|
|
17
|
-
* <
|
|
69
|
+
* <MantleStyleSheets forceTheme="dark" {...themeUrls} />
|
|
18
70
|
*/
|
|
19
71
|
forceTheme?: ResolvedTheme;
|
|
20
72
|
/**
|
|
@@ -32,7 +84,7 @@ type MantleStylesheetsProps = {
|
|
|
32
84
|
* }
|
|
33
85
|
*
|
|
34
86
|
* // root.tsx component
|
|
35
|
-
* <
|
|
87
|
+
* <MantleStyleSheets {...themeUrls} ssrCookie={loaderData.ssrCookie} nonce={nonce} />
|
|
36
88
|
* ```
|
|
37
89
|
*/
|
|
38
90
|
ssrCookie?: string;
|
|
@@ -46,37 +98,55 @@ type MantleStylesheetsProps = {
|
|
|
46
98
|
nonce?: string;
|
|
47
99
|
};
|
|
48
100
|
/**
|
|
49
|
-
* Renders
|
|
101
|
+
* Renders `<link rel="stylesheet">` tags for the dark, light-high-contrast, and
|
|
50
102
|
* dark-high-contrast theme CSS files. Each stylesheet is gated behind a `media` attribute
|
|
51
103
|
* matching its OS preference so it is non-render-blocking for users who do not need it.
|
|
52
104
|
*
|
|
105
|
+
* Use {@link mantleStyleSheetUrls} to collect the required CSS URL props from Vite `?url`
|
|
106
|
+
* imports and spread them in:
|
|
107
|
+
*
|
|
108
|
+
* ```ts
|
|
109
|
+
* import darkCssUrl from "@ngrok/mantle/mantle-dark.css?url";
|
|
110
|
+
* import darkHighContrastCssUrl from "@ngrok/mantle/mantle-dark-high-contrast.css?url";
|
|
111
|
+
* import lightHighContrastCssUrl from "@ngrok/mantle/mantle-light-high-contrast.css?url";
|
|
112
|
+
*
|
|
113
|
+
* const themeUrls = mantleStyleSheetUrls({ darkCssUrl, lightHighContrastCssUrl, darkHighContrastCssUrl });
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
53
116
|
* Place this component in `<head>`, after `<PreventWrongThemeFlashScript>`.
|
|
54
117
|
*
|
|
55
118
|
* On the client, a `MutationObserver` watches `html[data-applied-theme]` (kept in sync by
|
|
56
119
|
* `ThemeProvider`) and updates the `media` attributes to `"all"` when the user manually
|
|
57
120
|
* selects a theme that differs from their OS preference, ensuring the correct CSS is applied.
|
|
58
121
|
*
|
|
122
|
+
* When `forceTheme` is set, only the link tag for that theme is rendered — the others are
|
|
123
|
+
* omitted entirely to avoid unnecessary network requests.
|
|
124
|
+
*
|
|
59
125
|
* @example
|
|
60
126
|
* ```tsx
|
|
61
127
|
* // root.tsx
|
|
128
|
+
* import darkCssUrl from "@ngrok/mantle/mantle-dark.css?url";
|
|
129
|
+
* import darkHighContrastCssUrl from "@ngrok/mantle/mantle-dark-high-contrast.css?url";
|
|
130
|
+
* import lightHighContrastCssUrl from "@ngrok/mantle/mantle-light-high-contrast.css?url";
|
|
131
|
+
* import { mantleStyleSheetUrls, MantleStyleSheets, PreventWrongThemeFlashScript } from "@ngrok/mantle/theme";
|
|
132
|
+
*
|
|
133
|
+
* const themeUrls = mantleStyleSheetUrls({ darkCssUrl, lightHighContrastCssUrl, darkHighContrastCssUrl });
|
|
134
|
+
*
|
|
62
135
|
* <head>
|
|
63
136
|
* <PreventWrongThemeFlashScript nonce={nonce} />
|
|
64
|
-
* <
|
|
137
|
+
* <MantleStyleSheets {...themeUrls} nonce={nonce} ssrCookie={loaderData?.ssrCookie} />
|
|
65
138
|
* </head>
|
|
66
139
|
* ```
|
|
67
|
-
*
|
|
68
|
-
* @example
|
|
69
|
-
* ```tsx
|
|
70
|
-
* // Dark-only app
|
|
71
|
-
* <MantleStylesheets forceTheme="dark" />
|
|
72
|
-
* ```
|
|
73
140
|
*/
|
|
74
|
-
declare function
|
|
141
|
+
declare function MantleStyleSheets({
|
|
142
|
+
darkCssUrl,
|
|
143
|
+
lightHighContrastCssUrl,
|
|
144
|
+
darkHighContrastCssUrl,
|
|
75
145
|
forceTheme,
|
|
76
146
|
nonce,
|
|
77
147
|
ssrCookie
|
|
78
|
-
}:
|
|
79
|
-
declare namespace
|
|
148
|
+
}: MantleStyleSheetsProps): react_jsx_runtime0.JSX.Element;
|
|
149
|
+
declare namespace MantleStyleSheets {
|
|
80
150
|
var displayName: string;
|
|
81
151
|
}
|
|
82
152
|
//#endregion
|
|
@@ -115,7 +185,7 @@ declare function useTheme(): ThemeProviderState;
|
|
|
115
185
|
*/
|
|
116
186
|
declare function readThemeFromHtmlElement(): {
|
|
117
187
|
appliedTheme: "dark" | "light" | "light-high-contrast" | "dark-high-contrast" | undefined;
|
|
118
|
-
theme: "dark" | "light" | "light-high-contrast" | "dark-high-contrast" |
|
|
188
|
+
theme: "dark" | "light" | "system" | "light-high-contrast" | "dark-high-contrast" | undefined;
|
|
119
189
|
};
|
|
120
190
|
/**
|
|
121
191
|
* If the theme is "system", it will resolve the theme based on the user's media query preferences, otherwise it will return the theme as is.
|
|
@@ -353,5 +423,5 @@ declare const PreloadFont: {
|
|
|
353
423
|
displayName: string;
|
|
354
424
|
};
|
|
355
425
|
//#endregion
|
|
356
|
-
export { $resolvedTheme, $theme, type CoreFontName,
|
|
426
|
+
export { $resolvedTheme, $theme, type CoreFontName, MantleStyleSheets, type MantleStyleSheetsProps, type MantleThemeCssUrls, PreloadFont, PreventWrongThemeFlashScript, type ResolvedTheme, type Theme, ThemeProvider, assetsCdnOrigin, extractThemeCookie, fontHref, getStoredTheme, isResolvedTheme, isTheme, mantleStyleSheetUrls, preloadFontLink, preventWrongThemeFlashScriptContent, readThemeFromHtmlElement, resolvedThemes, themes, useAppliedTheme, useInitialHtmlThemeProps, useTheme };
|
|
357
427
|
//# sourceMappingURL=theme.d.ts.map
|
package/dist/theme.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as e,c as t,d as n,f as r,h as i,i as a,l as o,m as s,n as c,o as l,p as u,r as d,s as f,t as p,u as m}from"./theme-provider-8xCwja4v.js";import{useEffect as h}from"react";import{Fragment as g,jsx as _,jsxs as v}from"react/jsx-runtime";
|
|
1
|
+
import{a as e,c as t,d as n,f as r,h as i,i as a,l as o,m as s,n as c,o as l,p as u,r as d,s as f,t as p,u as m}from"./theme-provider-8xCwja4v.js";import{useEffect as h}from"react";import{Fragment as g,jsx as _,jsxs as v}from"react/jsx-runtime";const y=`mantle-dark-styles`,b=`mantle-light-high-contrast-styles`,x=`mantle-dark-high-contrast-styles`,S=`(prefers-color-scheme: dark)`,C=`(prefers-contrast: more) and (prefers-color-scheme: light)`,w=`(prefers-contrast: more) and (prefers-color-scheme: dark)`;function T(e,t){let n=t??e;return{dark:n===`dark`?`all`:S,lightHighContrast:n===`light-high-contrast`?`all`:C,darkHighContrast:n===`dark-high-contrast`?`all`:w}}function E(e){return e}function D(e){let{darkLinkId:t,lightHcLinkId:n,darkHcLinkId:r,mediaDark:i,mediaLightHc:a,mediaDarkHc:o,forceTheme:s}=e,c=document.documentElement.dataset.appliedTheme,l=s??c,u=document.getElementById(t),d=document.getElementById(n),f=document.getElementById(r);u&&(u.media=l===`dark`?`all`:i),d&&(d.media=l===`light-high-contrast`?`all`:a),f&&(f.media=l===`dark-high-contrast`?`all`:o)}function O(e){let t={darkLinkId:y,lightHcLinkId:b,darkHcLinkId:x,mediaDark:S,mediaLightHc:C,mediaDarkHc:w,forceTheme:e};return`(${D.toString()})(${JSON.stringify(t)})`}function k({darkCssUrl:e,lightHighContrastCssUrl:t,darkHighContrastCssUrl:n,forceTheme:i,nonce:o,ssrCookie:s}){h(()=>{function e(){let e=document.documentElement.dataset.appliedTheme;return r(e)?e:void 0}function t(){let{dark:t,lightHighContrast:n,darkHighContrast:r}=T(e(),i),a=document.getElementById(y),o=document.getElementById(b),s=document.getElementById(x);a&&(a.media=t),o&&(o.media=n),s&&(s.media=r)}t();let n=new MutationObserver(t);return n.observe(document.documentElement,{attributes:!0,attributeFilter:[`data-applied-theme`]}),()=>{n.disconnect()}},[i]);let c=s==null?void 0:a({cookie:s}),l=c===`system`?void 0:c,{dark:u,lightHighContrast:d,darkHighContrast:f}=T(l,i);return v(g,{children:[(!i||i===`dark`)&&_(`link`,{rel:`stylesheet`,id:y,href:e,media:u,suppressHydrationWarning:!0}),(!i||i===`light-high-contrast`)&&_(`link`,{rel:`stylesheet`,id:b,href:t,media:d,suppressHydrationWarning:!0}),(!i||i===`dark-high-contrast`)&&_(`link`,{rel:`stylesheet`,id:x,href:n,media:f,suppressHydrationWarning:!0}),!i&&l==null&&_(`script`,{dangerouslySetInnerHTML:{__html:O(i)},nonce:o,suppressHydrationWarning:!0})]})}k.displayName=`MantleStyleSheets`;const A=`https://assets.ngrok.com`,j=`${A}/fonts`,M={roobert:`/roobert/roobert-proportional-vf.woff2`,"jetbrains-mono":`/jetbrains/jetbrainsmono-wght.woff2`,"jetbrains-mono-italic":`/jetbrains/jetbrainsmono-italic-wght.woff2`,"family-regular":`/family/family-regular.woff2`,"family-italic":`/family/family-italic.woff2`};function N(e){return`${j}${e.startsWith(`/`)?e:`/${e}`}`}function P(e){return`<${N(M[e])}>; rel=preload; as=font; type="font/woff2"; crossorigin`}const F=({name:e})=>_(`link`,{rel:`preload`,href:N(M[e]),as:`font`,type:`font/woff2`,crossOrigin:`anonymous`});F.displayName=`PreloadFont`;export{m as $resolvedTheme,n as $theme,k as MantleStyleSheets,F as PreloadFont,p as PreventWrongThemeFlashScript,c as ThemeProvider,A as assetsCdnOrigin,d as extractThemeCookie,N as fontHref,a as getStoredTheme,r as isResolvedTheme,u as isTheme,E as mantleStyleSheetUrls,P as preloadFontLink,e as preventWrongThemeFlashScriptContent,l as readThemeFromHtmlElement,s as resolvedThemes,i as themes,f as useAppliedTheme,t as useInitialHtmlThemeProps,o as useTheme};
|
|
2
2
|
//# sourceMappingURL=theme.js.map
|
package/dist/theme.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.js","names":[],"sources":["../src/components/theme/mantle-stylesheets.tsx","../src/components/theme/fonts.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect } from \"react\";\n// ?url imports are resolved by Vite to a browser-accessible URL in both client and SSR\n// builds. The consuming app's Vite bundler handles the transformation; tsdown externalizes\n// these imports so they pass through to the consumer unchanged.\nimport darkCssUrl from \"../../mantle-dark.css?url\";\nimport darkHcCssUrl from \"../../mantle-dark-high-contrast.css?url\";\nimport lightHcCssUrl from \"../../mantle-light-high-contrast.css?url\";\nimport { getStoredTheme } from \"./theme-provider.js\";\nimport type { ResolvedTheme } from \"./themes.js\";\nimport { isResolvedTheme } from \"./themes.js\";\n\n/**\n * Stable IDs for the three lazy-loaded theme `<link>` elements.\n * Used to locate them in the DOM for media attribute updates.\n */\nconst DARK_LINK_ID = \"mantle-dark-styles\";\nconst LIGHT_HIGH_CONTRAST_LINK_ID = \"mantle-light-high-contrast-styles\";\nconst DARK_HIGH_CONTRAST_LINK_ID = \"mantle-dark-high-contrast-styles\";\n\n/**\n * Default `media` attribute values for each lazy-loaded stylesheet.\n * Each one matches only the OS preference for that theme, making them\n * non-render-blocking for users whose OS does not match.\n */\nconst MEDIA_DARK = \"(prefers-color-scheme: dark)\";\nconst MEDIA_LIGHT_HC = \"(prefers-contrast: more) and (prefers-color-scheme: light)\";\nconst MEDIA_DARK_HC = \"(prefers-contrast: more) and (prefers-color-scheme: dark)\";\n\ntype MediaValues = {\n\tdark: string;\n\tlightHighContrast: string;\n\tdarkHighContrast: string;\n};\n\n/**\n * Compute the `media` attribute value for each stylesheet given the active theme.\n * When a theme is active (either from the resolved applied theme or a forced override),\n * its stylesheet's `media` is set to `\"all\"` so the CSS is applied regardless of OS preference.\n */\nfunction computeMediaValues(\n\tappliedTheme: ResolvedTheme | undefined,\n\tforceTheme: ResolvedTheme | undefined,\n): MediaValues {\n\tconst theme = forceTheme ?? appliedTheme;\n\treturn {\n\t\tdark: theme === \"dark\" ? \"all\" : MEDIA_DARK,\n\t\tlightHighContrast: theme === \"light-high-contrast\" ? \"all\" : MEDIA_LIGHT_HC,\n\t\tdarkHighContrast: theme === \"dark-high-contrast\" ? \"all\" : MEDIA_DARK_HC,\n\t};\n}\n\nexport type MantleStylesheetsProps = {\n\t/**\n\t * Force a specific resolved theme's stylesheet to load unconditionally (`media=\"all\"`),\n\t * regardless of the user's OS preference. Use this when your app is locked to a single\n\t * theme (e.g. a dark-only page) so the required CSS is render-blocking as intended.\n\t *\n\t * When omitted, each stylesheet uses its OS media query and becomes non-render-blocking\n\t * for users whose OS preference does not match.\n\t *\n\t * @example\n\t * // Dark-only app — always load dark CSS eagerly\n\t * <MantleStylesheets forceTheme=\"dark\" />\n\t */\n\tforceTheme?: ResolvedTheme;\n\t/**\n\t * The theme cookie string from the incoming HTTP request (e.g. `request.headers.get(\"Cookie\")`\n\t * or the pre-extracted value from {@link extractThemeCookie}). When provided, the server can\n\t * resolve the stored theme and render the correct `media` attribute directly in the SSR HTML,\n\t * eliminating the need for the inline fix script in cases where the user has a non-system\n\t * theme stored in their cookie.\n\t *\n\t * @example\n\t * ```tsx\n\t * // root.tsx loader\n\t * export async function loader({ request }: Route.LoaderArgs) {\n\t * return { ssrCookie: extractThemeCookie(request.headers.get(\"Cookie\")) };\n\t * }\n\t *\n\t * // root.tsx component\n\t * <MantleStylesheets ssrCookie={loaderData.ssrCookie} nonce={nonce} />\n\t * ```\n\t */\n\tssrCookie?: string;\n\t/**\n\t * An optional CSP nonce to allowlist the inline script that fixes `media` attributes\n\t * synchronously after the `<link>` tags are parsed. Mirror the same nonce you pass\n\t * to {@link PreventWrongThemeFlashScript}.\n\t *\n\t * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce\n\t */\n\tnonce?: string;\n};\n\n/**\n * Inline script that runs synchronously after the `<link>` tags are parsed to fix their\n * `media` attributes based on the applied theme already written to `html[data-applied-theme]`\n * by `PreventWrongThemeFlashScript`. This eliminates FOUC for users who have manually\n * selected a theme that differs from their OS preference.\n */\nfunction fixMediaAttributes(args: {\n\tdarkLinkId: string;\n\tlightHcLinkId: string;\n\tdarkHcLinkId: string;\n\tmediaDark: string;\n\tmediaLightHc: string;\n\tmediaDarkHc: string;\n\tforceTheme: ResolvedTheme | undefined;\n}) {\n\tconst {\n\t\tdarkLinkId,\n\t\tlightHcLinkId,\n\t\tdarkHcLinkId,\n\t\tmediaDark,\n\t\tmediaLightHc,\n\t\tmediaDarkHc,\n\t\tforceTheme,\n\t} = args;\n\tconst appliedTheme = document.documentElement.dataset.appliedTheme;\n\tconst theme = forceTheme ?? appliedTheme;\n\n\tconst darkLink = document.getElementById(darkLinkId) as HTMLLinkElement | null;\n\tconst lightHcLink = document.getElementById(lightHcLinkId) as HTMLLinkElement | null;\n\tconst darkHcLink = document.getElementById(darkHcLinkId) as HTMLLinkElement | null;\n\n\tif (darkLink) {\n\t\tdarkLink.media = theme === \"dark\" ? \"all\" : mediaDark;\n\t}\n\tif (lightHcLink) {\n\t\tlightHcLink.media = theme === \"light-high-contrast\" ? \"all\" : mediaLightHc;\n\t}\n\tif (darkHcLink) {\n\t\tdarkHcLink.media = theme === \"dark-high-contrast\" ? \"all\" : mediaDarkHc;\n\t}\n}\n\nfunction fixMediaScriptContent(forceTheme: ResolvedTheme | undefined): string {\n\tconst args = {\n\t\tdarkLinkId: DARK_LINK_ID,\n\t\tlightHcLinkId: LIGHT_HIGH_CONTRAST_LINK_ID,\n\t\tdarkHcLinkId: DARK_HIGH_CONTRAST_LINK_ID,\n\t\tmediaDark: MEDIA_DARK,\n\t\tmediaLightHc: MEDIA_LIGHT_HC,\n\t\tmediaDarkHc: MEDIA_DARK_HC,\n\t\tforceTheme,\n\t} satisfies Parameters<typeof fixMediaAttributes>[0];\n\treturn `(${fixMediaAttributes.toString()})(${JSON.stringify(args)})`;\n}\n\n/**\n * Renders three `<link rel=\"stylesheet\">` tags for the dark, light-high-contrast, and\n * dark-high-contrast theme CSS files. Each stylesheet is gated behind a `media` attribute\n * matching its OS preference so it is non-render-blocking for users who do not need it.\n *\n * Place this component in `<head>`, after `<PreventWrongThemeFlashScript>`.\n *\n * On the client, a `MutationObserver` watches `html[data-applied-theme]` (kept in sync by\n * `ThemeProvider`) and updates the `media` attributes to `\"all\"` when the user manually\n * selects a theme that differs from their OS preference, ensuring the correct CSS is applied.\n *\n * @example\n * ```tsx\n * // root.tsx\n * <head>\n * <PreventWrongThemeFlashScript nonce={nonce} />\n * <MantleStylesheets />\n * </head>\n * ```\n *\n * @example\n * ```tsx\n * // Dark-only app\n * <MantleStylesheets forceTheme=\"dark\" />\n * ```\n */\nfunction MantleStylesheets({ forceTheme, nonce, ssrCookie }: MantleStylesheetsProps) {\n\tuseEffect(() => {\n\t\tfunction getAppliedTheme(): ResolvedTheme | undefined {\n\t\t\tconst value = document.documentElement.dataset.appliedTheme;\n\t\t\treturn isResolvedTheme(value) ? value : undefined;\n\t\t}\n\n\t\tfunction updateMediaAttributes() {\n\t\t\tconst { dark, lightHighContrast, darkHighContrast } = computeMediaValues(\n\t\t\t\tgetAppliedTheme(),\n\t\t\t\tforceTheme,\n\t\t\t);\n\n\t\t\tconst darkLink = document.getElementById(DARK_LINK_ID) as HTMLLinkElement | null;\n\t\t\tconst lightHighContrastLink = document.getElementById(\n\t\t\t\tLIGHT_HIGH_CONTRAST_LINK_ID,\n\t\t\t) as HTMLLinkElement | null;\n\t\t\tconst darkHighContrastLink = document.getElementById(\n\t\t\t\tDARK_HIGH_CONTRAST_LINK_ID,\n\t\t\t) as HTMLLinkElement | null;\n\n\t\t\tif (darkLink) {\n\t\t\t\tdarkLink.media = dark;\n\t\t\t}\n\t\t\tif (lightHighContrastLink) {\n\t\t\t\tlightHighContrastLink.media = lightHighContrast;\n\t\t\t}\n\t\t\tif (darkHighContrastLink) {\n\t\t\t\tdarkHighContrastLink.media = darkHighContrast;\n\t\t\t}\n\t\t}\n\n\t\t// Sync immediately on mount in case the applied theme diverges from the SSR-rendered media values\n\t\tupdateMediaAttributes();\n\n\t\t// Watch for theme changes driven by ThemeProvider\n\t\tconst observer = new MutationObserver(updateMediaAttributes);\n\t\tobserver.observe(document.documentElement, {\n\t\t\tattributes: true,\n\t\t\tattributeFilter: [\"data-applied-theme\"],\n\t\t});\n\n\t\treturn () => {\n\t\t\tobserver.disconnect();\n\t\t};\n\t}, [forceTheme]);\n\n\t// On SSR (and as the initial React render), emit the link tags with media values\n\t// derived from the cookie-stored theme (if available) and forceTheme.\n\t// The useEffect above will correct them on the client before the user can interact.\n\tconst ssrStoredTheme = ssrCookie != null ? getStoredTheme({ cookie: ssrCookie }) : undefined;\n\tconst ssrAppliedTheme = ssrStoredTheme !== \"system\" ? ssrStoredTheme : undefined;\n\tconst { dark, lightHighContrast, darkHighContrast } = computeMediaValues(\n\t\tssrAppliedTheme,\n\t\tforceTheme,\n\t);\n\n\t// The inline fix script corrects media attributes for users whose stored theme differs from\n\t// their OS preference. It is only needed when the SSR HTML may have been rendered with\n\t// incorrect media values — i.e. when neither ssrCookie (with a non-system theme) nor\n\t// forceTheme provide a deterministic answer at render time.\n\tconst needsFixScript = forceTheme == null && ssrAppliedTheme == null;\n\n\treturn (\n\t\t<>\n\t\t\t<link\n\t\t\t\trel=\"stylesheet\"\n\t\t\t\tid={DARK_LINK_ID}\n\t\t\t\thref={darkCssUrl}\n\t\t\t\tmedia={dark}\n\t\t\t\tsuppressHydrationWarning\n\t\t\t/>\n\t\t\t<link\n\t\t\t\trel=\"stylesheet\"\n\t\t\t\tid={LIGHT_HIGH_CONTRAST_LINK_ID}\n\t\t\t\thref={lightHcCssUrl}\n\t\t\t\tmedia={lightHighContrast}\n\t\t\t\tsuppressHydrationWarning\n\t\t\t/>\n\t\t\t<link\n\t\t\t\trel=\"stylesheet\"\n\t\t\t\tid={DARK_HIGH_CONTRAST_LINK_ID}\n\t\t\t\thref={darkHcCssUrl}\n\t\t\t\tmedia={darkHighContrast}\n\t\t\t\tsuppressHydrationWarning\n\t\t\t/>\n\t\t\t{needsFixScript && (\n\t\t\t\t<script\n\t\t\t\t\tdangerouslySetInnerHTML={{ __html: fixMediaScriptContent(forceTheme) }}\n\t\t\t\t\tnonce={nonce}\n\t\t\t\t\tsuppressHydrationWarning\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t);\n}\nMantleStylesheets.displayName = \"MantleStylesheets\";\n\nexport { MantleStylesheets };\n","/**\n * @fileoverview Helpers for preloading ngrok brand fonts from the CDN.\n * All font URLs resolve to `${assetsCdnOrigin}/fonts`.\n */\n\n/**\n * The origin for the assets CDN where custom ngrok fonts and assets are hosted.\n *\n * Keep this stable across the app so we can preconnect/DNS-prefetch consistently.\n * @public\n */\nconst assetsCdnOrigin = \"https://assets.ngrok.com\";\n\n/**\n * Base path for font assets on the CDN.\n * @internal\n */\nconst cdnBase = `${assetsCdnOrigin}/fonts`;\n\nconst coreFontNames = [\n\t\"roobert\",\n\t\"jetbrains-mono\",\n\t\"jetbrains-mono-italic\",\n\t\"family-regular\",\n\t\"family-italic\",\n] as const;\n/**\n * Named keys identifying each individual core font.\n * @public\n */\ntype CoreFontName = (typeof coreFontNames)[number];\n\n/**\n * Maps each {@link CoreFontName} to its CDN font path (relative to the fonts base).\n * @internal\n */\nconst coreFontPathByName = {\n\troobert: \"/roobert/roobert-proportional-vf.woff2\",\n\t\"jetbrains-mono\": \"/jetbrains/jetbrainsmono-wght.woff2\",\n\t\"jetbrains-mono-italic\": \"/jetbrains/jetbrainsmono-italic-wght.woff2\",\n\t\"family-regular\": \"/family/family-regular.woff2\",\n\t\"family-italic\": \"/family/family-italic.woff2\",\n} as const satisfies Record<CoreFontName, `/${string}`>;\n\ntype FontPath = `/${string}` | (string & {});\n\n/**\n * Builds an absolute CDN URL for a given font.\n *\n * @returns {`https://assets.ngrok.com/fonts${T}`} An absolute, literal-typed CDN URL.\n *\n * @example\n * const href = fontHref(\"/roobert/roobert-proportional-vf.woff2\");\n * // -> \"https://assets.ngrok.com/fonts/roobert/roobert-proportional-vf.woff2\"\n */\nfunction fontHref<T extends FontPath = FontPath>(font: T) {\n\tconst path = font.startsWith(\"/\") ? font : `/${font}`;\n\treturn `${cdnBase}${path}` as const;\n}\n\n/**\n * Props for {@link PreloadFont}.\n * @public\n */\ntype PreloadFontProps = {\n\t/**\n\t * The name of the individual core font to preload.\n\t *\n\t * - `\"roobert\"` — Roobert proportional variable font\n\t * - `\"jetbrains-mono\"` — JetBrains Mono variable weight\n\t * - `\"jetbrains-mono-italic\"` — JetBrains Mono italic variable weight\n\t * - `\"family-regular\"` — Family regular\n\t * - `\"family-italic\"` — Family italic\n\t */\n\tname: CoreFontName;\n};\n\n/**\n * Returns an HTTP `Link` header value that preloads a single core font by name.\n *\n * Identical in intent to {@link PreloadFont}, but for server-side use where\n * you want to send the preload hint as an HTTP header instead of (or in\n * addition to) an HTML `<link>` element. Sending this as a `Link` header lets\n * the browser start the font fetch before it has parsed any HTML.\n *\n * @remarks\n * For best performance, also send a `preconnect` hint to {@link assetsCdnOrigin}\n * in the same `Link` header.\n *\n * @example\n * ```ts\n * // In an HTTP handler / server entry:\n * headers.append(\"Link\", preloadFontLink(\"roobert\"));\n * headers.append(\"Link\", preloadFontLink(\"jetbrains-mono\"));\n *\n * // Or as a single combined header:\n * headers.set(\"Link\", [\n * `<${assetsCdnOrigin}>; rel=preconnect; crossorigin`,\n * preloadFontLink(\"roobert\"),\n * ].join(\", \"));\n * ```\n */\nfunction preloadFontLink(name: CoreFontName): string {\n\tconst href = fontHref(coreFontPathByName[name]);\n\treturn `<${href}>; rel=preload; as=font; type=\"font/woff2\"; crossorigin`;\n}\n\n/**\n * Preloads a single core font by name.\n *\n * Use this when you only need one or two specific fonts rather than all core\n * fonts. Include it as early as possible in the document `<head>`.\n *\n * @remarks\n * For best performance, pair this with preconnect/dns-prefetch hints to the CDN.\n *\n * @example\n * ```tsx\n * <head>\n * <link rel=\"preconnect\" href={assetsCdnOrigin} crossOrigin=\"anonymous\" />\n * <link rel=\"dns-prefetch\" href={assetsCdnOrigin} />\n * <PreloadFont name=\"roobert\" />\n * <PreloadFont name=\"jetbrains-mono\" />\n * </head>\n * ```\n */\nconst PreloadFont = ({ name }: PreloadFontProps) => (\n\t<link\n\t\trel=\"preload\"\n\t\thref={fontHref(coreFontPathByName[name])}\n\t\tas=\"font\"\n\t\ttype=\"font/woff2\"\n\t\tcrossOrigin=\"anonymous\"\n\t/>\n);\nPreloadFont.displayName = \"PreloadFont\";\n\nexport type { CoreFontName };\n\nexport {\n\t//,\n\tassetsCdnOrigin,\n\tfontHref,\n\tpreloadFontLink,\n\tPreloadFont,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"theme.js","names":[],"sources":["../src/components/theme/mantle-style-sheets.tsx","../src/components/theme/fonts.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect } from \"react\";\nimport { getStoredTheme } from \"./theme-provider.js\";\nimport type { ResolvedTheme } from \"./themes.js\";\nimport { isResolvedTheme } from \"./themes.js\";\n\n/**\n * Stable IDs for the three lazy-loaded theme `<link>` elements.\n * Used to locate them in the DOM for media attribute updates.\n */\nconst DARK_LINK_ID = \"mantle-dark-styles\";\nconst LIGHT_HIGH_CONTRAST_LINK_ID = \"mantle-light-high-contrast-styles\";\nconst DARK_HIGH_CONTRAST_LINK_ID = \"mantle-dark-high-contrast-styles\";\n\n/**\n * Default `media` attribute values for each lazy-loaded stylesheet.\n * Each one matches only the OS preference for that theme, making them\n * non-render-blocking for users whose OS does not match.\n */\nconst MEDIA_DARK = \"(prefers-color-scheme: dark)\";\nconst MEDIA_LIGHT_HC = \"(prefers-contrast: more) and (prefers-color-scheme: light)\";\nconst MEDIA_DARK_HC = \"(prefers-contrast: more) and (prefers-color-scheme: dark)\";\n\ntype MediaValues = {\n\tdark: string;\n\tlightHighContrast: string;\n\tdarkHighContrast: string;\n};\n\n/**\n * Compute the `media` attribute value for each stylesheet given the active theme.\n * When a theme is active (either from the resolved applied theme or a forced override),\n * its stylesheet's `media` is set to `\"all\"` so the CSS is applied regardless of OS preference.\n */\nfunction computeMediaValues(\n\tappliedTheme: ResolvedTheme | undefined,\n\tforceTheme: ResolvedTheme | undefined,\n): MediaValues {\n\tconst theme = forceTheme ?? appliedTheme;\n\treturn {\n\t\tdark: theme === \"dark\" ? \"all\" : MEDIA_DARK,\n\t\tlightHighContrast: theme === \"light-high-contrast\" ? \"all\" : MEDIA_LIGHT_HC,\n\t\tdarkHighContrast: theme === \"dark-high-contrast\" ? \"all\" : MEDIA_DARK_HC,\n\t};\n}\n\n/**\n * Browser-accessible URLs for mantle's three lazy-loaded theme stylesheets.\n *\n * Use {@link mantleStyleSheetUrls} to create this object from Vite `?url` imports.\n */\nexport type MantleThemeCssUrls = {\n\t/**\n\t * Browser-accessible URL for `mantle-dark.css`.\n\t * @example\n\t * ```tsx\n\t * // in vite app\n\t * import darkCssUrl from \"@ngrok/mantle/mantle-dark.css?url\"\n\t * ```\n\t */\n\tdarkCssUrl: string;\n\t/**\n\t * Browser-accessible URL for `mantle-light-high-contrast.css`.\n\t * @example\n\t * ```tsx\n\t * // in vite app\n\t * import lightHighContrastCssUrl from \"@ngrok/mantle/mantle-light-high-contrast.css?url\"\n\t * ```\n\t */\n\tlightHighContrastCssUrl: string;\n\t/**\n\t * Browser-accessible URL for `mantle-dark-high-contrast.css`.\n\t * @example\n\t * ```tsx\n\t * // in vite app\n\t * import darkHighContrastCssUrl from \"@ngrok/mantle/mantle-dark-high-contrast.css?url\"\n\t * ```\n\t */\n\tdarkHighContrastCssUrl: string;\n};\n\n/**\n * Collects the three Vite `?url` imports for mantle's theme stylesheets into a typed object\n * that can be spread directly into `<MantleStyleSheets>`.\n *\n * Call this once at the top of your app entry (e.g. `root.tsx`) and spread the result:\n *\n * ```ts\n * import darkCssUrl from \"@ngrok/mantle/mantle-dark.css?url\";\n * import darkHighContrastCssUrl from \"@ngrok/mantle/mantle-dark-high-contrast.css?url\";\n * import lightHighContrastCssUrl from \"@ngrok/mantle/mantle-light-high-contrast.css?url\";\n *\n * const themeUrls = mantleStyleSheetUrls({ darkCssUrl, lightHighContrastCssUrl, darkHighContrastCssUrl });\n *\n * // In JSX:\n * <MantleStyleSheets {...themeUrls} nonce={nonce} ssrCookie={ssrCookie} />\n * ```\n */\nfunction mantleStyleSheetUrls(urls: MantleThemeCssUrls): MantleThemeCssUrls {\n\treturn urls;\n}\n\nexport type MantleStyleSheetsProps = MantleThemeCssUrls & {\n\t/**\n\t * Force a specific resolved theme's stylesheet to load unconditionally (`media=\"all\"`),\n\t * regardless of the user's OS preference. Use this when your app is locked to a single\n\t * theme (e.g. a dark-only page) so the required CSS is render-blocking as intended.\n\t *\n\t * When omitted, each stylesheet uses its OS media query and becomes non-render-blocking\n\t * for users whose OS preference does not match.\n\t *\n\t * @example\n\t * // Dark-only app — always load dark CSS eagerly\n\t * <MantleStyleSheets forceTheme=\"dark\" {...themeUrls} />\n\t */\n\tforceTheme?: ResolvedTheme;\n\t/**\n\t * The theme cookie string from the incoming HTTP request (e.g. `request.headers.get(\"Cookie\")`\n\t * or the pre-extracted value from {@link extractThemeCookie}). When provided, the server can\n\t * resolve the stored theme and render the correct `media` attribute directly in the SSR HTML,\n\t * eliminating the need for the inline fix script in cases where the user has a non-system\n\t * theme stored in their cookie.\n\t *\n\t * @example\n\t * ```tsx\n\t * // root.tsx loader\n\t * export async function loader({ request }: Route.LoaderArgs) {\n\t * return { ssrCookie: extractThemeCookie(request.headers.get(\"Cookie\")) };\n\t * }\n\t *\n\t * // root.tsx component\n\t * <MantleStyleSheets {...themeUrls} ssrCookie={loaderData.ssrCookie} nonce={nonce} />\n\t * ```\n\t */\n\tssrCookie?: string;\n\t/**\n\t * An optional CSP nonce to allowlist the inline script that fixes `media` attributes\n\t * synchronously after the `<link>` tags are parsed. Mirror the same nonce you pass\n\t * to {@link PreventWrongThemeFlashScript}.\n\t *\n\t * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce\n\t */\n\tnonce?: string;\n};\n\n/**\n * Inline script that runs synchronously after the `<link>` tags are parsed to fix their\n * `media` attributes based on the applied theme already written to `html[data-applied-theme]`\n * by `PreventWrongThemeFlashScript`. This eliminates FOUC for users who have manually\n * selected a theme that differs from their OS preference.\n */\nfunction fixMediaAttributes(args: {\n\tdarkLinkId: string;\n\tlightHcLinkId: string;\n\tdarkHcLinkId: string;\n\tmediaDark: string;\n\tmediaLightHc: string;\n\tmediaDarkHc: string;\n\tforceTheme: ResolvedTheme | undefined;\n}) {\n\tconst {\n\t\tdarkLinkId,\n\t\tlightHcLinkId,\n\t\tdarkHcLinkId,\n\t\tmediaDark,\n\t\tmediaLightHc,\n\t\tmediaDarkHc,\n\t\tforceTheme,\n\t} = args;\n\tconst appliedTheme = document.documentElement.dataset.appliedTheme;\n\tconst theme = forceTheme ?? appliedTheme;\n\n\tconst darkLink = document.getElementById(darkLinkId) as HTMLLinkElement | null;\n\tconst lightHcLink = document.getElementById(lightHcLinkId) as HTMLLinkElement | null;\n\tconst darkHcLink = document.getElementById(darkHcLinkId) as HTMLLinkElement | null;\n\n\tif (darkLink) {\n\t\tdarkLink.media = theme === \"dark\" ? \"all\" : mediaDark;\n\t}\n\tif (lightHcLink) {\n\t\tlightHcLink.media = theme === \"light-high-contrast\" ? \"all\" : mediaLightHc;\n\t}\n\tif (darkHcLink) {\n\t\tdarkHcLink.media = theme === \"dark-high-contrast\" ? \"all\" : mediaDarkHc;\n\t}\n}\n\nfunction fixMediaScriptContent(forceTheme: ResolvedTheme | undefined): string {\n\tconst args = {\n\t\tdarkLinkId: DARK_LINK_ID,\n\t\tlightHcLinkId: LIGHT_HIGH_CONTRAST_LINK_ID,\n\t\tdarkHcLinkId: DARK_HIGH_CONTRAST_LINK_ID,\n\t\tmediaDark: MEDIA_DARK,\n\t\tmediaLightHc: MEDIA_LIGHT_HC,\n\t\tmediaDarkHc: MEDIA_DARK_HC,\n\t\tforceTheme,\n\t} satisfies Parameters<typeof fixMediaAttributes>[0];\n\treturn `(${fixMediaAttributes.toString()})(${JSON.stringify(args)})`;\n}\n\n/**\n * Renders `<link rel=\"stylesheet\">` tags for the dark, light-high-contrast, and\n * dark-high-contrast theme CSS files. Each stylesheet is gated behind a `media` attribute\n * matching its OS preference so it is non-render-blocking for users who do not need it.\n *\n * Use {@link mantleStyleSheetUrls} to collect the required CSS URL props from Vite `?url`\n * imports and spread them in:\n *\n * ```ts\n * import darkCssUrl from \"@ngrok/mantle/mantle-dark.css?url\";\n * import darkHighContrastCssUrl from \"@ngrok/mantle/mantle-dark-high-contrast.css?url\";\n * import lightHighContrastCssUrl from \"@ngrok/mantle/mantle-light-high-contrast.css?url\";\n *\n * const themeUrls = mantleStyleSheetUrls({ darkCssUrl, lightHighContrastCssUrl, darkHighContrastCssUrl });\n * ```\n *\n * Place this component in `<head>`, after `<PreventWrongThemeFlashScript>`.\n *\n * On the client, a `MutationObserver` watches `html[data-applied-theme]` (kept in sync by\n * `ThemeProvider`) and updates the `media` attributes to `\"all\"` when the user manually\n * selects a theme that differs from their OS preference, ensuring the correct CSS is applied.\n *\n * When `forceTheme` is set, only the link tag for that theme is rendered — the others are\n * omitted entirely to avoid unnecessary network requests.\n *\n * @example\n * ```tsx\n * // root.tsx\n * import darkCssUrl from \"@ngrok/mantle/mantle-dark.css?url\";\n * import darkHighContrastCssUrl from \"@ngrok/mantle/mantle-dark-high-contrast.css?url\";\n * import lightHighContrastCssUrl from \"@ngrok/mantle/mantle-light-high-contrast.css?url\";\n * import { mantleStyleSheetUrls, MantleStyleSheets, PreventWrongThemeFlashScript } from \"@ngrok/mantle/theme\";\n *\n * const themeUrls = mantleStyleSheetUrls({ darkCssUrl, lightHighContrastCssUrl, darkHighContrastCssUrl });\n *\n * <head>\n * <PreventWrongThemeFlashScript nonce={nonce} />\n * <MantleStyleSheets {...themeUrls} nonce={nonce} ssrCookie={loaderData?.ssrCookie} />\n * </head>\n * ```\n */\nfunction MantleStyleSheets({\n\tdarkCssUrl,\n\tlightHighContrastCssUrl,\n\tdarkHighContrastCssUrl,\n\tforceTheme,\n\tnonce,\n\tssrCookie,\n}: MantleStyleSheetsProps) {\n\tuseEffect(() => {\n\t\tfunction getAppliedTheme(): ResolvedTheme | undefined {\n\t\t\tconst value = document.documentElement.dataset.appliedTheme;\n\t\t\treturn isResolvedTheme(value) ? value : undefined;\n\t\t}\n\n\t\tfunction updateMediaAttributes() {\n\t\t\tconst { dark, lightHighContrast, darkHighContrast } = computeMediaValues(\n\t\t\t\tgetAppliedTheme(),\n\t\t\t\tforceTheme,\n\t\t\t);\n\n\t\t\tconst darkLink = document.getElementById(DARK_LINK_ID) as HTMLLinkElement | null;\n\t\t\tconst lightHighContrastLink = document.getElementById(\n\t\t\t\tLIGHT_HIGH_CONTRAST_LINK_ID,\n\t\t\t) as HTMLLinkElement | null;\n\t\t\tconst darkHighContrastLink = document.getElementById(\n\t\t\t\tDARK_HIGH_CONTRAST_LINK_ID,\n\t\t\t) as HTMLLinkElement | null;\n\n\t\t\tif (darkLink) {\n\t\t\t\tdarkLink.media = dark;\n\t\t\t}\n\t\t\tif (lightHighContrastLink) {\n\t\t\t\tlightHighContrastLink.media = lightHighContrast;\n\t\t\t}\n\t\t\tif (darkHighContrastLink) {\n\t\t\t\tdarkHighContrastLink.media = darkHighContrast;\n\t\t\t}\n\t\t}\n\n\t\t// Sync immediately on mount in case the applied theme diverges from the SSR-rendered media values\n\t\tupdateMediaAttributes();\n\n\t\t// Watch for theme changes driven by ThemeProvider\n\t\tconst observer = new MutationObserver(updateMediaAttributes);\n\t\tobserver.observe(document.documentElement, {\n\t\t\tattributes: true,\n\t\t\tattributeFilter: [\"data-applied-theme\"],\n\t\t});\n\n\t\treturn () => {\n\t\t\tobserver.disconnect();\n\t\t};\n\t}, [forceTheme]);\n\n\t// On SSR (and as the initial React render), emit the link tags with media values\n\t// derived from the cookie-stored theme (if available) and forceTheme.\n\t// The useEffect above will correct them on the client before the user can interact.\n\tconst ssrStoredTheme = ssrCookie != null ? getStoredTheme({ cookie: ssrCookie }) : undefined;\n\tconst ssrAppliedTheme = ssrStoredTheme !== \"system\" ? ssrStoredTheme : undefined;\n\tconst { dark, lightHighContrast, darkHighContrast } = computeMediaValues(\n\t\tssrAppliedTheme,\n\t\tforceTheme,\n\t);\n\n\t// The inline fix script corrects media attributes for users whose stored theme differs from\n\t// their OS preference. It is only needed when the SSR HTML may have been rendered with\n\t// incorrect media values — i.e. when neither ssrCookie (with a non-system theme) nor\n\t// forceTheme provide a deterministic answer at render time.\n\tconst needsFixScript = !forceTheme && ssrAppliedTheme == null;\n\n\t// When forceTheme is set, only render the link tag for that specific theme's stylesheet.\n\t// Light is the base theme with no dedicated lazy stylesheet, so forceTheme=\"light\" renders\n\t// no link tags at all. When forceTheme is unset, all three are rendered.\n\tconst renderDark = !forceTheme || forceTheme === \"dark\";\n\tconst renderLightHighContrast = !forceTheme || forceTheme === \"light-high-contrast\";\n\tconst renderDarkHighContrast = !forceTheme || forceTheme === \"dark-high-contrast\";\n\n\treturn (\n\t\t<>\n\t\t\t{renderDark && (\n\t\t\t\t<link\n\t\t\t\t\trel=\"stylesheet\"\n\t\t\t\t\tid={DARK_LINK_ID}\n\t\t\t\t\thref={darkCssUrl}\n\t\t\t\t\tmedia={dark}\n\t\t\t\t\tsuppressHydrationWarning\n\t\t\t\t/>\n\t\t\t)}\n\t\t\t{renderLightHighContrast && (\n\t\t\t\t<link\n\t\t\t\t\trel=\"stylesheet\"\n\t\t\t\t\tid={LIGHT_HIGH_CONTRAST_LINK_ID}\n\t\t\t\t\thref={lightHighContrastCssUrl}\n\t\t\t\t\tmedia={lightHighContrast}\n\t\t\t\t\tsuppressHydrationWarning\n\t\t\t\t/>\n\t\t\t)}\n\t\t\t{renderDarkHighContrast && (\n\t\t\t\t<link\n\t\t\t\t\trel=\"stylesheet\"\n\t\t\t\t\tid={DARK_HIGH_CONTRAST_LINK_ID}\n\t\t\t\t\thref={darkHighContrastCssUrl}\n\t\t\t\t\tmedia={darkHighContrast}\n\t\t\t\t\tsuppressHydrationWarning\n\t\t\t\t/>\n\t\t\t)}\n\t\t\t{needsFixScript && (\n\t\t\t\t<script\n\t\t\t\t\tdangerouslySetInnerHTML={{ __html: fixMediaScriptContent(forceTheme) }}\n\t\t\t\t\tnonce={nonce}\n\t\t\t\t\tsuppressHydrationWarning\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t);\n}\nMantleStyleSheets.displayName = \"MantleStyleSheets\";\n\nexport {\n\t//,\n\tmantleStyleSheetUrls,\n\tMantleStyleSheets,\n};\n","/**\n * @fileoverview Helpers for preloading ngrok brand fonts from the CDN.\n * All font URLs resolve to `${assetsCdnOrigin}/fonts`.\n */\n\n/**\n * The origin for the assets CDN where custom ngrok fonts and assets are hosted.\n *\n * Keep this stable across the app so we can preconnect/DNS-prefetch consistently.\n * @public\n */\nconst assetsCdnOrigin = \"https://assets.ngrok.com\";\n\n/**\n * Base path for font assets on the CDN.\n * @internal\n */\nconst cdnBase = `${assetsCdnOrigin}/fonts`;\n\nconst coreFontNames = [\n\t\"roobert\",\n\t\"jetbrains-mono\",\n\t\"jetbrains-mono-italic\",\n\t\"family-regular\",\n\t\"family-italic\",\n] as const;\n/**\n * Named keys identifying each individual core font.\n * @public\n */\ntype CoreFontName = (typeof coreFontNames)[number];\n\n/**\n * Maps each {@link CoreFontName} to its CDN font path (relative to the fonts base).\n * @internal\n */\nconst coreFontPathByName = {\n\troobert: \"/roobert/roobert-proportional-vf.woff2\",\n\t\"jetbrains-mono\": \"/jetbrains/jetbrainsmono-wght.woff2\",\n\t\"jetbrains-mono-italic\": \"/jetbrains/jetbrainsmono-italic-wght.woff2\",\n\t\"family-regular\": \"/family/family-regular.woff2\",\n\t\"family-italic\": \"/family/family-italic.woff2\",\n} as const satisfies Record<CoreFontName, `/${string}`>;\n\ntype FontPath = `/${string}` | (string & {});\n\n/**\n * Builds an absolute CDN URL for a given font.\n *\n * @returns {`https://assets.ngrok.com/fonts${T}`} An absolute, literal-typed CDN URL.\n *\n * @example\n * const href = fontHref(\"/roobert/roobert-proportional-vf.woff2\");\n * // -> \"https://assets.ngrok.com/fonts/roobert/roobert-proportional-vf.woff2\"\n */\nfunction fontHref<T extends FontPath = FontPath>(font: T) {\n\tconst path = font.startsWith(\"/\") ? font : `/${font}`;\n\treturn `${cdnBase}${path}` as const;\n}\n\n/**\n * Props for {@link PreloadFont}.\n * @public\n */\ntype PreloadFontProps = {\n\t/**\n\t * The name of the individual core font to preload.\n\t *\n\t * - `\"roobert\"` — Roobert proportional variable font\n\t * - `\"jetbrains-mono\"` — JetBrains Mono variable weight\n\t * - `\"jetbrains-mono-italic\"` — JetBrains Mono italic variable weight\n\t * - `\"family-regular\"` — Family regular\n\t * - `\"family-italic\"` — Family italic\n\t */\n\tname: CoreFontName;\n};\n\n/**\n * Returns an HTTP `Link` header value that preloads a single core font by name.\n *\n * Identical in intent to {@link PreloadFont}, but for server-side use where\n * you want to send the preload hint as an HTTP header instead of (or in\n * addition to) an HTML `<link>` element. Sending this as a `Link` header lets\n * the browser start the font fetch before it has parsed any HTML.\n *\n * @remarks\n * For best performance, also send a `preconnect` hint to {@link assetsCdnOrigin}\n * in the same `Link` header.\n *\n * @example\n * ```ts\n * // In an HTTP handler / server entry:\n * headers.append(\"Link\", preloadFontLink(\"roobert\"));\n * headers.append(\"Link\", preloadFontLink(\"jetbrains-mono\"));\n *\n * // Or as a single combined header:\n * headers.set(\"Link\", [\n * `<${assetsCdnOrigin}>; rel=preconnect; crossorigin`,\n * preloadFontLink(\"roobert\"),\n * ].join(\", \"));\n * ```\n */\nfunction preloadFontLink(name: CoreFontName): string {\n\tconst href = fontHref(coreFontPathByName[name]);\n\treturn `<${href}>; rel=preload; as=font; type=\"font/woff2\"; crossorigin`;\n}\n\n/**\n * Preloads a single core font by name.\n *\n * Use this when you only need one or two specific fonts rather than all core\n * fonts. Include it as early as possible in the document `<head>`.\n *\n * @remarks\n * For best performance, pair this with preconnect/dns-prefetch hints to the CDN.\n *\n * @example\n * ```tsx\n * <head>\n * <link rel=\"preconnect\" href={assetsCdnOrigin} crossOrigin=\"anonymous\" />\n * <link rel=\"dns-prefetch\" href={assetsCdnOrigin} />\n * <PreloadFont name=\"roobert\" />\n * <PreloadFont name=\"jetbrains-mono\" />\n * </head>\n * ```\n */\nconst PreloadFont = ({ name }: PreloadFontProps) => (\n\t<link\n\t\trel=\"preload\"\n\t\thref={fontHref(coreFontPathByName[name])}\n\t\tas=\"font\"\n\t\ttype=\"font/woff2\"\n\t\tcrossOrigin=\"anonymous\"\n\t/>\n);\nPreloadFont.displayName = \"PreloadFont\";\n\nexport type { CoreFontName };\n\nexport {\n\t//,\n\tassetsCdnOrigin,\n\tfontHref,\n\tpreloadFontLink,\n\tPreloadFont,\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ngrok/mantle",
|
|
3
|
-
"version": "0.66.
|
|
3
|
+
"version": "0.66.9",
|
|
4
4
|
"description": "mantle is ngrok's UI library and design system.",
|
|
5
5
|
"homepage": "https://mantle.ngrok.com",
|
|
6
6
|
"license": "MIT",
|
|
@@ -338,10 +338,11 @@
|
|
|
338
338
|
"@testing-library/jest-dom": "6.9.1",
|
|
339
339
|
"@testing-library/react": "16.3.2",
|
|
340
340
|
"@testing-library/user-event": "14.6.1",
|
|
341
|
-
"@
|
|
341
|
+
"@tsdown/css": "0.21.1",
|
|
342
342
|
"@types/prismjs": "1.26.6",
|
|
343
343
|
"@types/react": "19.2.14",
|
|
344
344
|
"@types/react-dom": "19.2.3",
|
|
345
|
+
"@vitest/browser-playwright": "4.0.18",
|
|
345
346
|
"browserslist": "4.28.1",
|
|
346
347
|
"date-fns": "4.1.0",
|
|
347
348
|
"happy-dom": "20.8.3",
|
|
@@ -349,7 +350,7 @@
|
|
|
349
350
|
"react": "19.2.4",
|
|
350
351
|
"react-dom": "19.2.4",
|
|
351
352
|
"tailwindcss": "4.2.1",
|
|
352
|
-
"tsdown": "0.21.
|
|
353
|
+
"tsdown": "0.21.1",
|
|
353
354
|
"typescript": "5.9.3",
|
|
354
355
|
"@cfg/tsconfig": "1.0.0"
|
|
355
356
|
},
|
|
@@ -369,7 +370,6 @@
|
|
|
369
370
|
"scripts": {
|
|
370
371
|
"build": "NODE_OPTIONS='--max-old-space-size=16384' tsdown",
|
|
371
372
|
"clean": "rm -rf dist",
|
|
372
|
-
"install:playwright": "node ../../scripts/install-playwright.js",
|
|
373
373
|
"prebuild": "pnpm run clean",
|
|
374
374
|
"test:watch": "TZ=UTC vitest watch",
|
|
375
375
|
"test": "TZ=UTC vitest run",
|
package/dist/style.css
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
MARK: SYNTAX HIGHLIGHT TOKEN STYLES
|
|
3
|
-
*/
|
|
4
|
-
.token.comment,
|
|
5
|
-
.token.prolog,
|
|
6
|
-
.token.doctype,
|
|
7
|
-
.token.cdata {
|
|
8
|
-
color: var(--color-gray-500);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
.token.property,
|
|
12
|
-
.token.tag,
|
|
13
|
-
.token.boolean,
|
|
14
|
-
.token.number,
|
|
15
|
-
.token.constant,
|
|
16
|
-
.token.symbol,
|
|
17
|
-
.token.deleted {
|
|
18
|
-
color: var(--color-indigo-600);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.token.attr-name,
|
|
22
|
-
.token.string,
|
|
23
|
-
.token.char,
|
|
24
|
-
.token.builtin,
|
|
25
|
-
.token.inserted {
|
|
26
|
-
color: var(--color-green-600);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
.token.operator,
|
|
30
|
-
.token.entity,
|
|
31
|
-
.token.url,
|
|
32
|
-
.language-css .token.string,
|
|
33
|
-
.style .token.string {
|
|
34
|
-
color: var(--color-green-600);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.token.selector,
|
|
38
|
-
.token.atrule,
|
|
39
|
-
.token.attr-value,
|
|
40
|
-
.token.keyword {
|
|
41
|
-
color: var(--color-blue-600);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.token.function,
|
|
45
|
-
.token.class-name {
|
|
46
|
-
color: var(--color-blue-600);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
.token.regex,
|
|
50
|
-
.token.important,
|
|
51
|
-
.token.variable {
|
|
52
|
-
color: var(--color-yellow-600);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.token.important,
|
|
56
|
-
.token.bold {
|
|
57
|
-
font-weight: bold;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.token.italic {
|
|
61
|
-
font-style: italic;
|
|
62
|
-
}
|