@particle-academy/react-fancy 6.4.0 → 6.6.0
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/CHANGELOG.md +29 -0
- package/dist/CodeBlock.types-BFMFB4X2.d.cts +53 -0
- package/dist/CodeBlock.types-BFMFB4X2.d.ts +53 -0
- package/dist/chunk-2X54BVXG.js +58 -0
- package/dist/chunk-2X54BVXG.js.map +1 -0
- package/dist/chunk-5WXY3Q6X.cjs +63 -0
- package/dist/chunk-5WXY3Q6X.cjs.map +1 -0
- package/dist/{chunk-AHZIT3CM.js → chunk-MI57NAKF.js} +24 -22
- package/dist/chunk-MI57NAKF.js.map +1 -0
- package/dist/{chunk-VAVS6WRZ.cjs → chunk-PM5I5ZHK.cjs} +23 -21
- package/dist/chunk-PM5I5ZHK.cjs.map +1 -0
- package/dist/{chunk-P24A2BIR.js → chunk-TFX4LL7V.js} +17 -12
- package/dist/chunk-TFX4LL7V.js.map +1 -0
- package/dist/{chunk-VQLPAYQF.cjs → chunk-THMFLRDW.cjs} +4 -4
- package/dist/{chunk-VQLPAYQF.cjs.map → chunk-THMFLRDW.cjs.map} +1 -1
- package/dist/{chunk-UMDOSJUH.js → chunk-VSTUGU7W.js} +3 -3
- package/dist/{chunk-UMDOSJUH.js.map → chunk-VSTUGU7W.js.map} +1 -1
- package/dist/{chunk-NIAG6OF6.cjs → chunk-YQZB5Z7C.cjs} +16 -11
- package/dist/chunk-YQZB5Z7C.cjs.map +1 -0
- package/dist/code-block.cjs +3 -3
- package/dist/code-block.d.cts +3 -45
- package/dist/code-block.d.ts +3 -45
- package/dist/code-block.js +2 -2
- package/dist/code-view.cjs +3 -3
- package/dist/code-view.d.cts +9 -6
- package/dist/code-view.d.ts +9 -6
- package/dist/code-view.js +2 -2
- package/dist/editor.cjs +5 -5
- package/dist/editor.js +3 -3
- package/dist/index.cjs +8 -8
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -4
- package/dist/styles.css +20 -0
- package/dist/styles.css.map +1 -1
- package/docs/CodeBlock.md +30 -5
- package/docs/CodeView.md +28 -8
- package/package.json +2 -2
- package/dist/chunk-6BB32POL.js +0 -29
- package/dist/chunk-6BB32POL.js.map +0 -1
- package/dist/chunk-AHZIT3CM.js.map +0 -1
- package/dist/chunk-HZUKPYR3.cjs +0 -31
- package/dist/chunk-HZUKPYR3.cjs.map +0 -1
- package/dist/chunk-NIAG6OF6.cjs.map +0 -1
- package/dist/chunk-P24A2BIR.js.map +0 -1
- package/dist/chunk-VAVS6WRZ.cjs.map +0 -1
package/docs/CodeView.md
CHANGED
|
@@ -6,10 +6,26 @@ primitives in [`@particle-academy/fancy-file-commons`](https://www.npmjs.com/pac
|
|
|
6
6
|
(no CodeMirror, no heavy dependency). Powers the [`Editor`](./Editor.md)'s Source
|
|
7
7
|
view, and is usable standalone.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
It highlights every grammar commons ships, case-insensitively — the same set as
|
|
10
|
+
[`CodeBlock`](./CodeBlock.md):
|
|
11
|
+
|
|
12
|
+
| Language | `language` values |
|
|
13
|
+
|---|---|
|
|
14
|
+
| TypeScript | `typescript`, `ts`, `tsx` |
|
|
15
|
+
| JavaScript | `javascript`, `js`, `jsx` |
|
|
16
|
+
| JSON | `json` (highlighted by the JavaScript grammar) |
|
|
17
|
+
| PHP | `php` |
|
|
18
|
+
| Python | `python`, `py` |
|
|
19
|
+
| Go | `go` |
|
|
20
|
+
| Markdown | `markdown`, `md` |
|
|
21
|
+
| Shell | `shell`, `sh`, `bash`, `zsh` |
|
|
22
|
+
| dotenv | `dotenv`, `env` |
|
|
23
|
+
| HTML / XML | `html`, `htm`, `xml`, `xhtml` |
|
|
24
|
+
|
|
25
|
+
Anything else, including `"plaintext"`, renders as plain text. The `Editor`'s
|
|
26
|
+
Source view passes `"html"` for HTML output and `"plaintext"` otherwise, so it is
|
|
27
|
+
unchanged. For a full IDE (line numbers, diff gutter), use
|
|
28
|
+
[`@particle-academy/fancy-code`](https://www.npmjs.com/package/@particle-academy/fancy-code)'s
|
|
13
29
|
`CodeEditor`.
|
|
14
30
|
|
|
15
31
|
## Import
|
|
@@ -50,7 +66,7 @@ Read-only (omit `onChange` or pass `readOnly`):
|
|
|
50
66
|
|------|------|---------|-------------|
|
|
51
67
|
| value | `string` | — | Source text (controlled). |
|
|
52
68
|
| onChange | `(value: string) => void` | — | Called on edit. Omit for a read-only view. |
|
|
53
|
-
| language | `
|
|
69
|
+
| language | `CodeLanguage` (any string) | `"plaintext"` | Language id or alias, case-insensitive (table above). Unknown values render plain. |
|
|
54
70
|
| readOnly | `boolean` | `false` | Read-only even if `onChange` is passed. |
|
|
55
71
|
| placeholder | `string` | — | Shown when empty. |
|
|
56
72
|
| minHeight | `number` | `120` | Min height (px) before growing with content. |
|
|
@@ -59,6 +75,10 @@ Read-only (omit `onChange` or pass `readOnly`):
|
|
|
59
75
|
|
|
60
76
|
## Theme
|
|
61
77
|
|
|
62
|
-
The view
|
|
63
|
-
|
|
64
|
-
|
|
78
|
+
The view carries both palettes as CSS variables (`--fc-<slot>-l` /
|
|
79
|
+
`--fc-<slot>-d`) and CSS picks one, so server-rendered HTML is right in either
|
|
80
|
+
theme with no flash. The dark palette applies under your Tailwind `dark` variant
|
|
81
|
+
(its `dark:` classes compile from the `@source`d dist, so it matches whatever
|
|
82
|
+
drives your other `dark:` utilities) or under a `.dark` / `[data-theme="dark"]`
|
|
83
|
+
ancestor via `styles.css`. See `CodeBlock`'s Theming section for overrides. PHP
|
|
84
|
+
without a `<?php` open tag is highlighted as PHP (snippet mode).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@particle-academy/react-fancy",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.6.0",
|
|
4
4
|
"description": "React UI component library for Human+ UX — controlled, agent-bridgeable primitives",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -1054,7 +1054,7 @@
|
|
|
1054
1054
|
"tailwindcss": "^4.0.0"
|
|
1055
1055
|
},
|
|
1056
1056
|
"dependencies": {
|
|
1057
|
-
"@particle-academy/fancy-file-commons": ">=0.
|
|
1057
|
+
"@particle-academy/fancy-file-commons": ">=0.5 <2.0",
|
|
1058
1058
|
"lucide-react": "^0.511.0",
|
|
1059
1059
|
"marked": "^15.0.0",
|
|
1060
1060
|
"tailwind-merge": "^3.0.0"
|
package/dist/chunk-6BB32POL.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { useSyncExternalStore } from 'react';
|
|
2
|
-
|
|
3
|
-
// src/components/CodeView/use-is-dark.ts
|
|
4
|
-
function darkQuery() {
|
|
5
|
-
return typeof window.matchMedia === "function" ? window.matchMedia("(prefers-color-scheme: dark)") : null;
|
|
6
|
-
}
|
|
7
|
-
function subscribeDark(callback) {
|
|
8
|
-
const mq = darkQuery();
|
|
9
|
-
mq?.addEventListener?.("change", callback);
|
|
10
|
-
const observer = new MutationObserver(callback);
|
|
11
|
-
observer.observe(document.documentElement, {
|
|
12
|
-
attributes: true,
|
|
13
|
-
attributeFilter: ["class"]
|
|
14
|
-
});
|
|
15
|
-
return () => {
|
|
16
|
-
mq?.removeEventListener?.("change", callback);
|
|
17
|
-
observer.disconnect();
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
function darkSnapshot() {
|
|
21
|
-
return document.documentElement.classList.contains("dark") || (darkQuery()?.matches ?? false);
|
|
22
|
-
}
|
|
23
|
-
function useIsDark() {
|
|
24
|
-
return useSyncExternalStore(subscribeDark, darkSnapshot, () => false);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export { useIsDark };
|
|
28
|
-
//# sourceMappingURL=chunk-6BB32POL.js.map
|
|
29
|
-
//# sourceMappingURL=chunk-6BB32POL.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/CodeView/use-is-dark.ts"],"names":[],"mappings":";;;AAWA,SAAS,SAAA,GAAmC;AAC1C,EAAA,OAAO,OAAO,MAAA,CAAO,UAAA,KAAe,aAChC,MAAA,CAAO,UAAA,CAAW,8BAA8B,CAAA,GAChD,IAAA;AACN;AAEA,SAAS,cAAc,QAAA,EAAkC;AACvD,EAAA,MAAM,KAAK,SAAA,EAAU;AACrB,EAAA,EAAA,EAAI,gBAAA,GAAmB,UAAU,QAAQ,CAAA;AACzC,EAAA,MAAM,QAAA,GAAW,IAAI,gBAAA,CAAiB,QAAQ,CAAA;AAC9C,EAAA,QAAA,CAAS,OAAA,CAAQ,SAAS,eAAA,EAAiB;AAAA,IACzC,UAAA,EAAY,IAAA;AAAA,IACZ,eAAA,EAAiB,CAAC,OAAO;AAAA,GAC1B,CAAA;AACD,EAAA,OAAO,MAAM;AACX,IAAA,EAAA,EAAI,mBAAA,GAAsB,UAAU,QAAQ,CAAA;AAC5C,IAAA,QAAA,CAAS,UAAA,EAAW;AAAA,EACtB,CAAA;AACF;AAEA,SAAS,YAAA,GAAwB;AAC/B,EAAA,OAAO,QAAA,CAAS,gBAAgB,SAAA,CAAU,QAAA,CAAS,MAAM,CAAA,KAAM,SAAA,IAAa,OAAA,IAAW,KAAA,CAAA;AACzF;AAGO,SAAS,SAAA,GAAqB;AACnC,EAAA,OAAO,oBAAA,CAAqB,aAAA,EAAe,YAAA,EAAc,MAAM,KAAK,CAAA;AACtE","file":"chunk-6BB32POL.js","sourcesContent":["import { useSyncExternalStore } from \"react\";\n\n// ── Dark-mode detection ────────────────────────────────────────────────────\n// The highlight palette is baked in as inline colors, so we resolve light/dark\n// ourselves. Follow BOTH signals so we match whatever drives the surrounding\n// `dark:` Tailwind variants: the `.dark` class (class strategy) OR the\n// prefers-color-scheme media query (media strategy). Shared by CodeView and\n// CodeBlock so the two always agree on the palette.\n\n// `matchMedia` is optional: jsdom (and so a consumer's component tests) has\n// none, and a missing media API must cost the media signal, not the render.\nfunction darkQuery(): MediaQueryList | null {\n return typeof window.matchMedia === \"function\"\n ? window.matchMedia(\"(prefers-color-scheme: dark)\")\n : null;\n}\n\nfunction subscribeDark(callback: () => void): () => void {\n const mq = darkQuery();\n mq?.addEventListener?.(\"change\", callback);\n const observer = new MutationObserver(callback);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"class\"],\n });\n return () => {\n mq?.removeEventListener?.(\"change\", callback);\n observer.disconnect();\n };\n}\n\nfunction darkSnapshot(): boolean {\n return document.documentElement.classList.contains(\"dark\") || (darkQuery()?.matches ?? false);\n}\n\n/** Light on the server (and during hydration), then the real theme. */\nexport function useIsDark(): boolean {\n return useSyncExternalStore(subscribeDark, darkSnapshot, () => false);\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/CodeBlock/CodeBlock.tsx"],"names":["CodeBlock"],"mappings":"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","file":"chunk-AHZIT3CM.js","sourcesContent":["import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n highlightCode,\n tokenizeHtml,\n LIGHT_COLORS,\n DARK_COLORS,\n type ThemeColors,\n type Token,\n} from \"@particle-academy/fancy-file-commons\";\nimport { cn } from \"../../utils/cn\";\nimport { visuallyHidden } from \"../../utils/visually-hidden\";\nimport { Button } from \"../Button/Button\";\nimport { defaultClipboardWriter, runWriter } from \"../ContentRenderer/copy\";\nimport { useIsDark } from \"../CodeView/use-is-dark\";\nimport type { CodeBlockProps } from \"./CodeBlock.types\";\n\n/** How long \"Copied\" / \"Copy failed\" stays before the button resets. */\nconst FEEDBACK_MS = 2000;\n\n/** Language ids that map to the one grammar fancy-file-commons ships. */\nconst HTML_ALIASES = new Set([\"html\", \"htm\", \"xml\", \"xhtml\"]);\n\nfunction tokensFor(code: string, language: string): Token[] {\n return HTML_ALIASES.has(language.toLowerCase()) ? tokenizeHtml(code) : [];\n}\n\n/**\n * Highlight one line at a time, clipping each token to the line it falls on, so\n * a token that spans lines (an HTML comment) keeps its colour on every line.\n * `highlightCode` escapes every slice it emits, highlighted or not.\n */\nfunction highlightLines(code: string, tokens: Token[], colors: ThemeColors): string[] {\n const out: string[] = [];\n let start = 0;\n let ti = 0;\n for (const line of code.split(\"\\n\")) {\n const end = start + line.length;\n while (ti < tokens.length && tokens[ti].end <= start) ti++;\n const clipped: Token[] = [];\n for (let j = ti; j < tokens.length && tokens[j].start < end; j++) {\n const s = Math.max(tokens[j].start, start) - start;\n const e = Math.min(tokens[j].end, end) - start;\n if (e > s) clipped.push({ type: tokens[j].type, start: s, end: e });\n }\n out.push(highlightCode(line, clipped, colors));\n start = end + 1;\n }\n return out;\n}\n\ntype CopyState = \"idle\" | \"copied\" | \"failed\";\n\n// Matches CodeView's text metrics so the two sit together.\nconst TEXT_METRICS = \"m-0 p-3 font-mono text-xs leading-relaxed\";\n\n/**\n * A display-only code block: `<pre><code>`, syntax-highlighted where a grammar\n * exists, with an optional copy button. It contains no textarea, input, select\n * or contenteditable element, so it is safe on pages that must not accept\n * input. For an editable or selectable source surface, use `CodeView`.\n */\nexport const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(function CodeBlock(\n {\n code,\n language = \"plaintext\",\n copyable = true,\n title,\n showLineNumbers = false,\n wrap = false,\n copyLabels,\n onCopyCode,\n className,\n style,\n ...rest\n },\n ref,\n) {\n const isDark = useIsDark();\n const colors: ThemeColors = isDark ? DARK_COLORS : LIGHT_COLORS;\n\n const tokens = useMemo(() => tokensFor(code, language), [code, language]);\n const html = useMemo(\n () => (showLineNumbers ? null : highlightCode(code, tokens, colors)),\n [code, tokens, colors, showLineNumbers],\n );\n const lines = useMemo(() => {\n if (!showLineNumbers) return null;\n const all = highlightLines(code, tokens, colors);\n // A trailing newline ends the last line; it does not start a new one.\n if (all.length > 1 && code.endsWith(\"\\n\")) all.pop();\n return all;\n }, [code, tokens, colors, showLineNumbers]);\n\n const [copyState, setCopyState] = useState<CopyState>(\"idle\");\n const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n useEffect(() => () => clearTimeout(timer.current), []);\n\n // Latest props at click time, without re-creating the handler.\n const latest = useRef({ code, onCopyCode });\n latest.current = { code, onCopyCode };\n\n const onCopyClick = useCallback(async () => {\n const { code: text, onCopyCode: report } = latest.current;\n // Raw `code`, never the highlighted HTML. `defaultClipboardWriter` throws\n // when `navigator.clipboard` is absent (SSR, insecure context), and\n // `runWriter` turns a throw, rejection or hang into `false`.\n const ok = await runWriter(defaultClipboardWriter, { kind: \"code\", text, html: \"\" });\n setCopyState(ok ? \"copied\" : \"failed\");\n clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopyState(\"idle\"), FEEDBACK_MS);\n report?.({ text, ok });\n }, []);\n\n const labels = {\n copy: copyLabels?.copy ?? \"Copy code\",\n copied: copyLabels?.copied ?? \"Copied\",\n failed: copyLabels?.failed ?? \"Copy failed\",\n };\n const label = copyState === \"copied\" ? labels.copied : copyState === \"failed\" ? labels.failed : labels.copy;\n const hasHeader = title != null && title !== false;\n const floating = copyable && !hasHeader;\n\n const copyButton = copyable ? (\n <Button\n type=\"button\"\n size=\"xs\"\n variant=\"ghost\"\n icon={copyState === \"copied\" ? \"check\" : copyState === \"failed\" ? \"x\" : \"copy\"}\n aria-label={label}\n title={label}\n data-react-fancy-code-block-copy=\"\"\n data-copy-state={copyState}\n onClick={onCopyClick}\n className={cn(\n floating && \"opacity-80 group-hover:opacity-100 focus-visible:opacity-100\",\n )}\n style={floating ? { backgroundColor: colors.background } : undefined}\n >\n {/* Visible feedback; idle is icon-only when floating over the code. */}\n {copyState !== \"idle\" ? label : hasHeader ? \"Copy\" : undefined}\n </Button>\n ) : null;\n\n const gutterWidth = lines ? `${String(lines.length).length}ch` : undefined;\n\n return (\n <div\n ref={ref}\n data-react-fancy-code-block=\"\"\n data-language={language}\n data-wrap={wrap ? \"true\" : \"false\"}\n className={cn(\n \"group relative overflow-hidden rounded-lg border\",\n className,\n )}\n style={{\n backgroundColor: colors.background,\n color: colors.foreground,\n borderColor: colors.gutterBorder,\n // `relative` inline too: the floating button and the live region are\n // absolute, and must not escape a host whose Tailwind lacks the class.\n position: \"relative\",\n ...style,\n }}\n {...rest}\n >\n {hasHeader && (\n <div\n data-react-fancy-code-block-header=\"\"\n className=\"flex items-center justify-between gap-2 border-b py-1 pl-3 pr-1 text-xs\"\n style={{\n backgroundColor: colors.gutterBackground,\n color: colors.gutterForeground,\n borderColor: colors.gutterBorder,\n }}\n >\n <span data-react-fancy-code-block-title=\"\" className=\"min-w-0 truncate font-mono\">\n {title}\n </span>\n {copyButton}\n </div>\n )}\n\n <pre\n data-react-fancy-code-block-pre=\"\"\n className={cn(TEXT_METRICS, \"border-none\", wrap ? \"overflow-x-hidden\" : \"overflow-x-auto\", floating && \"pr-12\")}\n style={{\n whiteSpace: wrap ? \"pre-wrap\" : \"pre\",\n overflowWrap: wrap ? \"break-word\" : \"normal\",\n backgroundColor: \"transparent\",\n color: \"inherit\",\n }}\n >\n {lines ? (\n <code\n data-react-fancy-code-block-code=\"\"\n style={{ display: wrap ? \"block\" : \"inline-block\", minWidth: \"100%\" }}\n >\n {lines.map((lineHtml, i) => (\n <span key={i} data-react-fancy-code-block-line={i + 1} style={{ display: \"flex\" }}>\n <span\n data-react-fancy-code-block-line-number=\"\"\n aria-hidden=\"true\"\n className=\"select-none\"\n style={{\n flex: \"none\",\n width: gutterWidth,\n marginRight: \"1.5ch\",\n textAlign: \"right\",\n color: colors.gutterForeground,\n userSelect: \"none\",\n }}\n >\n {i + 1}\n </span>\n <span\n data-react-fancy-code-block-line-content=\"\"\n style={{ flex: \"1 1 auto\", minWidth: 0 }}\n dangerouslySetInnerHTML={{ __html: lineHtml }}\n />\n </span>\n ))}\n </code>\n ) : (\n <code data-react-fancy-code-block-code=\"\" dangerouslySetInnerHTML={{ __html: html ?? \"\" }} />\n )}\n </pre>\n\n {floating && (\n <div\n data-react-fancy-code-block-actions=\"\"\n className=\"absolute right-2 top-2\"\n style={{ position: \"absolute\", right: \"0.5rem\", top: \"0.5rem\" }}\n >\n {copyButton}\n </div>\n )}\n\n {copyable && (\n <span role=\"status\" aria-live=\"polite\" className=\"sr-only\" style={visuallyHidden}>\n {copyState === \"idle\" ? \"\" : label}\n </span>\n )}\n </div>\n );\n});\n\nCodeBlock.displayName = \"CodeBlock\";\n"]}
|
package/dist/chunk-HZUKPYR3.cjs
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var react = require('react');
|
|
4
|
-
|
|
5
|
-
// src/components/CodeView/use-is-dark.ts
|
|
6
|
-
function darkQuery() {
|
|
7
|
-
return typeof window.matchMedia === "function" ? window.matchMedia("(prefers-color-scheme: dark)") : null;
|
|
8
|
-
}
|
|
9
|
-
function subscribeDark(callback) {
|
|
10
|
-
const mq = darkQuery();
|
|
11
|
-
mq?.addEventListener?.("change", callback);
|
|
12
|
-
const observer = new MutationObserver(callback);
|
|
13
|
-
observer.observe(document.documentElement, {
|
|
14
|
-
attributes: true,
|
|
15
|
-
attributeFilter: ["class"]
|
|
16
|
-
});
|
|
17
|
-
return () => {
|
|
18
|
-
mq?.removeEventListener?.("change", callback);
|
|
19
|
-
observer.disconnect();
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
function darkSnapshot() {
|
|
23
|
-
return document.documentElement.classList.contains("dark") || (darkQuery()?.matches ?? false);
|
|
24
|
-
}
|
|
25
|
-
function useIsDark() {
|
|
26
|
-
return react.useSyncExternalStore(subscribeDark, darkSnapshot, () => false);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
exports.useIsDark = useIsDark;
|
|
30
|
-
//# sourceMappingURL=chunk-HZUKPYR3.cjs.map
|
|
31
|
-
//# sourceMappingURL=chunk-HZUKPYR3.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/CodeView/use-is-dark.ts"],"names":["useSyncExternalStore"],"mappings":";;;;;AAWA,SAAS,SAAA,GAAmC;AAC1C,EAAA,OAAO,OAAO,MAAA,CAAO,UAAA,KAAe,aAChC,MAAA,CAAO,UAAA,CAAW,8BAA8B,CAAA,GAChD,IAAA;AACN;AAEA,SAAS,cAAc,QAAA,EAAkC;AACvD,EAAA,MAAM,KAAK,SAAA,EAAU;AACrB,EAAA,EAAA,EAAI,gBAAA,GAAmB,UAAU,QAAQ,CAAA;AACzC,EAAA,MAAM,QAAA,GAAW,IAAI,gBAAA,CAAiB,QAAQ,CAAA;AAC9C,EAAA,QAAA,CAAS,OAAA,CAAQ,SAAS,eAAA,EAAiB;AAAA,IACzC,UAAA,EAAY,IAAA;AAAA,IACZ,eAAA,EAAiB,CAAC,OAAO;AAAA,GAC1B,CAAA;AACD,EAAA,OAAO,MAAM;AACX,IAAA,EAAA,EAAI,mBAAA,GAAsB,UAAU,QAAQ,CAAA;AAC5C,IAAA,QAAA,CAAS,UAAA,EAAW;AAAA,EACtB,CAAA;AACF;AAEA,SAAS,YAAA,GAAwB;AAC/B,EAAA,OAAO,QAAA,CAAS,gBAAgB,SAAA,CAAU,QAAA,CAAS,MAAM,CAAA,KAAM,SAAA,IAAa,OAAA,IAAW,KAAA,CAAA;AACzF;AAGO,SAAS,SAAA,GAAqB;AACnC,EAAA,OAAOA,0BAAA,CAAqB,aAAA,EAAe,YAAA,EAAc,MAAM,KAAK,CAAA;AACtE","file":"chunk-HZUKPYR3.cjs","sourcesContent":["import { useSyncExternalStore } from \"react\";\n\n// ── Dark-mode detection ────────────────────────────────────────────────────\n// The highlight palette is baked in as inline colors, so we resolve light/dark\n// ourselves. Follow BOTH signals so we match whatever drives the surrounding\n// `dark:` Tailwind variants: the `.dark` class (class strategy) OR the\n// prefers-color-scheme media query (media strategy). Shared by CodeView and\n// CodeBlock so the two always agree on the palette.\n\n// `matchMedia` is optional: jsdom (and so a consumer's component tests) has\n// none, and a missing media API must cost the media signal, not the render.\nfunction darkQuery(): MediaQueryList | null {\n return typeof window.matchMedia === \"function\"\n ? window.matchMedia(\"(prefers-color-scheme: dark)\")\n : null;\n}\n\nfunction subscribeDark(callback: () => void): () => void {\n const mq = darkQuery();\n mq?.addEventListener?.(\"change\", callback);\n const observer = new MutationObserver(callback);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"class\"],\n });\n return () => {\n mq?.removeEventListener?.(\"change\", callback);\n observer.disconnect();\n };\n}\n\nfunction darkSnapshot(): boolean {\n return document.documentElement.classList.contains(\"dark\") || (darkQuery()?.matches ?? false);\n}\n\n/** Light on the server (and during hydration), then the real theme. */\nexport function useIsDark(): boolean {\n return useSyncExternalStore(subscribeDark, darkSnapshot, () => false);\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/CodeView/CodeView.tsx"],"names":["useRef","useIsDark","DARK_COLORS","LIGHT_COLORS","useMemo","tokenizeHtml","highlightCode","useEffect","jsx","cn","jsxs"],"mappings":"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","file":"chunk-NIAG6OF6.cjs","sourcesContent":["import { useEffect, useMemo, useRef } from \"react\";\nimport {\n highlightCode,\n tokenizeHtml,\n LIGHT_COLORS,\n DARK_COLORS,\n type ThemeColors,\n} from \"@particle-academy/fancy-file-commons\";\nimport { cn } from \"../../utils/cn\";\nimport { useIsDark } from \"./use-is-dark\";\nimport type { CodeViewProps } from \"./CodeView.types\";\n\n// Shared text metrics — the overlay and the textarea MUST match exactly (font,\n// size, line-height, padding, wrapping) so the highlighted glyphs sit under the\n// caret. Keep this string identical on both layers.\nconst TEXT_METRICS = \"m-0 p-3 font-mono text-xs leading-relaxed\";\n\n/**\n * A lightweight, syntax-highlighted source view built on the pure highlight\n * primitives in `@particle-academy/fancy-file-commons` — a highlight overlay\n * with a transparent, auto-growing textarea on top; the container scrolls both\n * together. Fills its parent's height (`h-full`) and grows with content down to\n * `minHeight`. Only HTML is highlighted; other languages render as plain text.\n */\nexport function CodeView({\n value,\n onChange,\n language = \"plaintext\",\n readOnly = false,\n placeholder,\n minHeight = 120,\n maxHeight,\n className,\n}: CodeViewProps) {\n const textareaRef = useRef<HTMLTextAreaElement | null>(null);\n const isDark = useIsDark();\n const colors: ThemeColors = isDark ? DARK_COLORS : LIGHT_COLORS;\n\n const highlighted = useMemo(() => {\n const tokens = language === \"html\" ? tokenizeHtml(value) : [];\n return highlightCode(value, tokens, colors);\n }, [value, language, colors]);\n\n // Grow the textarea to its content height so it never scrolls internally —\n // the outer container owns scrolling and moves overlay + textarea in lockstep.\n useEffect(() => {\n const el = textareaRef.current;\n if (!el) return;\n el.style.height = \"auto\";\n el.style.height = `${el.scrollHeight}px`;\n }, [value]);\n\n const editable = !readOnly && !!onChange;\n\n return (\n <div\n data-react-fancy-code-view=\"\"\n // No baked-in height: the consumer picks the fill mechanism — `flex-1`\n // inside a flex column (the Editor source body), or `h-full` when the\n // parent has a definite height. A hard-coded `h-full` here collapses to\n // content height under a flex-computed (indefinite) parent.\n className={cn(\"relative overflow-auto\", className)}\n style={{ backgroundColor: colors.background, color: colors.foreground, minHeight, maxHeight }}\n >\n <div className=\"relative min-h-full\">\n {/* Highlight overlay */}\n <pre\n aria-hidden=\"true\"\n className={cn(\"pointer-events-none absolute inset-0 overflow-hidden border-none\", TEXT_METRICS)}\n style={{ whiteSpace: \"pre-wrap\", overflowWrap: \"break-word\" }}\n >\n <code dangerouslySetInnerHTML={{ __html: highlighted + \"\\n\" }} />\n </pre>\n\n {/* Placeholder */}\n {placeholder && value.length === 0 && (\n <div className={cn(\"pointer-events-none absolute left-0 top-0 opacity-40\", TEXT_METRICS)}>\n {placeholder}\n </div>\n )}\n\n {/* Transparent input layer (caret + selection visible, glyphs from overlay) */}\n <textarea\n ref={textareaRef}\n data-react-fancy-code-view-input=\"\"\n value={value}\n onChange={editable ? (e) => onChange!(e.target.value) : undefined}\n readOnly={!editable}\n spellCheck={false}\n autoComplete=\"off\"\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n aria-label={placeholder}\n className={cn(\n \"relative block w-full resize-none border-none bg-transparent text-transparent outline-none\",\n TEXT_METRICS,\n )}\n style={{\n caretColor: colors.cursorColor,\n overflow: \"hidden\",\n whiteSpace: \"pre-wrap\",\n overflowWrap: \"break-word\",\n minHeight,\n }}\n />\n </div>\n </div>\n );\n}\n\nCodeView.displayName = \"CodeView\";\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/CodeView/CodeView.tsx"],"names":[],"mappings":"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","file":"chunk-P24A2BIR.js","sourcesContent":["import { useEffect, useMemo, useRef } from \"react\";\nimport {\n highlightCode,\n tokenizeHtml,\n LIGHT_COLORS,\n DARK_COLORS,\n type ThemeColors,\n} from \"@particle-academy/fancy-file-commons\";\nimport { cn } from \"../../utils/cn\";\nimport { useIsDark } from \"./use-is-dark\";\nimport type { CodeViewProps } from \"./CodeView.types\";\n\n// Shared text metrics — the overlay and the textarea MUST match exactly (font,\n// size, line-height, padding, wrapping) so the highlighted glyphs sit under the\n// caret. Keep this string identical on both layers.\nconst TEXT_METRICS = \"m-0 p-3 font-mono text-xs leading-relaxed\";\n\n/**\n * A lightweight, syntax-highlighted source view built on the pure highlight\n * primitives in `@particle-academy/fancy-file-commons` — a highlight overlay\n * with a transparent, auto-growing textarea on top; the container scrolls both\n * together. Fills its parent's height (`h-full`) and grows with content down to\n * `minHeight`. Only HTML is highlighted; other languages render as plain text.\n */\nexport function CodeView({\n value,\n onChange,\n language = \"plaintext\",\n readOnly = false,\n placeholder,\n minHeight = 120,\n maxHeight,\n className,\n}: CodeViewProps) {\n const textareaRef = useRef<HTMLTextAreaElement | null>(null);\n const isDark = useIsDark();\n const colors: ThemeColors = isDark ? DARK_COLORS : LIGHT_COLORS;\n\n const highlighted = useMemo(() => {\n const tokens = language === \"html\" ? tokenizeHtml(value) : [];\n return highlightCode(value, tokens, colors);\n }, [value, language, colors]);\n\n // Grow the textarea to its content height so it never scrolls internally —\n // the outer container owns scrolling and moves overlay + textarea in lockstep.\n useEffect(() => {\n const el = textareaRef.current;\n if (!el) return;\n el.style.height = \"auto\";\n el.style.height = `${el.scrollHeight}px`;\n }, [value]);\n\n const editable = !readOnly && !!onChange;\n\n return (\n <div\n data-react-fancy-code-view=\"\"\n // No baked-in height: the consumer picks the fill mechanism — `flex-1`\n // inside a flex column (the Editor source body), or `h-full` when the\n // parent has a definite height. A hard-coded `h-full` here collapses to\n // content height under a flex-computed (indefinite) parent.\n className={cn(\"relative overflow-auto\", className)}\n style={{ backgroundColor: colors.background, color: colors.foreground, minHeight, maxHeight }}\n >\n <div className=\"relative min-h-full\">\n {/* Highlight overlay */}\n <pre\n aria-hidden=\"true\"\n className={cn(\"pointer-events-none absolute inset-0 overflow-hidden border-none\", TEXT_METRICS)}\n style={{ whiteSpace: \"pre-wrap\", overflowWrap: \"break-word\" }}\n >\n <code dangerouslySetInnerHTML={{ __html: highlighted + \"\\n\" }} />\n </pre>\n\n {/* Placeholder */}\n {placeholder && value.length === 0 && (\n <div className={cn(\"pointer-events-none absolute left-0 top-0 opacity-40\", TEXT_METRICS)}>\n {placeholder}\n </div>\n )}\n\n {/* Transparent input layer (caret + selection visible, glyphs from overlay) */}\n <textarea\n ref={textareaRef}\n data-react-fancy-code-view-input=\"\"\n value={value}\n onChange={editable ? (e) => onChange!(e.target.value) : undefined}\n readOnly={!editable}\n spellCheck={false}\n autoComplete=\"off\"\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n aria-label={placeholder}\n className={cn(\n \"relative block w-full resize-none border-none bg-transparent text-transparent outline-none\",\n TEXT_METRICS,\n )}\n style={{\n caretColor: colors.cursorColor,\n overflow: \"hidden\",\n whiteSpace: \"pre-wrap\",\n overflowWrap: \"break-word\",\n minHeight,\n }}\n />\n </div>\n </div>\n );\n}\n\nCodeView.displayName = \"CodeView\";\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/CodeBlock/CodeBlock.tsx"],"names":["tokenizeHtml","highlightCode","forwardRef","CodeBlock","useIsDark","DARK_COLORS","LIGHT_COLORS","useMemo","useState","useRef","useEffect","useCallback","runWriter","defaultClipboardWriter","jsx","Button","cn","jsxs","visuallyHidden"],"mappings":"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","file":"chunk-VAVS6WRZ.cjs","sourcesContent":["import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n highlightCode,\n tokenizeHtml,\n LIGHT_COLORS,\n DARK_COLORS,\n type ThemeColors,\n type Token,\n} from \"@particle-academy/fancy-file-commons\";\nimport { cn } from \"../../utils/cn\";\nimport { visuallyHidden } from \"../../utils/visually-hidden\";\nimport { Button } from \"../Button/Button\";\nimport { defaultClipboardWriter, runWriter } from \"../ContentRenderer/copy\";\nimport { useIsDark } from \"../CodeView/use-is-dark\";\nimport type { CodeBlockProps } from \"./CodeBlock.types\";\n\n/** How long \"Copied\" / \"Copy failed\" stays before the button resets. */\nconst FEEDBACK_MS = 2000;\n\n/** Language ids that map to the one grammar fancy-file-commons ships. */\nconst HTML_ALIASES = new Set([\"html\", \"htm\", \"xml\", \"xhtml\"]);\n\nfunction tokensFor(code: string, language: string): Token[] {\n return HTML_ALIASES.has(language.toLowerCase()) ? tokenizeHtml(code) : [];\n}\n\n/**\n * Highlight one line at a time, clipping each token to the line it falls on, so\n * a token that spans lines (an HTML comment) keeps its colour on every line.\n * `highlightCode` escapes every slice it emits, highlighted or not.\n */\nfunction highlightLines(code: string, tokens: Token[], colors: ThemeColors): string[] {\n const out: string[] = [];\n let start = 0;\n let ti = 0;\n for (const line of code.split(\"\\n\")) {\n const end = start + line.length;\n while (ti < tokens.length && tokens[ti].end <= start) ti++;\n const clipped: Token[] = [];\n for (let j = ti; j < tokens.length && tokens[j].start < end; j++) {\n const s = Math.max(tokens[j].start, start) - start;\n const e = Math.min(tokens[j].end, end) - start;\n if (e > s) clipped.push({ type: tokens[j].type, start: s, end: e });\n }\n out.push(highlightCode(line, clipped, colors));\n start = end + 1;\n }\n return out;\n}\n\ntype CopyState = \"idle\" | \"copied\" | \"failed\";\n\n// Matches CodeView's text metrics so the two sit together.\nconst TEXT_METRICS = \"m-0 p-3 font-mono text-xs leading-relaxed\";\n\n/**\n * A display-only code block: `<pre><code>`, syntax-highlighted where a grammar\n * exists, with an optional copy button. It contains no textarea, input, select\n * or contenteditable element, so it is safe on pages that must not accept\n * input. For an editable or selectable source surface, use `CodeView`.\n */\nexport const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(function CodeBlock(\n {\n code,\n language = \"plaintext\",\n copyable = true,\n title,\n showLineNumbers = false,\n wrap = false,\n copyLabels,\n onCopyCode,\n className,\n style,\n ...rest\n },\n ref,\n) {\n const isDark = useIsDark();\n const colors: ThemeColors = isDark ? DARK_COLORS : LIGHT_COLORS;\n\n const tokens = useMemo(() => tokensFor(code, language), [code, language]);\n const html = useMemo(\n () => (showLineNumbers ? null : highlightCode(code, tokens, colors)),\n [code, tokens, colors, showLineNumbers],\n );\n const lines = useMemo(() => {\n if (!showLineNumbers) return null;\n const all = highlightLines(code, tokens, colors);\n // A trailing newline ends the last line; it does not start a new one.\n if (all.length > 1 && code.endsWith(\"\\n\")) all.pop();\n return all;\n }, [code, tokens, colors, showLineNumbers]);\n\n const [copyState, setCopyState] = useState<CopyState>(\"idle\");\n const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n useEffect(() => () => clearTimeout(timer.current), []);\n\n // Latest props at click time, without re-creating the handler.\n const latest = useRef({ code, onCopyCode });\n latest.current = { code, onCopyCode };\n\n const onCopyClick = useCallback(async () => {\n const { code: text, onCopyCode: report } = latest.current;\n // Raw `code`, never the highlighted HTML. `defaultClipboardWriter` throws\n // when `navigator.clipboard` is absent (SSR, insecure context), and\n // `runWriter` turns a throw, rejection or hang into `false`.\n const ok = await runWriter(defaultClipboardWriter, { kind: \"code\", text, html: \"\" });\n setCopyState(ok ? \"copied\" : \"failed\");\n clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopyState(\"idle\"), FEEDBACK_MS);\n report?.({ text, ok });\n }, []);\n\n const labels = {\n copy: copyLabels?.copy ?? \"Copy code\",\n copied: copyLabels?.copied ?? \"Copied\",\n failed: copyLabels?.failed ?? \"Copy failed\",\n };\n const label = copyState === \"copied\" ? labels.copied : copyState === \"failed\" ? labels.failed : labels.copy;\n const hasHeader = title != null && title !== false;\n const floating = copyable && !hasHeader;\n\n const copyButton = copyable ? (\n <Button\n type=\"button\"\n size=\"xs\"\n variant=\"ghost\"\n icon={copyState === \"copied\" ? \"check\" : copyState === \"failed\" ? \"x\" : \"copy\"}\n aria-label={label}\n title={label}\n data-react-fancy-code-block-copy=\"\"\n data-copy-state={copyState}\n onClick={onCopyClick}\n className={cn(\n floating && \"opacity-80 group-hover:opacity-100 focus-visible:opacity-100\",\n )}\n style={floating ? { backgroundColor: colors.background } : undefined}\n >\n {/* Visible feedback; idle is icon-only when floating over the code. */}\n {copyState !== \"idle\" ? label : hasHeader ? \"Copy\" : undefined}\n </Button>\n ) : null;\n\n const gutterWidth = lines ? `${String(lines.length).length}ch` : undefined;\n\n return (\n <div\n ref={ref}\n data-react-fancy-code-block=\"\"\n data-language={language}\n data-wrap={wrap ? \"true\" : \"false\"}\n className={cn(\n \"group relative overflow-hidden rounded-lg border\",\n className,\n )}\n style={{\n backgroundColor: colors.background,\n color: colors.foreground,\n borderColor: colors.gutterBorder,\n // `relative` inline too: the floating button and the live region are\n // absolute, and must not escape a host whose Tailwind lacks the class.\n position: \"relative\",\n ...style,\n }}\n {...rest}\n >\n {hasHeader && (\n <div\n data-react-fancy-code-block-header=\"\"\n className=\"flex items-center justify-between gap-2 border-b py-1 pl-3 pr-1 text-xs\"\n style={{\n backgroundColor: colors.gutterBackground,\n color: colors.gutterForeground,\n borderColor: colors.gutterBorder,\n }}\n >\n <span data-react-fancy-code-block-title=\"\" className=\"min-w-0 truncate font-mono\">\n {title}\n </span>\n {copyButton}\n </div>\n )}\n\n <pre\n data-react-fancy-code-block-pre=\"\"\n className={cn(TEXT_METRICS, \"border-none\", wrap ? \"overflow-x-hidden\" : \"overflow-x-auto\", floating && \"pr-12\")}\n style={{\n whiteSpace: wrap ? \"pre-wrap\" : \"pre\",\n overflowWrap: wrap ? \"break-word\" : \"normal\",\n backgroundColor: \"transparent\",\n color: \"inherit\",\n }}\n >\n {lines ? (\n <code\n data-react-fancy-code-block-code=\"\"\n style={{ display: wrap ? \"block\" : \"inline-block\", minWidth: \"100%\" }}\n >\n {lines.map((lineHtml, i) => (\n <span key={i} data-react-fancy-code-block-line={i + 1} style={{ display: \"flex\" }}>\n <span\n data-react-fancy-code-block-line-number=\"\"\n aria-hidden=\"true\"\n className=\"select-none\"\n style={{\n flex: \"none\",\n width: gutterWidth,\n marginRight: \"1.5ch\",\n textAlign: \"right\",\n color: colors.gutterForeground,\n userSelect: \"none\",\n }}\n >\n {i + 1}\n </span>\n <span\n data-react-fancy-code-block-line-content=\"\"\n style={{ flex: \"1 1 auto\", minWidth: 0 }}\n dangerouslySetInnerHTML={{ __html: lineHtml }}\n />\n </span>\n ))}\n </code>\n ) : (\n <code data-react-fancy-code-block-code=\"\" dangerouslySetInnerHTML={{ __html: html ?? \"\" }} />\n )}\n </pre>\n\n {floating && (\n <div\n data-react-fancy-code-block-actions=\"\"\n className=\"absolute right-2 top-2\"\n style={{ position: \"absolute\", right: \"0.5rem\", top: \"0.5rem\" }}\n >\n {copyButton}\n </div>\n )}\n\n {copyable && (\n <span role=\"status\" aria-live=\"polite\" className=\"sr-only\" style={visuallyHidden}>\n {copyState === \"idle\" ? \"\" : label}\n </span>\n )}\n </div>\n );\n});\n\nCodeBlock.displayName = \"CodeBlock\";\n"]}
|