@particle-academy/react-fancy 6.5.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/chunk-2X54BVXG.js +58 -0
  3. package/dist/chunk-2X54BVXG.js.map +1 -0
  4. package/dist/chunk-5WXY3Q6X.cjs +63 -0
  5. package/dist/chunk-5WXY3Q6X.cjs.map +1 -0
  6. package/dist/{chunk-YRI6OFLY.js → chunk-MI57NAKF.js} +24 -21
  7. package/dist/chunk-MI57NAKF.js.map +1 -0
  8. package/dist/{chunk-QH4YSG7C.cjs → chunk-PM5I5ZHK.cjs} +23 -20
  9. package/dist/chunk-PM5I5ZHK.cjs.map +1 -0
  10. package/dist/{chunk-PVHZO4T3.js → chunk-TFX4LL7V.js} +17 -12
  11. package/dist/chunk-TFX4LL7V.js.map +1 -0
  12. package/dist/{chunk-RE4BYTVM.cjs → chunk-THMFLRDW.cjs} +4 -4
  13. package/dist/{chunk-RE4BYTVM.cjs.map → chunk-THMFLRDW.cjs.map} +1 -1
  14. package/dist/{chunk-YYNUTUJO.js → chunk-VSTUGU7W.js} +3 -3
  15. package/dist/{chunk-YYNUTUJO.js.map → chunk-VSTUGU7W.js.map} +1 -1
  16. package/dist/{chunk-TQL3QW7F.cjs → chunk-YQZB5Z7C.cjs} +16 -11
  17. package/dist/chunk-YQZB5Z7C.cjs.map +1 -0
  18. package/dist/code-block.cjs +3 -3
  19. package/dist/code-block.js +2 -2
  20. package/dist/code-view.cjs +3 -3
  21. package/dist/code-view.js +2 -2
  22. package/dist/editor.cjs +5 -5
  23. package/dist/editor.js +3 -3
  24. package/dist/index.cjs +8 -8
  25. package/dist/index.js +4 -4
  26. package/dist/styles.css +20 -0
  27. package/dist/styles.css.map +1 -1
  28. package/docs/CodeBlock.md +11 -3
  29. package/docs/CodeView.md +7 -3
  30. package/package.json +2 -2
  31. package/dist/chunk-6BB32POL.js +0 -29
  32. package/dist/chunk-6BB32POL.js.map +0 -1
  33. package/dist/chunk-HZUKPYR3.cjs +0 -31
  34. package/dist/chunk-HZUKPYR3.cjs.map +0 -1
  35. package/dist/chunk-PVHZO4T3.js.map +0 -1
  36. package/dist/chunk-QH4YSG7C.cjs.map +0 -1
  37. package/dist/chunk-TQL3QW7F.cjs.map +0 -1
  38. package/dist/chunk-YRI6OFLY.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -11,6 +11,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [6.6.0] - 2026-11-02
15
+
16
+ ### Fixed
17
+
18
+ - **`CodeBlock` and `CodeView` no longer flash light on a dark site.** They picked one palette with a client-only hook, so server-rendered HTML always carried the light colours until hydration. Now the root carries BOTH palettes as CSS variables (`--fc-<slot>-l` / `--fc-<slot>-d`), every colour reads `var(--fc-<slot>, var(--fc-<slot>-l))`, and CSS switches to dark: the root's `dark:[--fc-<slot>:var(--fc-<slot>-d)]` classes (compiled by your Tailwind from the `@source`d dist, so they follow your `dark` variant, class or media) and a zero-specificity `styles.css` rule under `.dark` / `[data-theme="dark"]` for hosts that do not scan the package. Server and client markup are now identical in both themes. Verified in a real browser: dark under `.dark`, under `[data-theme="dark"]`, with only the Tailwind CSS, and with only `styles.css`; light with no CSS at all.
19
+ - **PHP snippets without `<?php` are highlighted.** `<CodeBlock language="php" code={'use Prism\\Prism\\Facades\\Prism;'} />` rendered as one plain span, because PHP files are inline HTML until their open tag. Both components now tokenize with `fancy-file-commons`' snippet mode: no open tag means PHP, and a `?>` switches to HTML. Code that starts with `<?php` highlights exactly as before. Reported by Prism.
20
+
21
+ ### Changed
22
+
23
+ - **Token spans read CSS variables instead of carrying hex colours** (`style="color:var(--fc-keyword,var(--fc-keyword-l))"`), and the root's `background-color` / `color` / `border-color` are `var()` references too. **Consumer action:** none for rendering. If a test asserted a literal colour on a `CodeBlock` / `CodeView` element (e.g. `style.backgroundColor === "rgb(24, 24, 27)"` under `.dark`), assert the `--fc-*` variables instead; jsdom does not resolve `var()`. To recolour a slot, set its variables on the component: `style={{ "--fc-keyword-l": "...", "--fc-keyword-d": "..." }}`.
24
+ - **The internal `useIsDark` hook is removed.** It was never exported. Nothing in either component reads `matchMedia` or the document's class any more.
25
+ - **Requires `@particle-academy/fancy-file-commons` `>=0.5`** (still `<2.0`), the first release with `highlightCodeWithVariables`, `themeVariables` and `tokenize`'s `snippet` option. npm installs it for you. **Consumer action:** none. *(Release step, not yet applied: raise the `dependencies` range from `>=0.4` together with the lockfile once 0.5.0 is on the registry.)*
26
+
14
27
  ## [6.5.0] - 2026-11-02
15
28
 
16
29
  ### Added
@@ -0,0 +1,58 @@
1
+ import { themeVariables, LIGHT_COLORS, DARK_COLORS, themeColorVar } from '@particle-academy/fancy-file-commons';
2
+
3
+ // src/components/CodeView/code-palette.ts
4
+ var CODE_PALETTE_KEYS = [
5
+ "background",
6
+ "foreground",
7
+ "gutterBackground",
8
+ "gutterForeground",
9
+ "gutterBorder",
10
+ "cursorColor",
11
+ "keyword",
12
+ "string",
13
+ "comment",
14
+ "number",
15
+ "operator",
16
+ "function",
17
+ "type",
18
+ "tag",
19
+ "attribute",
20
+ "attributeValue",
21
+ "punctuation",
22
+ "variable"
23
+ ];
24
+ var CODE_PALETTE_DARK_CLASS = [
25
+ "dark:[--fc-background:var(--fc-background-d)]",
26
+ "dark:[--fc-foreground:var(--fc-foreground-d)]",
27
+ "dark:[--fc-gutterBackground:var(--fc-gutterBackground-d)]",
28
+ "dark:[--fc-gutterForeground:var(--fc-gutterForeground-d)]",
29
+ "dark:[--fc-gutterBorder:var(--fc-gutterBorder-d)]",
30
+ "dark:[--fc-cursorColor:var(--fc-cursorColor-d)]",
31
+ "dark:[--fc-keyword:var(--fc-keyword-d)]",
32
+ "dark:[--fc-string:var(--fc-string-d)]",
33
+ "dark:[--fc-comment:var(--fc-comment-d)]",
34
+ "dark:[--fc-number:var(--fc-number-d)]",
35
+ "dark:[--fc-operator:var(--fc-operator-d)]",
36
+ "dark:[--fc-function:var(--fc-function-d)]",
37
+ "dark:[--fc-type:var(--fc-type-d)]",
38
+ "dark:[--fc-tag:var(--fc-tag-d)]",
39
+ "dark:[--fc-attribute:var(--fc-attribute-d)]",
40
+ "dark:[--fc-attributeValue:var(--fc-attributeValue-d)]",
41
+ "dark:[--fc-punctuation:var(--fc-punctuation-d)]",
42
+ "dark:[--fc-variable:var(--fc-variable-d)]"
43
+ ].join(" ");
44
+ var CODE_PALETTE_ATTR = "data-react-fancy-code-palette";
45
+ var ALL_VARIABLES = themeVariables(LIGHT_COLORS, DARK_COLORS);
46
+ var CODE_PALETTE_STYLE = Object.fromEntries(
47
+ CODE_PALETTE_KEYS.flatMap((key) => [
48
+ [`--fc-${key}-l`, ALL_VARIABLES[`--fc-${key}-l`]],
49
+ [`--fc-${key}-d`, ALL_VARIABLES[`--fc-${key}-d`]]
50
+ ])
51
+ );
52
+ function paletteColor(key) {
53
+ return themeColorVar(key);
54
+ }
55
+
56
+ export { CODE_PALETTE_ATTR, CODE_PALETTE_DARK_CLASS, CODE_PALETTE_STYLE, paletteColor };
57
+ //# sourceMappingURL=chunk-2X54BVXG.js.map
58
+ //# sourceMappingURL=chunk-2X54BVXG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/CodeView/code-palette.ts"],"names":[],"mappings":";;;AA+BO,IAAM,iBAAA,GAAoB;AAAA,EAC/B,YAAA;AAAA,EACA,YAAA;AAAA,EACA,kBAAA;AAAA,EACA,kBAAA;AAAA,EACA,cAAA;AAAA,EACA,aAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AAAA,EACA,KAAA;AAAA,EACA,WAAA;AAAA,EACA,gBAAA;AAAA,EACA,aAAA;AAAA,EACA;AACF,CAAA;AAKO,IAAM,uBAAA,GAA0B;AAAA,EACrC,+CAAA;AAAA,EACA,+CAAA;AAAA,EACA,2DAAA;AAAA,EACA,2DAAA;AAAA,EACA,mDAAA;AAAA,EACA,iDAAA;AAAA,EACA,yCAAA;AAAA,EACA,uCAAA;AAAA,EACA,yCAAA;AAAA,EACA,uCAAA;AAAA,EACA,2CAAA;AAAA,EACA,2CAAA;AAAA,EACA,mCAAA;AAAA,EACA,iCAAA;AAAA,EACA,6CAAA;AAAA,EACA,uDAAA;AAAA,EACA,iDAAA;AAAA,EACA;AACF,CAAA,CAAE,KAAK,GAAG;AAGH,IAAM,iBAAA,GAAoB;AAEjC,IAAM,aAAA,GAAgB,cAAA,CAAe,YAAA,EAAc,WAAW,CAAA;AAGvD,IAAM,qBAAoC,MAAA,CAAO,WAAA;AAAA,EACtD,iBAAA,CAAkB,OAAA,CAAQ,CAAC,GAAA,KAAQ;AAAA,IACjC,CAAC,QAAQ,GAAG,CAAA,EAAA,CAAA,EAAM,cAAc,CAAA,KAAA,EAAQ,GAAG,IAAI,CAAC,CAAA;AAAA,IAChD,CAAC,QAAQ,GAAG,CAAA,EAAA,CAAA,EAAM,cAAc,CAAA,KAAA,EAAQ,GAAG,IAAI,CAAC;AAAA,GACjD;AACH;AAGO,SAAS,aAAa,GAAA,EAA6B;AACxD,EAAA,OAAO,cAAc,GAAG,CAAA;AAC1B","file":"chunk-2X54BVXG.js","sourcesContent":["import type { CSSProperties } from \"react\";\nimport {\n themeColorVar,\n themeVariables,\n LIGHT_COLORS,\n DARK_COLORS,\n type ThemeColorKey,\n} from \"@particle-academy/fancy-file-commons\";\n\n// ── Both palettes, switched by CSS ──────────────────────────────────────────\n// CodeBlock and CodeView used to pick ONE palette with a client-only hook, so\n// server-rendered HTML always carried the light colours and a dark site flashed\n// light until hydration. Now the markup is the same in either theme:\n//\n// 1. the root declares `--fc-<key>-l` and `--fc-<key>-d` (both palettes, via\n// fancy-file-commons' `themeVariables`, which allow-lists every value);\n// 2. everything coloured reads `var(--fc-<key>,var(--fc-<key>-l))`, so it is\n// light unless `--fc-<key>` is set;\n// 3. `--fc-<key>` is set to the dark value by EITHER\n// - the `dark:` classes below, compiled by the consumer's Tailwind from\n// react-fancy's dist, so they follow the consumer's own `dark` variant\n// (class strategy, `dark-variant.css`, or the default media query), or\n// - the zero-specificity rule in `styles.css` keyed on\n// `[data-react-fancy-code-palette]` under `.dark` / `[data-theme=\"dark\"]`,\n// for a host whose Tailwind does not scan this package.\n// Together they cover what the old hook followed: `.dark` OR the media\n// query (the latter through Tailwind's default `dark` variant).\n//\n// The class strings MUST stay literal: Tailwind finds them by scanning dist.\n\n/** The palette slots CodeBlock and CodeView paint. */\nexport const CODE_PALETTE_KEYS = [\n \"background\",\n \"foreground\",\n \"gutterBackground\",\n \"gutterForeground\",\n \"gutterBorder\",\n \"cursorColor\",\n \"keyword\",\n \"string\",\n \"comment\",\n \"number\",\n \"operator\",\n \"function\",\n \"type\",\n \"tag\",\n \"attribute\",\n \"attributeValue\",\n \"punctuation\",\n \"variable\",\n] as const satisfies readonly ThemeColorKey[];\n\nexport type CodePaletteKey = (typeof CODE_PALETTE_KEYS)[number];\n\n/** One `dark:` class per key in `CODE_PALETTE_KEYS`; a test keeps them in step. */\nexport const CODE_PALETTE_DARK_CLASS = [\n \"dark:[--fc-background:var(--fc-background-d)]\",\n \"dark:[--fc-foreground:var(--fc-foreground-d)]\",\n \"dark:[--fc-gutterBackground:var(--fc-gutterBackground-d)]\",\n \"dark:[--fc-gutterForeground:var(--fc-gutterForeground-d)]\",\n \"dark:[--fc-gutterBorder:var(--fc-gutterBorder-d)]\",\n \"dark:[--fc-cursorColor:var(--fc-cursorColor-d)]\",\n \"dark:[--fc-keyword:var(--fc-keyword-d)]\",\n \"dark:[--fc-string:var(--fc-string-d)]\",\n \"dark:[--fc-comment:var(--fc-comment-d)]\",\n \"dark:[--fc-number:var(--fc-number-d)]\",\n \"dark:[--fc-operator:var(--fc-operator-d)]\",\n \"dark:[--fc-function:var(--fc-function-d)]\",\n \"dark:[--fc-type:var(--fc-type-d)]\",\n \"dark:[--fc-tag:var(--fc-tag-d)]\",\n \"dark:[--fc-attribute:var(--fc-attribute-d)]\",\n \"dark:[--fc-attributeValue:var(--fc-attributeValue-d)]\",\n \"dark:[--fc-punctuation:var(--fc-punctuation-d)]\",\n \"dark:[--fc-variable:var(--fc-variable-d)]\",\n].join(\" \");\n\n/** Marks the element that declares the palette; styles.css keys its dark rule on it. */\nexport const CODE_PALETTE_ATTR = \"data-react-fancy-code-palette\";\n\nconst ALL_VARIABLES = themeVariables(LIGHT_COLORS, DARK_COLORS);\n\n/** Both palettes as custom properties, for the root's `style`. */\nexport const CODE_PALETTE_STYLE: CSSProperties = Object.fromEntries(\n CODE_PALETTE_KEYS.flatMap((key) => [\n [`--fc-${key}-l`, ALL_VARIABLES[`--fc-${key}-l`]],\n [`--fc-${key}-d`, ALL_VARIABLES[`--fc-${key}-d`]],\n ]),\n) as CSSProperties;\n\n/** The CSS value for palette slot `key`: dark under the dark switch, light otherwise. */\nexport function paletteColor(key: CodePaletteKey): string {\n return themeColorVar(key);\n}\n"]}
@@ -0,0 +1,63 @@
1
+ 'use strict';
2
+
3
+ var fancyFileCommons = require('@particle-academy/fancy-file-commons');
4
+
5
+ // src/components/CodeView/code-palette.ts
6
+ var CODE_PALETTE_KEYS = [
7
+ "background",
8
+ "foreground",
9
+ "gutterBackground",
10
+ "gutterForeground",
11
+ "gutterBorder",
12
+ "cursorColor",
13
+ "keyword",
14
+ "string",
15
+ "comment",
16
+ "number",
17
+ "operator",
18
+ "function",
19
+ "type",
20
+ "tag",
21
+ "attribute",
22
+ "attributeValue",
23
+ "punctuation",
24
+ "variable"
25
+ ];
26
+ var CODE_PALETTE_DARK_CLASS = [
27
+ "dark:[--fc-background:var(--fc-background-d)]",
28
+ "dark:[--fc-foreground:var(--fc-foreground-d)]",
29
+ "dark:[--fc-gutterBackground:var(--fc-gutterBackground-d)]",
30
+ "dark:[--fc-gutterForeground:var(--fc-gutterForeground-d)]",
31
+ "dark:[--fc-gutterBorder:var(--fc-gutterBorder-d)]",
32
+ "dark:[--fc-cursorColor:var(--fc-cursorColor-d)]",
33
+ "dark:[--fc-keyword:var(--fc-keyword-d)]",
34
+ "dark:[--fc-string:var(--fc-string-d)]",
35
+ "dark:[--fc-comment:var(--fc-comment-d)]",
36
+ "dark:[--fc-number:var(--fc-number-d)]",
37
+ "dark:[--fc-operator:var(--fc-operator-d)]",
38
+ "dark:[--fc-function:var(--fc-function-d)]",
39
+ "dark:[--fc-type:var(--fc-type-d)]",
40
+ "dark:[--fc-tag:var(--fc-tag-d)]",
41
+ "dark:[--fc-attribute:var(--fc-attribute-d)]",
42
+ "dark:[--fc-attributeValue:var(--fc-attributeValue-d)]",
43
+ "dark:[--fc-punctuation:var(--fc-punctuation-d)]",
44
+ "dark:[--fc-variable:var(--fc-variable-d)]"
45
+ ].join(" ");
46
+ var CODE_PALETTE_ATTR = "data-react-fancy-code-palette";
47
+ var ALL_VARIABLES = fancyFileCommons.themeVariables(fancyFileCommons.LIGHT_COLORS, fancyFileCommons.DARK_COLORS);
48
+ var CODE_PALETTE_STYLE = Object.fromEntries(
49
+ CODE_PALETTE_KEYS.flatMap((key) => [
50
+ [`--fc-${key}-l`, ALL_VARIABLES[`--fc-${key}-l`]],
51
+ [`--fc-${key}-d`, ALL_VARIABLES[`--fc-${key}-d`]]
52
+ ])
53
+ );
54
+ function paletteColor(key) {
55
+ return fancyFileCommons.themeColorVar(key);
56
+ }
57
+
58
+ exports.CODE_PALETTE_ATTR = CODE_PALETTE_ATTR;
59
+ exports.CODE_PALETTE_DARK_CLASS = CODE_PALETTE_DARK_CLASS;
60
+ exports.CODE_PALETTE_STYLE = CODE_PALETTE_STYLE;
61
+ exports.paletteColor = paletteColor;
62
+ //# sourceMappingURL=chunk-5WXY3Q6X.cjs.map
63
+ //# sourceMappingURL=chunk-5WXY3Q6X.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/CodeView/code-palette.ts"],"names":["themeVariables","LIGHT_COLORS","DARK_COLORS","themeColorVar"],"mappings":";;;;;AA+BO,IAAM,iBAAA,GAAoB;AAAA,EAC/B,YAAA;AAAA,EACA,YAAA;AAAA,EACA,kBAAA;AAAA,EACA,kBAAA;AAAA,EACA,cAAA;AAAA,EACA,aAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AAAA,EACA,KAAA;AAAA,EACA,WAAA;AAAA,EACA,gBAAA;AAAA,EACA,aAAA;AAAA,EACA;AACF,CAAA;AAKO,IAAM,uBAAA,GAA0B;AAAA,EACrC,+CAAA;AAAA,EACA,+CAAA;AAAA,EACA,2DAAA;AAAA,EACA,2DAAA;AAAA,EACA,mDAAA;AAAA,EACA,iDAAA;AAAA,EACA,yCAAA;AAAA,EACA,uCAAA;AAAA,EACA,yCAAA;AAAA,EACA,uCAAA;AAAA,EACA,2CAAA;AAAA,EACA,2CAAA;AAAA,EACA,mCAAA;AAAA,EACA,iCAAA;AAAA,EACA,6CAAA;AAAA,EACA,uDAAA;AAAA,EACA,iDAAA;AAAA,EACA;AACF,CAAA,CAAE,KAAK,GAAG;AAGH,IAAM,iBAAA,GAAoB;AAEjC,IAAM,aAAA,GAAgBA,+BAAA,CAAeC,6BAAA,EAAcC,4BAAW,CAAA;AAGvD,IAAM,qBAAoC,MAAA,CAAO,WAAA;AAAA,EACtD,iBAAA,CAAkB,OAAA,CAAQ,CAAC,GAAA,KAAQ;AAAA,IACjC,CAAC,QAAQ,GAAG,CAAA,EAAA,CAAA,EAAM,cAAc,CAAA,KAAA,EAAQ,GAAG,IAAI,CAAC,CAAA;AAAA,IAChD,CAAC,QAAQ,GAAG,CAAA,EAAA,CAAA,EAAM,cAAc,CAAA,KAAA,EAAQ,GAAG,IAAI,CAAC;AAAA,GACjD;AACH;AAGO,SAAS,aAAa,GAAA,EAA6B;AACxD,EAAA,OAAOC,+BAAc,GAAG,CAAA;AAC1B","file":"chunk-5WXY3Q6X.cjs","sourcesContent":["import type { CSSProperties } from \"react\";\nimport {\n themeColorVar,\n themeVariables,\n LIGHT_COLORS,\n DARK_COLORS,\n type ThemeColorKey,\n} from \"@particle-academy/fancy-file-commons\";\n\n// ── Both palettes, switched by CSS ──────────────────────────────────────────\n// CodeBlock and CodeView used to pick ONE palette with a client-only hook, so\n// server-rendered HTML always carried the light colours and a dark site flashed\n// light until hydration. Now the markup is the same in either theme:\n//\n// 1. the root declares `--fc-<key>-l` and `--fc-<key>-d` (both palettes, via\n// fancy-file-commons' `themeVariables`, which allow-lists every value);\n// 2. everything coloured reads `var(--fc-<key>,var(--fc-<key>-l))`, so it is\n// light unless `--fc-<key>` is set;\n// 3. `--fc-<key>` is set to the dark value by EITHER\n// - the `dark:` classes below, compiled by the consumer's Tailwind from\n// react-fancy's dist, so they follow the consumer's own `dark` variant\n// (class strategy, `dark-variant.css`, or the default media query), or\n// - the zero-specificity rule in `styles.css` keyed on\n// `[data-react-fancy-code-palette]` under `.dark` / `[data-theme=\"dark\"]`,\n// for a host whose Tailwind does not scan this package.\n// Together they cover what the old hook followed: `.dark` OR the media\n// query (the latter through Tailwind's default `dark` variant).\n//\n// The class strings MUST stay literal: Tailwind finds them by scanning dist.\n\n/** The palette slots CodeBlock and CodeView paint. */\nexport const CODE_PALETTE_KEYS = [\n \"background\",\n \"foreground\",\n \"gutterBackground\",\n \"gutterForeground\",\n \"gutterBorder\",\n \"cursorColor\",\n \"keyword\",\n \"string\",\n \"comment\",\n \"number\",\n \"operator\",\n \"function\",\n \"type\",\n \"tag\",\n \"attribute\",\n \"attributeValue\",\n \"punctuation\",\n \"variable\",\n] as const satisfies readonly ThemeColorKey[];\n\nexport type CodePaletteKey = (typeof CODE_PALETTE_KEYS)[number];\n\n/** One `dark:` class per key in `CODE_PALETTE_KEYS`; a test keeps them in step. */\nexport const CODE_PALETTE_DARK_CLASS = [\n \"dark:[--fc-background:var(--fc-background-d)]\",\n \"dark:[--fc-foreground:var(--fc-foreground-d)]\",\n \"dark:[--fc-gutterBackground:var(--fc-gutterBackground-d)]\",\n \"dark:[--fc-gutterForeground:var(--fc-gutterForeground-d)]\",\n \"dark:[--fc-gutterBorder:var(--fc-gutterBorder-d)]\",\n \"dark:[--fc-cursorColor:var(--fc-cursorColor-d)]\",\n \"dark:[--fc-keyword:var(--fc-keyword-d)]\",\n \"dark:[--fc-string:var(--fc-string-d)]\",\n \"dark:[--fc-comment:var(--fc-comment-d)]\",\n \"dark:[--fc-number:var(--fc-number-d)]\",\n \"dark:[--fc-operator:var(--fc-operator-d)]\",\n \"dark:[--fc-function:var(--fc-function-d)]\",\n \"dark:[--fc-type:var(--fc-type-d)]\",\n \"dark:[--fc-tag:var(--fc-tag-d)]\",\n \"dark:[--fc-attribute:var(--fc-attribute-d)]\",\n \"dark:[--fc-attributeValue:var(--fc-attributeValue-d)]\",\n \"dark:[--fc-punctuation:var(--fc-punctuation-d)]\",\n \"dark:[--fc-variable:var(--fc-variable-d)]\",\n].join(\" \");\n\n/** Marks the element that declares the palette; styles.css keys its dark rule on it. */\nexport const CODE_PALETTE_ATTR = \"data-react-fancy-code-palette\";\n\nconst ALL_VARIABLES = themeVariables(LIGHT_COLORS, DARK_COLORS);\n\n/** Both palettes as custom properties, for the root's `style`. */\nexport const CODE_PALETTE_STYLE: CSSProperties = Object.fromEntries(\n CODE_PALETTE_KEYS.flatMap((key) => [\n [`--fc-${key}-l`, ALL_VARIABLES[`--fc-${key}-l`]],\n [`--fc-${key}-d`, ALL_VARIABLES[`--fc-${key}-d`]],\n ]),\n) as CSSProperties;\n\n/** The CSS value for palette slot `key`: dark under the dark switch, light otherwise. */\nexport function paletteColor(key: CodePaletteKey): string {\n return themeColorVar(key);\n}\n"]}
@@ -1,17 +1,17 @@
1
1
  import { runWriter, defaultClipboardWriter } from './chunk-J2BWXCES.js';
2
2
  import { visuallyHidden } from './chunk-I3LAFKJU.js';
3
- import { useIsDark } from './chunk-6BB32POL.js';
3
+ import { paletteColor, CODE_PALETTE_STYLE, CODE_PALETTE_DARK_CLASS, CODE_PALETTE_ATTR } from './chunk-2X54BVXG.js';
4
4
  import { Button } from './chunk-4GPH7HOR.js';
5
5
  import { cn } from './chunk-MMW2JGKZ.js';
6
6
  import { forwardRef, useMemo, useState, useRef, useEffect, useCallback } from 'react';
7
- import { highlightCode, DARK_COLORS, LIGHT_COLORS, tokenize } from '@particle-academy/fancy-file-commons';
7
+ import { highlightCodeWithVariables, tokenize } from '@particle-academy/fancy-file-commons';
8
8
  import { jsx, jsxs } from 'react/jsx-runtime';
9
9
 
10
10
  var FEEDBACK_MS = 2e3;
11
11
  function tokensFor(code, language) {
12
- return tokenize(code, language) ?? [];
12
+ return tokenize(code, language, { snippet: true }) ?? [];
13
13
  }
14
- function highlightLines(code, tokens, colors) {
14
+ function highlightLines(code, tokens) {
15
15
  const out = [];
16
16
  let start = 0;
17
17
  let ti = 0;
@@ -24,7 +24,7 @@ function highlightLines(code, tokens, colors) {
24
24
  const e = Math.min(tokens[j].end, end) - start;
25
25
  if (e > s) clipped.push({ type: tokens[j].type, start: s, end: e });
26
26
  }
27
- out.push(highlightCode(line, clipped, colors));
27
+ out.push(highlightCodeWithVariables(line, clipped));
28
28
  start = end + 1;
29
29
  }
30
30
  return out;
@@ -43,19 +43,17 @@ var CodeBlock = forwardRef(function CodeBlock2({
43
43
  style,
44
44
  ...rest
45
45
  }, ref) {
46
- const isDark = useIsDark();
47
- const colors = isDark ? DARK_COLORS : LIGHT_COLORS;
48
46
  const tokens = useMemo(() => tokensFor(code, language), [code, language]);
49
47
  const html = useMemo(
50
- () => showLineNumbers ? null : highlightCode(code, tokens, colors),
51
- [code, tokens, colors, showLineNumbers]
48
+ () => showLineNumbers ? null : highlightCodeWithVariables(code, tokens),
49
+ [code, tokens, showLineNumbers]
52
50
  );
53
51
  const lines = useMemo(() => {
54
52
  if (!showLineNumbers) return null;
55
- const all = highlightLines(code, tokens, colors);
53
+ const all = highlightLines(code, tokens);
56
54
  if (all.length > 1 && code.endsWith("\n")) all.pop();
57
55
  return all;
58
- }, [code, tokens, colors, showLineNumbers]);
56
+ }, [code, tokens, showLineNumbers]);
59
57
  const [copyState, setCopyState] = useState("idle");
60
58
  const timer = useRef(void 0);
61
59
  useEffect(() => () => clearTimeout(timer.current), []);
@@ -92,7 +90,7 @@ var CodeBlock = forwardRef(function CodeBlock2({
92
90
  className: cn(
93
91
  floating && "opacity-80 group-hover:opacity-100 focus-visible:opacity-100"
94
92
  ),
95
- style: floating ? { backgroundColor: colors.background } : void 0,
93
+ style: floating ? { backgroundColor: paletteColor("background") } : void 0,
96
94
  children: copyState !== "idle" ? label : hasHeader ? "Copy" : void 0
97
95
  }
98
96
  ) : null;
@@ -104,14 +102,19 @@ var CodeBlock = forwardRef(function CodeBlock2({
104
102
  "data-react-fancy-code-block": "",
105
103
  "data-language": language,
106
104
  "data-wrap": wrap ? "true" : "false",
105
+ ...{ [CODE_PALETTE_ATTR]: "" },
107
106
  className: cn(
108
107
  "group relative overflow-hidden rounded-lg border",
108
+ CODE_PALETTE_DARK_CLASS,
109
109
  className
110
110
  ),
111
111
  style: {
112
- backgroundColor: colors.background,
113
- color: colors.foreground,
114
- borderColor: colors.gutterBorder,
112
+ // Both palettes; the dark one applies under the consumer's `dark`
113
+ // variant or a `.dark` / `[data-theme="dark"]` ancestor (code-palette).
114
+ ...CODE_PALETTE_STYLE,
115
+ backgroundColor: paletteColor("background"),
116
+ color: paletteColor("foreground"),
117
+ borderColor: paletteColor("gutterBorder"),
115
118
  // `relative` inline too: the floating button and the live region are
116
119
  // absolute, and must not escape a host whose Tailwind lacks the class.
117
120
  position: "relative",
@@ -125,9 +128,9 @@ var CodeBlock = forwardRef(function CodeBlock2({
125
128
  "data-react-fancy-code-block-header": "",
126
129
  className: "flex items-center justify-between gap-2 border-b py-1 pl-3 pr-1 text-xs",
127
130
  style: {
128
- backgroundColor: colors.gutterBackground,
129
- color: colors.gutterForeground,
130
- borderColor: colors.gutterBorder
131
+ backgroundColor: paletteColor("gutterBackground"),
132
+ color: paletteColor("gutterForeground"),
133
+ borderColor: paletteColor("gutterBorder")
131
134
  },
132
135
  children: [
133
136
  /* @__PURE__ */ jsx("span", { "data-react-fancy-code-block-title": "", className: "min-w-0 truncate font-mono", children: title }),
@@ -163,7 +166,7 @@ var CodeBlock = forwardRef(function CodeBlock2({
163
166
  width: gutterWidth,
164
167
  marginRight: "1.5ch",
165
168
  textAlign: "right",
166
- color: colors.gutterForeground,
169
+ color: paletteColor("gutterForeground"),
167
170
  userSelect: "none"
168
171
  },
169
172
  children: i + 1
@@ -199,5 +202,5 @@ var CodeBlock = forwardRef(function CodeBlock2({
199
202
  CodeBlock.displayName = "CodeBlock";
200
203
 
201
204
  export { CodeBlock };
202
- //# sourceMappingURL=chunk-YRI6OFLY.js.map
203
- //# sourceMappingURL=chunk-YRI6OFLY.js.map
205
+ //# sourceMappingURL=chunk-MI57NAKF.js.map
206
+ //# sourceMappingURL=chunk-MI57NAKF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/CodeBlock/CodeBlock.tsx"],"names":["CodeBlock"],"mappings":"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","file":"chunk-MI57NAKF.js","sourcesContent":["import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n highlightCodeWithVariables,\n tokenize,\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 {\n CODE_PALETTE_ATTR,\n CODE_PALETTE_DARK_CLASS,\n CODE_PALETTE_STYLE,\n paletteColor,\n} from \"../CodeView/code-palette\";\nimport type { CodeBlockProps } from \"./CodeBlock.types\";\n\n/** How long \"Copied\" / \"Copy failed\" stays before the button resets. */\nconst FEEDBACK_MS = 2000;\n\n/**\n * Tokens for any language fancy-file-commons can highlight (aliases included,\n * case-insensitive); none for anything else, which renders as plain text.\n * `snippet: true` because a code block shows documentation snippets: PHP\n * without an opening `<?php` is read as PHP, not inline HTML. `tokenize`\n * returns sorted, non-overlapping, in-bounds tokens, which `highlightLines`\n * relies on.\n */\nfunction tokensFor(code: string, language: string): Token[] {\n return tokenize(code, language, { snippet: true }) ?? [];\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 * `highlightCodeWithVariables` escapes every slice it emits, highlighted or not,\n * and writes no colour value: spans read the palette variables on the root.\n */\nfunction highlightLines(code: string, tokens: Token[]): 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(highlightCodeWithVariables(line, clipped));\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 tokens = useMemo(() => tokensFor(code, language), [code, language]);\n const html = useMemo(\n () => (showLineNumbers ? null : highlightCodeWithVariables(code, tokens)),\n [code, tokens, showLineNumbers],\n );\n const lines = useMemo(() => {\n if (!showLineNumbers) return null;\n const all = highlightLines(code, tokens);\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, 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: paletteColor(\"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 {...{ [CODE_PALETTE_ATTR]: \"\" }}\n className={cn(\n \"group relative overflow-hidden rounded-lg border\",\n CODE_PALETTE_DARK_CLASS,\n className,\n )}\n style={{\n // Both palettes; the dark one applies under the consumer's `dark`\n // variant or a `.dark` / `[data-theme=\"dark\"]` ancestor (code-palette).\n ...CODE_PALETTE_STYLE,\n backgroundColor: paletteColor(\"background\"),\n color: paletteColor(\"foreground\"),\n borderColor: paletteColor(\"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: paletteColor(\"gutterBackground\"),\n color: paletteColor(\"gutterForeground\"),\n borderColor: paletteColor(\"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: paletteColor(\"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"]}
@@ -2,7 +2,7 @@
2
2
 
3
3
  var chunk7ZCNZUZK_cjs = require('./chunk-7ZCNZUZK.cjs');
4
4
  var chunk2C64OVOZ_cjs = require('./chunk-2C64OVOZ.cjs');
5
- var chunkHZUKPYR3_cjs = require('./chunk-HZUKPYR3.cjs');
5
+ var chunk5WXY3Q6X_cjs = require('./chunk-5WXY3Q6X.cjs');
6
6
  var chunkNZHVDWOM_cjs = require('./chunk-NZHVDWOM.cjs');
7
7
  var chunkZU5Q77OT_cjs = require('./chunk-ZU5Q77OT.cjs');
8
8
  var react = require('react');
@@ -11,9 +11,9 @@ var jsxRuntime = require('react/jsx-runtime');
11
11
 
12
12
  var FEEDBACK_MS = 2e3;
13
13
  function tokensFor(code, language) {
14
- return fancyFileCommons.tokenize(code, language) ?? [];
14
+ return fancyFileCommons.tokenize(code, language, { snippet: true }) ?? [];
15
15
  }
16
- function highlightLines(code, tokens, colors) {
16
+ function highlightLines(code, tokens) {
17
17
  const out = [];
18
18
  let start = 0;
19
19
  let ti = 0;
@@ -26,7 +26,7 @@ function highlightLines(code, tokens, colors) {
26
26
  const e = Math.min(tokens[j].end, end) - start;
27
27
  if (e > s) clipped.push({ type: tokens[j].type, start: s, end: e });
28
28
  }
29
- out.push(fancyFileCommons.highlightCode(line, clipped, colors));
29
+ out.push(fancyFileCommons.highlightCodeWithVariables(line, clipped));
30
30
  start = end + 1;
31
31
  }
32
32
  return out;
@@ -45,19 +45,17 @@ var CodeBlock = react.forwardRef(function CodeBlock2({
45
45
  style,
46
46
  ...rest
47
47
  }, ref) {
48
- const isDark = chunkHZUKPYR3_cjs.useIsDark();
49
- const colors = isDark ? fancyFileCommons.DARK_COLORS : fancyFileCommons.LIGHT_COLORS;
50
48
  const tokens = react.useMemo(() => tokensFor(code, language), [code, language]);
51
49
  const html = react.useMemo(
52
- () => showLineNumbers ? null : fancyFileCommons.highlightCode(code, tokens, colors),
53
- [code, tokens, colors, showLineNumbers]
50
+ () => showLineNumbers ? null : fancyFileCommons.highlightCodeWithVariables(code, tokens),
51
+ [code, tokens, showLineNumbers]
54
52
  );
55
53
  const lines = react.useMemo(() => {
56
54
  if (!showLineNumbers) return null;
57
- const all = highlightLines(code, tokens, colors);
55
+ const all = highlightLines(code, tokens);
58
56
  if (all.length > 1 && code.endsWith("\n")) all.pop();
59
57
  return all;
60
- }, [code, tokens, colors, showLineNumbers]);
58
+ }, [code, tokens, showLineNumbers]);
61
59
  const [copyState, setCopyState] = react.useState("idle");
62
60
  const timer = react.useRef(void 0);
63
61
  react.useEffect(() => () => clearTimeout(timer.current), []);
@@ -94,7 +92,7 @@ var CodeBlock = react.forwardRef(function CodeBlock2({
94
92
  className: chunkZU5Q77OT_cjs.cn(
95
93
  floating && "opacity-80 group-hover:opacity-100 focus-visible:opacity-100"
96
94
  ),
97
- style: floating ? { backgroundColor: colors.background } : void 0,
95
+ style: floating ? { backgroundColor: chunk5WXY3Q6X_cjs.paletteColor("background") } : void 0,
98
96
  children: copyState !== "idle" ? label : hasHeader ? "Copy" : void 0
99
97
  }
100
98
  ) : null;
@@ -106,14 +104,19 @@ var CodeBlock = react.forwardRef(function CodeBlock2({
106
104
  "data-react-fancy-code-block": "",
107
105
  "data-language": language,
108
106
  "data-wrap": wrap ? "true" : "false",
107
+ ...{ [chunk5WXY3Q6X_cjs.CODE_PALETTE_ATTR]: "" },
109
108
  className: chunkZU5Q77OT_cjs.cn(
110
109
  "group relative overflow-hidden rounded-lg border",
110
+ chunk5WXY3Q6X_cjs.CODE_PALETTE_DARK_CLASS,
111
111
  className
112
112
  ),
113
113
  style: {
114
- backgroundColor: colors.background,
115
- color: colors.foreground,
116
- borderColor: colors.gutterBorder,
114
+ // Both palettes; the dark one applies under the consumer's `dark`
115
+ // variant or a `.dark` / `[data-theme="dark"]` ancestor (code-palette).
116
+ ...chunk5WXY3Q6X_cjs.CODE_PALETTE_STYLE,
117
+ backgroundColor: chunk5WXY3Q6X_cjs.paletteColor("background"),
118
+ color: chunk5WXY3Q6X_cjs.paletteColor("foreground"),
119
+ borderColor: chunk5WXY3Q6X_cjs.paletteColor("gutterBorder"),
117
120
  // `relative` inline too: the floating button and the live region are
118
121
  // absolute, and must not escape a host whose Tailwind lacks the class.
119
122
  position: "relative",
@@ -127,9 +130,9 @@ var CodeBlock = react.forwardRef(function CodeBlock2({
127
130
  "data-react-fancy-code-block-header": "",
128
131
  className: "flex items-center justify-between gap-2 border-b py-1 pl-3 pr-1 text-xs",
129
132
  style: {
130
- backgroundColor: colors.gutterBackground,
131
- color: colors.gutterForeground,
132
- borderColor: colors.gutterBorder
133
+ backgroundColor: chunk5WXY3Q6X_cjs.paletteColor("gutterBackground"),
134
+ color: chunk5WXY3Q6X_cjs.paletteColor("gutterForeground"),
135
+ borderColor: chunk5WXY3Q6X_cjs.paletteColor("gutterBorder")
133
136
  },
134
137
  children: [
135
138
  /* @__PURE__ */ jsxRuntime.jsx("span", { "data-react-fancy-code-block-title": "", className: "min-w-0 truncate font-mono", children: title }),
@@ -165,7 +168,7 @@ var CodeBlock = react.forwardRef(function CodeBlock2({
165
168
  width: gutterWidth,
166
169
  marginRight: "1.5ch",
167
170
  textAlign: "right",
168
- color: colors.gutterForeground,
171
+ color: chunk5WXY3Q6X_cjs.paletteColor("gutterForeground"),
169
172
  userSelect: "none"
170
173
  },
171
174
  children: i + 1
@@ -201,5 +204,5 @@ var CodeBlock = react.forwardRef(function CodeBlock2({
201
204
  CodeBlock.displayName = "CodeBlock";
202
205
 
203
206
  exports.CodeBlock = CodeBlock;
204
- //# sourceMappingURL=chunk-QH4YSG7C.cjs.map
205
- //# sourceMappingURL=chunk-QH4YSG7C.cjs.map
207
+ //# sourceMappingURL=chunk-PM5I5ZHK.cjs.map
208
+ //# sourceMappingURL=chunk-PM5I5ZHK.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/CodeBlock/CodeBlock.tsx"],"names":["tokenize","highlightCodeWithVariables","forwardRef","CodeBlock","useMemo","useState","useRef","useEffect","useCallback","runWriter","defaultClipboardWriter","jsx","Button","cn","paletteColor","jsxs","CODE_PALETTE_ATTR","CODE_PALETTE_DARK_CLASS","CODE_PALETTE_STYLE","visuallyHidden"],"mappings":"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","file":"chunk-PM5I5ZHK.cjs","sourcesContent":["import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n highlightCodeWithVariables,\n tokenize,\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 {\n CODE_PALETTE_ATTR,\n CODE_PALETTE_DARK_CLASS,\n CODE_PALETTE_STYLE,\n paletteColor,\n} from \"../CodeView/code-palette\";\nimport type { CodeBlockProps } from \"./CodeBlock.types\";\n\n/** How long \"Copied\" / \"Copy failed\" stays before the button resets. */\nconst FEEDBACK_MS = 2000;\n\n/**\n * Tokens for any language fancy-file-commons can highlight (aliases included,\n * case-insensitive); none for anything else, which renders as plain text.\n * `snippet: true` because a code block shows documentation snippets: PHP\n * without an opening `<?php` is read as PHP, not inline HTML. `tokenize`\n * returns sorted, non-overlapping, in-bounds tokens, which `highlightLines`\n * relies on.\n */\nfunction tokensFor(code: string, language: string): Token[] {\n return tokenize(code, language, { snippet: true }) ?? [];\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 * `highlightCodeWithVariables` escapes every slice it emits, highlighted or not,\n * and writes no colour value: spans read the palette variables on the root.\n */\nfunction highlightLines(code: string, tokens: Token[]): 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(highlightCodeWithVariables(line, clipped));\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 tokens = useMemo(() => tokensFor(code, language), [code, language]);\n const html = useMemo(\n () => (showLineNumbers ? null : highlightCodeWithVariables(code, tokens)),\n [code, tokens, showLineNumbers],\n );\n const lines = useMemo(() => {\n if (!showLineNumbers) return null;\n const all = highlightLines(code, tokens);\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, 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: paletteColor(\"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 {...{ [CODE_PALETTE_ATTR]: \"\" }}\n className={cn(\n \"group relative overflow-hidden rounded-lg border\",\n CODE_PALETTE_DARK_CLASS,\n className,\n )}\n style={{\n // Both palettes; the dark one applies under the consumer's `dark`\n // variant or a `.dark` / `[data-theme=\"dark\"]` ancestor (code-palette).\n ...CODE_PALETTE_STYLE,\n backgroundColor: paletteColor(\"background\"),\n color: paletteColor(\"foreground\"),\n borderColor: paletteColor(\"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: paletteColor(\"gutterBackground\"),\n color: paletteColor(\"gutterForeground\"),\n borderColor: paletteColor(\"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: paletteColor(\"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"]}
@@ -1,7 +1,7 @@
1
- import { useIsDark } from './chunk-6BB32POL.js';
1
+ import { CODE_PALETTE_STYLE, paletteColor, CODE_PALETTE_DARK_CLASS, CODE_PALETTE_ATTR } from './chunk-2X54BVXG.js';
2
2
  import { cn } from './chunk-MMW2JGKZ.js';
3
3
  import { useRef, useMemo, useEffect } from 'react';
4
- import { tokenize, highlightCode, DARK_COLORS, LIGHT_COLORS } from '@particle-academy/fancy-file-commons';
4
+ import { tokenize, highlightCodeWithVariables } from '@particle-academy/fancy-file-commons';
5
5
  import { jsx, jsxs } from 'react/jsx-runtime';
6
6
 
7
7
  var TEXT_METRICS = "m-0 p-3 font-mono text-xs leading-relaxed";
@@ -16,12 +16,10 @@ function CodeView({
16
16
  className
17
17
  }) {
18
18
  const textareaRef = useRef(null);
19
- const isDark = useIsDark();
20
- const colors = isDark ? DARK_COLORS : LIGHT_COLORS;
21
19
  const highlighted = useMemo(() => {
22
- const tokens = tokenize(value, language) ?? [];
23
- return highlightCode(value, tokens, colors);
24
- }, [value, language, colors]);
20
+ const tokens = tokenize(value, language, { snippet: true }) ?? [];
21
+ return highlightCodeWithVariables(value, tokens);
22
+ }, [value, language]);
25
23
  useEffect(() => {
26
24
  const el = textareaRef.current;
27
25
  if (!el) return;
@@ -33,8 +31,15 @@ function CodeView({
33
31
  "div",
34
32
  {
35
33
  "data-react-fancy-code-view": "",
36
- className: cn("relative overflow-auto", className),
37
- style: { backgroundColor: colors.background, color: colors.foreground, minHeight, maxHeight },
34
+ ...{ [CODE_PALETTE_ATTR]: "" },
35
+ className: cn("relative overflow-auto", CODE_PALETTE_DARK_CLASS, className),
36
+ style: {
37
+ ...CODE_PALETTE_STYLE,
38
+ backgroundColor: paletteColor("background"),
39
+ color: paletteColor("foreground"),
40
+ minHeight,
41
+ maxHeight
42
+ },
38
43
  children: /* @__PURE__ */ jsxs("div", { className: "relative min-h-full", children: [
39
44
  /* @__PURE__ */ jsx(
40
45
  "pre",
@@ -64,7 +69,7 @@ function CodeView({
64
69
  TEXT_METRICS
65
70
  ),
66
71
  style: {
67
- caretColor: colors.cursorColor,
72
+ caretColor: paletteColor("cursorColor"),
68
73
  overflow: "hidden",
69
74
  whiteSpace: "pre-wrap",
70
75
  overflowWrap: "break-word",
@@ -79,5 +84,5 @@ function CodeView({
79
84
  CodeView.displayName = "CodeView";
80
85
 
81
86
  export { CodeView };
82
- //# sourceMappingURL=chunk-PVHZO4T3.js.map
83
- //# sourceMappingURL=chunk-PVHZO4T3.js.map
87
+ //# sourceMappingURL=chunk-TFX4LL7V.js.map
88
+ //# sourceMappingURL=chunk-TFX4LL7V.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/CodeView/CodeView.tsx"],"names":[],"mappings":"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","file":"chunk-TFX4LL7V.js","sourcesContent":["import { useEffect, useMemo, useRef } from \"react\";\nimport { highlightCodeWithVariables, tokenize } from \"@particle-academy/fancy-file-commons\";\nimport { cn } from \"../../utils/cn\";\nimport {\n CODE_PALETTE_ATTR,\n CODE_PALETTE_DARK_CLASS,\n CODE_PALETTE_STYLE,\n paletteColor,\n} from \"./code-palette\";\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`. Highlights every language fancy-file-commons ships (see\n * `CodeViewProps.language`); anything else renders 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 // Snippet mode: PHP without an opening `<?php` is read as PHP. Colours come\n // from the palette variables on the root, so server and client render the\n // same markup in either theme (see code-palette).\n const highlighted = useMemo(() => {\n const tokens = tokenize(value, language, { snippet: true }) ?? [];\n return highlightCodeWithVariables(value, tokens);\n }, [value, language]);\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 {...{ [CODE_PALETTE_ATTR]: \"\" }}\n className={cn(\"relative overflow-auto\", CODE_PALETTE_DARK_CLASS, className)}\n style={{\n ...CODE_PALETTE_STYLE,\n backgroundColor: paletteColor(\"background\"),\n color: paletteColor(\"foreground\"),\n minHeight,\n maxHeight,\n }}\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: paletteColor(\"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"]}
@@ -3,7 +3,7 @@
3
3
  var chunkVQJ5LJJH_cjs = require('./chunk-VQJ5LJJH.cjs');
4
4
  var chunk4GOOSZEK_cjs = require('./chunk-4GOOSZEK.cjs');
5
5
  var chunkMPCZUZ4O_cjs = require('./chunk-MPCZUZ4O.cjs');
6
- var chunkTQL3QW7F_cjs = require('./chunk-TQL3QW7F.cjs');
6
+ var chunkYQZB5Z7C_cjs = require('./chunk-YQZB5Z7C.cjs');
7
7
  var chunkL3V3BYVS_cjs = require('./chunk-L3V3BYVS.cjs');
8
8
  var chunkNZHVDWOM_cjs = require('./chunk-NZHVDWOM.cjs');
9
9
  var chunkSNHU2AI4_cjs = require('./chunk-SNHU2AI4.cjs');
@@ -163,7 +163,7 @@ function EditorContent({ className, maxHeight: maxHeightProp, sourceClassName })
163
163
  );
164
164
  if (showSource) {
165
165
  return /* @__PURE__ */ jsxRuntime.jsx(
166
- chunkTQL3QW7F_cjs.CodeView,
166
+ chunkYQZB5Z7C_cjs.CodeView,
167
167
  {
168
168
  value: _sourceValue,
169
169
  onChange: _onSourceInput,
@@ -395,5 +395,5 @@ var Editor = Object.assign(EditorRoot, {
395
395
 
396
396
  exports.Editor = Editor;
397
397
  exports.useEditor = useEditor;
398
- //# sourceMappingURL=chunk-RE4BYTVM.cjs.map
399
- //# sourceMappingURL=chunk-RE4BYTVM.cjs.map
398
+ //# sourceMappingURL=chunk-THMFLRDW.cjs.map
399
+ //# sourceMappingURL=chunk-THMFLRDW.cjs.map