@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.
- package/CHANGELOG.md +13 -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-YRI6OFLY.js → chunk-MI57NAKF.js} +24 -21
- package/dist/chunk-MI57NAKF.js.map +1 -0
- package/dist/{chunk-QH4YSG7C.cjs → chunk-PM5I5ZHK.cjs} +23 -20
- package/dist/chunk-PM5I5ZHK.cjs.map +1 -0
- package/dist/{chunk-PVHZO4T3.js → chunk-TFX4LL7V.js} +17 -12
- package/dist/chunk-TFX4LL7V.js.map +1 -0
- package/dist/{chunk-RE4BYTVM.cjs → chunk-THMFLRDW.cjs} +4 -4
- package/dist/{chunk-RE4BYTVM.cjs.map → chunk-THMFLRDW.cjs.map} +1 -1
- package/dist/{chunk-YYNUTUJO.js → chunk-VSTUGU7W.js} +3 -3
- package/dist/{chunk-YYNUTUJO.js.map → chunk-VSTUGU7W.js.map} +1 -1
- package/dist/{chunk-TQL3QW7F.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.js +2 -2
- package/dist/code-view.cjs +3 -3
- 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.js +4 -4
- package/dist/styles.css +20 -0
- package/dist/styles.css.map +1 -1
- package/docs/CodeBlock.md +11 -3
- package/docs/CodeView.md +7 -3
- package/package.json +2 -2
- package/dist/chunk-6BB32POL.js +0 -29
- package/dist/chunk-6BB32POL.js.map +0 -1
- package/dist/chunk-HZUKPYR3.cjs +0 -31
- package/dist/chunk-HZUKPYR3.cjs.map +0 -1
- package/dist/chunk-PVHZO4T3.js.map +0 -1
- package/dist/chunk-QH4YSG7C.cjs.map +0 -1
- package/dist/chunk-TQL3QW7F.cjs.map +0 -1
- 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 {
|
|
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 {
|
|
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
|
|
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(
|
|
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 :
|
|
51
|
-
[code, tokens,
|
|
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
|
|
53
|
+
const all = highlightLines(code, tokens);
|
|
56
54
|
if (all.length > 1 && code.endsWith("\n")) all.pop();
|
|
57
55
|
return all;
|
|
58
|
-
}, [code, tokens,
|
|
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:
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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:
|
|
129
|
-
color:
|
|
130
|
-
borderColor:
|
|
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:
|
|
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-
|
|
203
|
-
//# sourceMappingURL=chunk-
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
53
|
-
[code, tokens,
|
|
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
|
|
55
|
+
const all = highlightLines(code, tokens);
|
|
58
56
|
if (all.length > 1 && code.endsWith("\n")) all.pop();
|
|
59
57
|
return all;
|
|
60
|
-
}, [code, tokens,
|
|
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:
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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:
|
|
131
|
-
color:
|
|
132
|
-
borderColor:
|
|
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:
|
|
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-
|
|
205
|
-
//# sourceMappingURL=chunk-
|
|
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 {
|
|
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,
|
|
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
|
|
24
|
-
}, [value, language
|
|
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
|
-
|
|
37
|
-
|
|
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:
|
|
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-
|
|
83
|
-
//# sourceMappingURL=chunk-
|
|
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":";;;;;;AAcA,IAAM,YAAA,GAAe,2CAAA;AAUd,SAAS,QAAA,CAAS;AAAA,EACvB,KAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA,GAAW,WAAA;AAAA,EACX,QAAA,GAAW,KAAA;AAAA,EACX,WAAA;AAAA,EACA,SAAA,GAAY,GAAA;AAAA,EACZ,SAAA;AAAA,EACA;AACF,CAAA,EAAkB;AAChB,EAAA,MAAM,WAAA,GAAc,OAAmC,IAAI,CAAA;AAI3D,EAAA,MAAM,WAAA,GAAc,QAAQ,MAAM;AAChC,IAAA,MAAM,MAAA,GAAS,SAAS,KAAA,EAAO,QAAA,EAAU,EAAE,OAAA,EAAS,IAAA,EAAM,CAAA,IAAK,EAAC;AAChE,IAAA,OAAO,0BAAA,CAA2B,OAAO,MAAM,CAAA;AAAA,EACjD,CAAA,EAAG,CAAC,KAAA,EAAO,QAAQ,CAAC,CAAA;AAIpB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,KAAK,WAAA,CAAY,OAAA;AACvB,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,EAAA,CAAG,MAAM,MAAA,GAAS,MAAA;AAClB,IAAA,EAAA,CAAG,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG,EAAA,CAAG,YAAY,CAAA,EAAA,CAAA;AAAA,EACtC,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,MAAM,QAAA,GAAW,CAAC,QAAA,IAAY,CAAC,CAAC,QAAA;AAEhC,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,4BAAA,EAA2B,EAAA;AAAA,MAK1B,GAAG,EAAE,CAAC,iBAAiB,GAAG,EAAA,EAAG;AAAA,MAC9B,SAAA,EAAW,EAAA,CAAG,wBAAA,EAA0B,uBAAA,EAAyB,SAAS,CAAA;AAAA,MAC1E,KAAA,EAAO;AAAA,QACL,GAAG,kBAAA;AAAA,QACH,eAAA,EAAiB,aAAa,YAAY,CAAA;AAAA,QAC1C,KAAA,EAAO,aAAa,YAAY,CAAA;AAAA,QAChC,SAAA;AAAA,QACA;AAAA,OACF;AAAA,MAEA,QAAA,kBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qBAAA,EAEb,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,aAAA,EAAY,MAAA;AAAA,YACZ,SAAA,EAAW,EAAA,CAAG,kEAAA,EAAoE,YAAY,CAAA;AAAA,YAC9F,KAAA,EAAO,EAAE,UAAA,EAAY,UAAA,EAAY,cAAc,YAAA,EAAa;AAAA,YAE5D,8BAAC,MAAA,EAAA,EAAK,uBAAA,EAAyB,EAAE,MAAA,EAAQ,WAAA,GAAc,MAAK,EAAG;AAAA;AAAA,SACjE;AAAA,QAGC,WAAA,IAAe,KAAA,CAAM,MAAA,KAAW,CAAA,oBAC/B,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,EAAA,CAAG,sDAAA,EAAwD,YAAY,CAAA,EACpF,QAAA,EAAA,WAAA,EACH,CAAA;AAAA,wBAIF,GAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,GAAA,EAAK,WAAA;AAAA,YACL,kCAAA,EAAiC,EAAA;AAAA,YACjC,KAAA;AAAA,YACA,QAAA,EAAU,WAAW,CAAC,CAAA,KAAM,SAAU,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA,GAAI,MAAA;AAAA,YACxD,UAAU,CAAC,QAAA;AAAA,YACX,UAAA,EAAY,KAAA;AAAA,YACZ,YAAA,EAAa,KAAA;AAAA,YACb,WAAA,EAAY,KAAA;AAAA,YACZ,cAAA,EAAe,KAAA;AAAA,YACf,YAAA,EAAY,WAAA;AAAA,YACZ,SAAA,EAAW,EAAA;AAAA,cACT,4FAAA;AAAA,cACA;AAAA,aACF;AAAA,YACA,KAAA,EAAO;AAAA,cACL,UAAA,EAAY,aAAa,aAAa,CAAA;AAAA,cACtC,QAAA,EAAU,QAAA;AAAA,cACV,UAAA,EAAY,UAAA;AAAA,cACZ,YAAA,EAAc,YAAA;AAAA,cACd;AAAA;AACF;AAAA;AACF,OAAA,EACF;AAAA;AAAA,GACF;AAEJ;AAEA,QAAA,CAAS,WAAA,GAAc,UAAA","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
|
|
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
|
-
|
|
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-
|
|
399
|
-
//# sourceMappingURL=chunk-
|
|
398
|
+
//# sourceMappingURL=chunk-THMFLRDW.cjs.map
|
|
399
|
+
//# sourceMappingURL=chunk-THMFLRDW.cjs.map
|