@texuli/blocks 0.0.0-stage → 0.1.0-next.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/LICENSE +21 -0
- package/README.md +77 -2
- package/dist/code/CodeBlockView.svelte +135 -0
- package/dist/code/CodeBlockView.svelte.d.ts +5 -0
- package/dist/code/check.d.ts +15 -0
- package/dist/code/check.js +14 -0
- package/dist/code/code.css +201 -0
- package/dist/code/code.css.d.ts +1 -0
- package/dist/code/engine-entry.d.ts +10 -0
- package/dist/code/engine-entry.js +10 -0
- package/dist/code/engine.d.ts +18 -0
- package/dist/code/engine.js +18 -0
- package/dist/code/family-engine.d.ts +23 -0
- package/dist/code/family-engine.js +4 -0
- package/dist/code/family.d.ts +7 -0
- package/dist/code/family.js +5 -0
- package/dist/code/grammars.d.ts +12 -0
- package/dist/code/grammars.js +83 -0
- package/dist/code/highlight.d.ts +15 -0
- package/dist/code/highlight.js +53 -0
- package/dist/code/index.d.ts +13 -0
- package/dist/code/index.js +12 -0
- package/dist/code/labels.d.ts +27 -0
- package/dist/code/labels.js +24 -0
- package/dist/code/languages/bash.d.ts +3 -0
- package/dist/code/languages/bash.js +48 -0
- package/dist/code/languages/css.d.ts +10 -0
- package/dist/code/languages/css.js +67 -0
- package/dist/code/languages/html.d.ts +6 -0
- package/dist/code/languages/html.js +35 -0
- package/dist/code/languages/javascript.d.ts +3 -0
- package/dist/code/languages/javascript.js +113 -0
- package/dist/code/languages/json.d.ts +3 -0
- package/dist/code/languages/json.js +24 -0
- package/dist/code/languages/python.d.ts +3 -0
- package/dist/code/languages/python.js +35 -0
- package/dist/code/languages/shared.d.ts +22 -0
- package/dist/code/languages/shared.js +37 -0
- package/dist/code/languages/sql.d.ts +3 -0
- package/dist/code/languages/sql.js +32 -0
- package/dist/code/languages/yaml.d.ts +7 -0
- package/dist/code/languages/yaml.js +36 -0
- package/dist/code/renderers.d.ts +5 -0
- package/dist/code/renderers.js +5 -0
- package/dist/code/token-styles.d.ts +25 -0
- package/dist/code/token-styles.js +51 -0
- package/dist/code/tokenize-code.d.ts +17 -0
- package/dist/code/tokenize-code.js +34 -0
- package/dist/code/tokenizer.d.ts +76 -0
- package/dist/code/tokenizer.js +171 -0
- package/dist/core/catalog/entries.d.ts +10 -0
- package/dist/core/catalog/entries.js +589 -0
- package/dist/core/catalog/index.d.ts +4 -0
- package/dist/core/catalog/index.js +2 -0
- package/dist/core/catalog/types.d.ts +36 -0
- package/dist/core/catalog/types.js +1 -0
- package/dist/core/catalog/validate.d.ts +15 -0
- package/dist/core/catalog/validate.js +46 -0
- package/dist/core/formula/evaluate.d.ts +14 -0
- package/dist/core/formula/evaluate.js +181 -0
- package/dist/core/formula/index.d.ts +8 -0
- package/dist/core/formula/index.js +6 -0
- package/dist/core/formula/parse.d.ts +19 -0
- package/dist/core/formula/parse.js +452 -0
- package/dist/core/formula/types.d.ts +76 -0
- package/dist/core/formula/types.js +7 -0
- package/dist/core/index.d.ts +14 -0
- package/dist/core/index.js +13 -0
- package/dist/core/limits.d.ts +27 -0
- package/dist/core/limits.js +26 -0
- package/dist/core/safety.d.ts +30 -0
- package/dist/core/safety.js +110 -0
- package/dist/core/schema/blocks.d.ts +138 -0
- package/dist/core/schema/blocks.js +181 -0
- package/dist/core/schema/code-family.d.ts +4 -0
- package/dist/core/schema/code-family.js +75 -0
- package/dist/core/schema/line-spec.d.ts +7 -0
- package/dist/core/schema/line-spec.js +19 -0
- package/dist/core/schema/media-family.d.ts +4 -0
- package/dist/core/schema/media-family.js +260 -0
- package/dist/core/schema/parse-family.d.ts +14 -0
- package/dist/core/schema/parse-family.js +37 -0
- package/dist/core/schema/primitives.d.ts +73 -0
- package/dist/core/schema/primitives.js +140 -0
- package/dist/core/schema/text-family.d.ts +4 -0
- package/dist/core/schema/text-family.js +121 -0
- package/dist/core/schema/vocabulary.d.ts +20 -0
- package/dist/core/schema/vocabulary.js +33 -0
- package/dist/core/text.d.ts +31 -0
- package/dist/core/text.js +139 -0
- package/dist/core/untrusted.d.ts +20 -0
- package/dist/core/untrusted.js +377 -0
- package/dist/css-modules.d.ts +1 -0
- package/dist/css.d.ts +1 -0
- package/dist/data/ChartBlockView.svelte +77 -0
- package/dist/data/ChartBlockView.svelte.d.ts +10 -0
- package/dist/data/DataBlockView.svelte +109 -0
- package/dist/data/DataBlockView.svelte.d.ts +5 -0
- package/dist/data/check.d.ts +10 -0
- package/dist/data/check.js +9 -0
- package/dist/data/engine.d.ts +18 -0
- package/dist/data/engine.js +10 -0
- package/dist/data/family.d.ts +6 -0
- package/dist/data/family.js +4 -0
- package/dist/data/index.d.ts +9 -0
- package/dist/data/index.js +8 -0
- package/dist/data/labels.d.ts +8 -0
- package/dist/data/labels.js +8 -0
- package/dist/data/renderers.d.ts +2 -0
- package/dist/data/renderers.js +2 -0
- package/dist/data/schema.d.ts +84 -0
- package/dist/data/schema.js +385 -0
- package/dist/diagram/BoardDiagram.svelte +48 -0
- package/dist/diagram/BoardDiagram.svelte.d.ts +8 -0
- package/dist/diagram/DiagramFamily.svelte +46 -0
- package/dist/diagram/DiagramFamily.svelte.d.ts +10 -0
- package/dist/diagram/FlowDiagram.svelte +108 -0
- package/dist/diagram/FlowDiagram.svelte.d.ts +8 -0
- package/dist/diagram/TerminalDiagram.svelte +38 -0
- package/dist/diagram/TerminalDiagram.svelte.d.ts +8 -0
- package/dist/diagram/TreeDiagram.svelte +149 -0
- package/dist/diagram/TreeDiagram.svelte.d.ts +8 -0
- package/dist/diagram/diagram.css +287 -0
- package/dist/diagram/diagram.css.d.ts +1 -0
- package/dist/diagram/engine.d.ts +16 -0
- package/dist/diagram/engine.js +14 -0
- package/dist/diagram/family.d.ts +7 -0
- package/dist/diagram/family.js +5 -0
- package/dist/diagram/index.d.ts +10 -0
- package/dist/diagram/index.js +9 -0
- package/dist/diagram/layout.d.ts +26 -0
- package/dist/diagram/layout.js +165 -0
- package/dist/diagram/schema.d.ts +74 -0
- package/dist/diagram/schema.js +358 -0
- package/dist/diagram/tones.d.ts +6 -0
- package/dist/diagram/tones.js +16 -0
- package/dist/embed/EmbedApp.svelte +138 -0
- package/dist/embed/EmbedApp.svelte.d.ts +17 -0
- package/dist/embed/EmbedBlockView.svelte +32 -0
- package/dist/embed/EmbedBlockView.svelte.d.ts +5 -0
- package/dist/embed/LinkPreview.svelte +48 -0
- package/dist/embed/LinkPreview.svelte.d.ts +10 -0
- package/dist/embed/engine.d.ts +30 -0
- package/dist/embed/engine.js +17 -0
- package/dist/embed/family.d.ts +7 -0
- package/dist/embed/family.js +5 -0
- package/dist/embed/index.d.ts +5 -0
- package/dist/embed/index.js +3 -0
- package/dist/embed/renderers.d.ts +3 -0
- package/dist/embed/renderers.js +3 -0
- package/dist/embed/sandbox.d.ts +86 -0
- package/dist/embed/sandbox.js +114 -0
- package/dist/embed/schema.d.ts +30 -0
- package/dist/embed/schema.js +217 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +15 -0
- package/dist/interactive/CalculatorBlock.svelte +61 -0
- package/dist/interactive/CalculatorBlock.svelte.d.ts +10 -0
- package/dist/interactive/ChecklistBlock.svelte +44 -0
- package/dist/interactive/ChecklistBlock.svelte.d.ts +10 -0
- package/dist/interactive/InteractiveBlockView.svelte +31 -0
- package/dist/interactive/InteractiveBlockView.svelte.d.ts +5 -0
- package/dist/interactive/PollBlock.svelte +111 -0
- package/dist/interactive/PollBlock.svelte.d.ts +10 -0
- package/dist/interactive/QuizBlock.svelte +75 -0
- package/dist/interactive/QuizBlock.svelte.d.ts +10 -0
- package/dist/interactive/check.d.ts +10 -0
- package/dist/interactive/check.js +9 -0
- package/dist/interactive/engine.d.ts +19 -0
- package/dist/interactive/engine.js +12 -0
- package/dist/interactive/family.d.ts +6 -0
- package/dist/interactive/family.js +4 -0
- package/dist/interactive/index.d.ts +13 -0
- package/dist/interactive/index.js +13 -0
- package/dist/interactive/interactive.css +97 -0
- package/dist/interactive/interactive.css.d.ts +1 -0
- package/dist/interactive/labels.d.ts +16 -0
- package/dist/interactive/labels.js +16 -0
- package/dist/interactive/renderers.d.ts +9 -0
- package/dist/interactive/renderers.js +9 -0
- package/dist/interactive/schema.d.ts +76 -0
- package/dist/interactive/schema.js +324 -0
- package/dist/map/MapBlockView.svelte +465 -0
- package/dist/map/MapBlockView.svelte.d.ts +5 -0
- package/dist/map/check.d.ts +11 -0
- package/dist/map/check.js +6 -0
- package/dist/map/engine.d.ts +19 -0
- package/dist/map/engine.js +8 -0
- package/dist/map/family.d.ts +7 -0
- package/dist/map/family.js +5 -0
- package/dist/map/index.d.ts +11 -0
- package/dist/map/index.js +10 -0
- package/dist/map/labels.d.ts +27 -0
- package/dist/map/labels.js +21 -0
- package/dist/map/renderers.d.ts +2 -0
- package/dist/map/renderers.js +2 -0
- package/dist/map/schema.d.ts +41 -0
- package/dist/map/schema.js +176 -0
- package/dist/map/view-model.d.ts +55 -0
- package/dist/map/view-model.js +168 -0
- package/dist/media/MediaBlockView.svelte +258 -0
- package/dist/media/MediaBlockView.svelte.d.ts +5 -0
- package/dist/media/check.d.ts +14 -0
- package/dist/media/check.js +13 -0
- package/dist/media/engine.d.ts +18 -0
- package/dist/media/engine.js +10 -0
- package/dist/media/family.d.ts +7 -0
- package/dist/media/family.js +5 -0
- package/dist/media/index.d.ts +11 -0
- package/dist/media/index.js +10 -0
- package/dist/media/labels.d.ts +7 -0
- package/dist/media/labels.js +12 -0
- package/dist/media/renderers.d.ts +5 -0
- package/dist/media/renderers.js +5 -0
- package/dist/media/resolve.d.ts +6 -0
- package/dist/media/resolve.js +15 -0
- package/dist/structure/NestedBlocks.svelte +65 -0
- package/dist/structure/NestedBlocks.svelte.d.ts +11 -0
- package/dist/structure/SafeBody.svelte +13 -0
- package/dist/structure/SafeBody.svelte.d.ts +7 -0
- package/dist/structure/StructureFamily.svelte +157 -0
- package/dist/structure/StructureFamily.svelte.d.ts +5 -0
- package/dist/structure/check.d.ts +14 -0
- package/dist/structure/check.js +27 -0
- package/dist/structure/engine.d.ts +18 -0
- package/dist/structure/engine.js +18 -0
- package/dist/structure/family.d.ts +7 -0
- package/dist/structure/family.js +5 -0
- package/dist/structure/index.d.ts +11 -0
- package/dist/structure/index.js +10 -0
- package/dist/structure/labels.d.ts +11 -0
- package/dist/structure/labels.js +11 -0
- package/dist/structure/renderers.d.ts +5 -0
- package/dist/structure/renderers.js +5 -0
- package/dist/structure/schema.d.ts +71 -0
- package/dist/structure/schema.js +327 -0
- package/dist/structure/structure.css +182 -0
- package/dist/structure/structure.css.d.ts +1 -0
- package/dist/text/RichText.svelte +169 -0
- package/dist/text/RichText.svelte.d.ts +13 -0
- package/dist/text/TextBlockView.svelte +177 -0
- package/dist/text/TextBlockView.svelte.d.ts +5 -0
- package/dist/text/check.d.ts +14 -0
- package/dist/text/check.js +13 -0
- package/dist/text/engine.d.ts +17 -0
- package/dist/text/engine.js +10 -0
- package/dist/text/family.d.ts +7 -0
- package/dist/text/family.js +5 -0
- package/dist/text/index.d.ts +12 -0
- package/dist/text/index.js +11 -0
- package/dist/text/labels.d.ts +22 -0
- package/dist/text/labels.js +29 -0
- package/dist/text/markdown.d.ts +80 -0
- package/dist/text/markdown.js +361 -0
- package/dist/text/renderers.d.ts +6 -0
- package/dist/text/renderers.js +6 -0
- package/package.json +167 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dailybot, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,78 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @texuli/blocks
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Content blocks: the closed content vocabulary for pages, documents and slides.
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
**Core engine (T-blocks-core):** schemas, limits, untrusted-input guard, closed
|
|
8
|
+
formula AST, text helpers and catalog metadata. UI family renderers land in
|
|
9
|
+
later PLAN_001 tasks.
|
|
10
|
+
|
|
11
|
+
## Core API (`@texuli/blocks` / `@texuli/blocks/core`)
|
|
12
|
+
|
|
13
|
+
| Area | Exports |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| Limits | `BLOCK_LIMITS` |
|
|
16
|
+
| Safety | `isUnsafeTextCharacter`, `classifyMediaSource`, `hasUnsafeText` |
|
|
17
|
+
| Schemas | `safeParseBlocks`, `safeParseBlock`, `BLOCK_TYPES`, block types |
|
|
18
|
+
| Untrusted | `parseUntrustedBlocks` |
|
|
19
|
+
| Formula | `parseFormula`, `evaluateFormula`, `formatFormulaValue`, `FORMULA_LIMITS` |
|
|
20
|
+
| Catalog | `BLOCK_CATALOG`, `getCatalogEntry`, `validateBlocksForAuthoring` |
|
|
21
|
+
| Text | `walkBlocks`, `blockText`, `headingOutline`, `slugifyHeading` |
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import {
|
|
25
|
+
parseUntrustedBlocks,
|
|
26
|
+
parseFormula,
|
|
27
|
+
evaluateFormula,
|
|
28
|
+
BLOCK_CATALOG,
|
|
29
|
+
} from '@texuli/blocks';
|
|
30
|
+
|
|
31
|
+
const loaded = parseUntrustedBlocks(agentJson);
|
|
32
|
+
if (loaded.success) {
|
|
33
|
+
// loaded.blocks is schema-valid
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const formula = parseFormula({
|
|
37
|
+
op: 'mul',
|
|
38
|
+
left: { op: 'var', name: 'seats' },
|
|
39
|
+
right: { op: 'num', value: 12 },
|
|
40
|
+
});
|
|
41
|
+
if (formula.success) evaluateFormula(formula.formula, { seats: 3 }); // 36
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Core block types (this task)
|
|
45
|
+
|
|
46
|
+
`heading`, `text`, `list`, `quote`, `callout`, `divider`, `code`, `cta`,
|
|
47
|
+
`columns`. Catalog entries include `useWhen`, `dontUseWhen`, `limits`, `a11y`,
|
|
48
|
+
`exampleMinimal` and `exampleRich`.
|
|
49
|
+
|
|
50
|
+
### Rules
|
|
51
|
+
|
|
52
|
+
- No zod: owned validators only (runtime allowlist).
|
|
53
|
+
- No HTML sinks; CTA `href` uses `@texuli/core` `safeHref` (`untrusted` tier).
|
|
54
|
+
- Formulas are closed JSON ASTs — never executed as code.
|
|
55
|
+
- Trojan Source / bidi controls are stripped or refused.
|
|
56
|
+
- Fictional example data only (`example.com`).
|
|
57
|
+
|
|
58
|
+
## Layout
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
packages/blocks/
|
|
62
|
+
├── src/
|
|
63
|
+
│ ├── index.ts re-exports core
|
|
64
|
+
│ └── core/ engine (this task)
|
|
65
|
+
│ ├── limits.ts, safety.ts, untrusted.ts, text.ts
|
|
66
|
+
│ ├── schema/ vocabulary, primitives, blocks
|
|
67
|
+
│ ├── formula/ parse + evaluate
|
|
68
|
+
│ └── catalog/ entries + authoring validate
|
|
69
|
+
└── tests/core.test.ts
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Testing
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pnpm vitest run packages/blocks/tests/core.test.ts
|
|
76
|
+
pnpm --filter @texuli/blocks typecheck
|
|
77
|
+
npx biome check packages/blocks
|
|
78
|
+
```
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onDestroy } from 'svelte';
|
|
3
|
+
import { copyText } from 'texuli/kbd-code';
|
|
4
|
+
import { checkCodeBlock } from './check.js';
|
|
5
|
+
import type { CodeBlockViewProps } from './family.js';
|
|
6
|
+
import { loadCodeGrammar, resolveCodeLanguage } from './grammars.js';
|
|
7
|
+
import { resolveHighlightSteps } from './highlight.js';
|
|
8
|
+
import { fillCodeLabel, resolveCodeBlockLabels } from './labels.js';
|
|
9
|
+
import { activeHighlightIndex, codeTokenClass } from './token-styles.js';
|
|
10
|
+
import { type CodeToken, type Grammar, splitTokenLines, tokenize } from './tokenizer.js';
|
|
11
|
+
import './code.css';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The code family renderer. It takes untrusted data and renders only what
|
|
15
|
+
* `checkCodeBlock` accepts; anything else becomes an inert notice. Tokens are
|
|
16
|
+
* printed as text in spans colored by theme roles, never as HTML. The grammar
|
|
17
|
+
* of the block's language loads on demand; until it arrives (and for `text`
|
|
18
|
+
* or unknown languages) the same text shows unhighlighted.
|
|
19
|
+
*/
|
|
20
|
+
let {
|
|
21
|
+
block,
|
|
22
|
+
labels,
|
|
23
|
+
oninvalid,
|
|
24
|
+
lineNumbers,
|
|
25
|
+
highlights,
|
|
26
|
+
hostStep,
|
|
27
|
+
step,
|
|
28
|
+
copyable = true,
|
|
29
|
+
}: CodeBlockViewProps = $props();
|
|
30
|
+
|
|
31
|
+
const check = $derived(checkCodeBlock(block));
|
|
32
|
+
const text = $derived(resolveCodeBlockLabels(labels));
|
|
33
|
+
const uid = $props.id();
|
|
34
|
+
const nameId = `tx-code-name-${uid}`;
|
|
35
|
+
|
|
36
|
+
$effect(() => {
|
|
37
|
+
if (!check.ok) oninvalid?.(check.issues);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const code = $derived(check.ok ? check.block.code : '');
|
|
41
|
+
const lang = $derived(check.ok ? check.block.lang : undefined);
|
|
42
|
+
|
|
43
|
+
let loaded = $state<{ lang: string | undefined; grammar: Grammar } | null>(null);
|
|
44
|
+
$effect(() => {
|
|
45
|
+
const wanted = lang;
|
|
46
|
+
let current = true;
|
|
47
|
+
void loadCodeGrammar(wanted).then((grammar) => {
|
|
48
|
+
if (current) loaded = grammar ? { lang: wanted, grammar } : null;
|
|
49
|
+
});
|
|
50
|
+
return () => {
|
|
51
|
+
current = false;
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const tokenized = $derived(loaded !== null && loaded.lang === lang);
|
|
56
|
+
const tokens = $derived.by((): CodeToken[] => {
|
|
57
|
+
if (loaded && loaded.lang === lang) return tokenize(code, loaded.grammar);
|
|
58
|
+
return code === '' ? [] : [{ kind: 'plain', text: code }];
|
|
59
|
+
});
|
|
60
|
+
const lines = $derived(splitTokenLines(tokens));
|
|
61
|
+
// Block data sets highlights and line numbers; props override them.
|
|
62
|
+
const highlightSteps = $derived(
|
|
63
|
+
highlights ?? (check.ok ? check.block.highlights : undefined) ?? []
|
|
64
|
+
);
|
|
65
|
+
const numbered = $derived(lineNumbers ?? (check.ok ? check.block.lineNumbers : undefined) ?? false);
|
|
66
|
+
const steps = $derived(resolveHighlightSteps(highlightSteps, lines.length));
|
|
67
|
+
const active = $derived(
|
|
68
|
+
activeHighlightIndex(steps.length, step, hostStep, check.ok ? (check.block.step ?? 0) : 0)
|
|
69
|
+
);
|
|
70
|
+
const marked = $derived(new Set(active >= 0 ? steps[active] : []));
|
|
71
|
+
const name = $derived(
|
|
72
|
+
check.ok ? (check.block.filename ?? check.block.lang?.toUpperCase() ?? text.code) : text.code
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
let status = $state<'idle' | 'copying' | 'copied' | 'failed'>('idle');
|
|
76
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
77
|
+
onDestroy(() => clearTimeout(timer));
|
|
78
|
+
|
|
79
|
+
async function copy(): Promise<void> {
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
status = 'copying';
|
|
82
|
+
status = (await copyText(code)) ? 'copied' : 'failed';
|
|
83
|
+
timer = setTimeout(() => {
|
|
84
|
+
status = 'idle';
|
|
85
|
+
}, 2000);
|
|
86
|
+
}
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
{#if !check.ok}
|
|
90
|
+
<p class="tx-block tx-block-invalid" role="note" data-tx-block="invalid">{text.invalid}</p>
|
|
91
|
+
{:else}
|
|
92
|
+
<figure
|
|
93
|
+
class="tx-block tx-code"
|
|
94
|
+
data-tx-block="code"
|
|
95
|
+
data-lang={lang}
|
|
96
|
+
data-grammar={lang === undefined ? undefined : (resolveCodeLanguage(lang) ?? 'none')}
|
|
97
|
+
data-tokenized={tokenized || undefined}
|
|
98
|
+
data-highlight-step={active >= 0 ? active : undefined}
|
|
99
|
+
data-tone={check.block.tone}
|
|
100
|
+
>
|
|
101
|
+
<div class="tx-code__bar">
|
|
102
|
+
<span id={nameId} class="tx-code__name">{name}</span>
|
|
103
|
+
{#if copyable}
|
|
104
|
+
<button
|
|
105
|
+
type="button"
|
|
106
|
+
class="tx-code__copy"
|
|
107
|
+
data-state={status}
|
|
108
|
+
aria-label={fillCodeLabel(text.copyName, name)}
|
|
109
|
+
aria-busy={status === 'copying' || undefined}
|
|
110
|
+
disabled={status === 'copying'}
|
|
111
|
+
onclick={() => copy()}
|
|
112
|
+
>
|
|
113
|
+
<svg class="tx-code__icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
|
114
|
+
{#if status === 'copied'}
|
|
115
|
+
<path d="M20 6 9 17l-5-5" />
|
|
116
|
+
{:else if status === 'failed'}
|
|
117
|
+
<path d="M12 8v5m0 4h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0" />
|
|
118
|
+
{:else}
|
|
119
|
+
<rect x="9" y="9" width="13" height="13" rx="2" /><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
120
|
+
{/if}
|
|
121
|
+
</svg>
|
|
122
|
+
<span aria-hidden="true">{status === 'copied' ? text.copied : status === 'failed' ? text.copyFailed : text.copy}</span>
|
|
123
|
+
</button>
|
|
124
|
+
{/if}
|
|
125
|
+
</div>
|
|
126
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
127
|
+
<!-- Long lines scroll, so keyboard users must reach them (WCAG 2.1.1); a focusable scroll area needs a role to carry its name. -->
|
|
128
|
+
<!-- Each line ends with a real newline except the last, so the text equals the source; line numbers are CSS content, never text. Code reads left to right even on RTL pages. -->
|
|
129
|
+
<pre class="tx-code__pre" class:tx-code__pre--numbered={numbered} role="region" tabindex="0" dir="ltr" aria-labelledby={nameId}><code class="tx-code__code">{#each lines as line, index (index)}{@const number = index + 1}<svelte:element this={marked.has(number) ? 'mark' : 'span'} class="tx-code__line" data-line={number} data-highlighted={marked.has(number) || undefined}>{#each line as token, t (t)}<span class={codeTokenClass(token.kind)}>{token.text}</span>{/each}{index < lines.length - 1 ? '\n' : ''}</svelte:element>{/each}</code></pre>
|
|
130
|
+
{#if check.block.caption}
|
|
131
|
+
<figcaption class="tx-code__caption">{check.block.caption}</figcaption>
|
|
132
|
+
{/if}
|
|
133
|
+
<span class="tx-code__sr-only" aria-live="polite">{status === 'copied' ? text.copiedAnnouncement : status === 'failed' ? text.copyFailedAnnouncement : ''}</span>
|
|
134
|
+
</figure>
|
|
135
|
+
{/if}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/// <reference path="../css-modules.d.ts" />
|
|
2
|
+
import type { CodeBlockViewProps } from './family.js';
|
|
3
|
+
declare const CodeBlockView: import("svelte").Component<CodeBlockViewProps, {}, "">;
|
|
4
|
+
type CodeBlockView = ReturnType<typeof CodeBlockView>;
|
|
5
|
+
export default CodeBlockView;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FieldIssue } from '../core/schema/primitives.js';
|
|
2
|
+
import type { CodeFamilyBlock } from './family-engine.js';
|
|
3
|
+
export type CodeBlockCheck = {
|
|
4
|
+
ok: true;
|
|
5
|
+
block: CodeFamilyBlock;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
issues: FieldIssue[];
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* The only way data reaches the code renderer: untrusted input runs through
|
|
12
|
+
* the block schemas (length, line count, language token, no bidi controls),
|
|
13
|
+
* then must be a code block.
|
|
14
|
+
*/
|
|
15
|
+
export declare function checkCodeBlock(value: unknown): CodeBlockCheck;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { parseCodeFamilyBlock } from "../core/schema/code-family.js";
|
|
2
|
+
import { parseFamilyBlock } from "../core/schema/parse-family.js";
|
|
3
|
+
/**
|
|
4
|
+
* The only way data reaches the code renderer: untrusted input runs through
|
|
5
|
+
* the block schemas (length, line count, language token, no bidi controls),
|
|
6
|
+
* then must be a code block.
|
|
7
|
+
*/
|
|
8
|
+
export function checkCodeBlock(value) {
|
|
9
|
+
// Only this family's parser: the renderer does not bundle every family's rules.
|
|
10
|
+
const parsed = parseFamilyBlock(value, [parseCodeFamilyBlock], 'code');
|
|
11
|
+
if (!parsed.success)
|
|
12
|
+
return { ok: false, issues: parsed.issues };
|
|
13
|
+
return { ok: true, block: parsed.data };
|
|
14
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code block — header bar, copy button, scroller, lines, highlights and token
|
|
3
|
+
* colors. Token colors are theme roles that reach AA as text on `surface` and
|
|
4
|
+
* on `muted` (highlighted lines) in every library theme; keywords and tags are
|
|
5
|
+
* bold and comments italic, so kinds differ without color too.
|
|
6
|
+
* @layer tx.components so consumer utilities can override.
|
|
7
|
+
*/
|
|
8
|
+
@layer tx.components {
|
|
9
|
+
.tx-code {
|
|
10
|
+
/* In a grid or flex parent, long lines scroll inside instead of widening the page. */
|
|
11
|
+
min-inline-size: 0;
|
|
12
|
+
max-inline-size: 100%;
|
|
13
|
+
margin: 0;
|
|
14
|
+
border: var(--tx-border-width, 1px) solid var(--tx-border, currentColor);
|
|
15
|
+
border-radius: var(--tx-radius, 0.5rem);
|
|
16
|
+
background-color: var(--tx-surface, Canvas);
|
|
17
|
+
color: var(--tx-surface-foreground, CanvasText);
|
|
18
|
+
overflow: clip;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.tx-code__bar {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
justify-content: space-between;
|
|
25
|
+
gap: 0.5rem;
|
|
26
|
+
padding-block: 0.25rem;
|
|
27
|
+
padding-inline: 0.75rem 0.25rem;
|
|
28
|
+
border-block-end: var(--tx-border-width, 1px) solid var(--tx-border, currentColor);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.tx-code__name {
|
|
32
|
+
min-inline-size: 0;
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
text-overflow: ellipsis;
|
|
35
|
+
white-space: nowrap;
|
|
36
|
+
font-family: var(--tx-font-mono, ui-monospace, monospace);
|
|
37
|
+
font-size: var(--tx-font-size-xs, 0.75rem);
|
|
38
|
+
font-weight: var(--tx-font-weight-medium, 500);
|
|
39
|
+
color: var(--tx-muted-foreground, CanvasText);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.tx-code__copy {
|
|
43
|
+
display: inline-flex;
|
|
44
|
+
flex-shrink: 0;
|
|
45
|
+
align-items: center;
|
|
46
|
+
gap: 0.375rem;
|
|
47
|
+
min-block-size: var(--tx-target-size, 2rem);
|
|
48
|
+
padding-inline: 0.5rem;
|
|
49
|
+
border: 0;
|
|
50
|
+
border-radius: calc(var(--tx-radius, 0.5rem) * 0.75);
|
|
51
|
+
background: transparent;
|
|
52
|
+
color: var(--tx-muted-foreground, ButtonText);
|
|
53
|
+
font: inherit;
|
|
54
|
+
font-size: var(--tx-font-size-xs, 0.75rem);
|
|
55
|
+
font-weight: var(--tx-font-weight-medium, 500);
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.tx-code__copy:hover {
|
|
60
|
+
background-color: var(--tx-muted, Canvas);
|
|
61
|
+
color: var(--tx-foreground, ButtonText);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.tx-code__copy:focus-visible,
|
|
65
|
+
.tx-code__pre:focus-visible {
|
|
66
|
+
outline: var(--tx-focus-ring-width, 2px) solid var(--tx-ring, Highlight);
|
|
67
|
+
outline-offset: calc(var(--tx-focus-ring-width, 2px) * -1);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.tx-code__copy:disabled {
|
|
71
|
+
cursor: progress;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.tx-code__icon {
|
|
75
|
+
inline-size: 0.875rem;
|
|
76
|
+
block-size: 0.875rem;
|
|
77
|
+
fill: none;
|
|
78
|
+
stroke: currentColor;
|
|
79
|
+
stroke-width: 2;
|
|
80
|
+
stroke-linecap: round;
|
|
81
|
+
stroke-linejoin: round;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.tx-code__pre {
|
|
85
|
+
margin: 0;
|
|
86
|
+
padding-block: 0.75rem;
|
|
87
|
+
overflow-x: auto;
|
|
88
|
+
font-family: var(--tx-font-mono, ui-monospace, monospace);
|
|
89
|
+
font-size: 0.8125rem;
|
|
90
|
+
line-height: 1.6;
|
|
91
|
+
tab-size: 2;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.tx-code__code {
|
|
95
|
+
display: block;
|
|
96
|
+
min-inline-size: max-content;
|
|
97
|
+
font: inherit;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.tx-code__line {
|
|
101
|
+
display: block;
|
|
102
|
+
padding-inline: 0.75rem 1rem;
|
|
103
|
+
border-inline-start: 2px solid transparent;
|
|
104
|
+
background: transparent;
|
|
105
|
+
color: inherit;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.tx-code__line[data-highlighted] {
|
|
109
|
+
border-inline-start-color: var(--tx-primary, Highlight);
|
|
110
|
+
background-color: var(--tx-muted, Canvas);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.tx-code__pre--numbered .tx-code__line::before {
|
|
114
|
+
display: inline-block;
|
|
115
|
+
min-inline-size: 2.5ch;
|
|
116
|
+
margin-inline-end: 1.25ch;
|
|
117
|
+
content: attr(data-line);
|
|
118
|
+
color: var(--tx-muted-foreground, CanvasText);
|
|
119
|
+
text-align: end;
|
|
120
|
+
user-select: none;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* Numbers are visual only: the line text is what is read and copied. */
|
|
124
|
+
@supports (content: "a" / "") {
|
|
125
|
+
.tx-code__pre--numbered .tx-code__line::before {
|
|
126
|
+
content: attr(data-line) / "";
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.tx-code-tok--keyword,
|
|
131
|
+
.tx-code-tok--tag {
|
|
132
|
+
color: var(--tx-primary, CanvasText);
|
|
133
|
+
font-weight: var(--tx-font-weight-bold, 700);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.tx-code-tok--string,
|
|
137
|
+
.tx-code-tok--attr {
|
|
138
|
+
color: var(--tx-success, CanvasText);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.tx-code-tok--number,
|
|
142
|
+
.tx-code-tok--constant {
|
|
143
|
+
color: var(--tx-destructive, CanvasText);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.tx-code-tok--function,
|
|
147
|
+
.tx-code-tok--type {
|
|
148
|
+
color: var(--tx-info, CanvasText);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.tx-code-tok--comment {
|
|
152
|
+
color: var(--tx-muted-foreground, CanvasText);
|
|
153
|
+
font-style: italic;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.tx-code__caption {
|
|
157
|
+
padding-block: 0.5rem;
|
|
158
|
+
padding-inline: 0.75rem;
|
|
159
|
+
border-block-start: var(--tx-border-width, 1px) solid var(--tx-border, currentColor);
|
|
160
|
+
color: var(--tx-muted-foreground, CanvasText);
|
|
161
|
+
font-family: var(--tx-font-sans, system-ui, sans-serif);
|
|
162
|
+
font-size: var(--tx-font-size-sm, 0.875rem);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.tx-code__sr-only {
|
|
166
|
+
position: absolute;
|
|
167
|
+
inline-size: 1px;
|
|
168
|
+
block-size: 1px;
|
|
169
|
+
margin: -1px;
|
|
170
|
+
padding: 0;
|
|
171
|
+
overflow: hidden;
|
|
172
|
+
clip-path: inset(50%);
|
|
173
|
+
white-space: nowrap;
|
|
174
|
+
border: 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.tx-block-invalid {
|
|
178
|
+
margin: 0;
|
|
179
|
+
padding-block: 0.5em;
|
|
180
|
+
padding-inline: 0.75em;
|
|
181
|
+
border: 1px dashed currentColor;
|
|
182
|
+
border-radius: 0.375em;
|
|
183
|
+
font-size: 0.875em;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@media (forced-colors: active) {
|
|
187
|
+
.tx-code,
|
|
188
|
+
.tx-code__bar {
|
|
189
|
+
border-color: CanvasText;
|
|
190
|
+
}
|
|
191
|
+
.tx-code__line[data-highlighted] {
|
|
192
|
+
forced-color-adjust: none;
|
|
193
|
+
background-color: Highlight;
|
|
194
|
+
color: HighlightText;
|
|
195
|
+
border-inline-start-color: HighlightText;
|
|
196
|
+
}
|
|
197
|
+
.tx-code__line[data-highlighted] [class^="tx-code-tok--"] {
|
|
198
|
+
color: HighlightText;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@texuli/blocks/code/engine` — Svelte-free code family surface for hosts that
|
|
3
|
+
* parse/highlight without mounting the renderer. Grammars + labels + checks;
|
|
4
|
+
* full tokenize helpers remain on `@texuli/blocks/code/tokenizer`.
|
|
5
|
+
*/
|
|
6
|
+
export { type CodeBlockCheck, checkCodeBlock } from './check.js';
|
|
7
|
+
export { CODE_BLOCK_TYPES, type CodeBlockViewProps, type CodeFamilyBlock, type CodeFamilyBlockType, isCodeFamilyBlock, } from './family-engine.js';
|
|
8
|
+
export { CODE_GRAMMAR_LANGUAGES, type CodeGrammarLanguage, loadCodeGrammar, resolveCodeLanguage, } from './grammars.js';
|
|
9
|
+
export { type CodeBlockLabelOverrides, type CodeBlockLabels, DEFAULT_CODE_BLOCK_LABELS, fillCodeLabel, resolveCodeBlockLabels, } from './labels.js';
|
|
10
|
+
export { activeHighlightIndex, CODE_COLOR_ROLES, CODE_TOKEN_STYLES, type CodeColorRole, type CodeTokenStyle, codeTokenClass, } from './token-styles.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@texuli/blocks/code/engine` — Svelte-free code family surface for hosts that
|
|
3
|
+
* parse/highlight without mounting the renderer. Grammars + labels + checks;
|
|
4
|
+
* full tokenize helpers remain on `@texuli/blocks/code/tokenizer`.
|
|
5
|
+
*/
|
|
6
|
+
export { checkCodeBlock } from "./check.js";
|
|
7
|
+
export { CODE_BLOCK_TYPES, isCodeFamilyBlock, } from "./family-engine.js";
|
|
8
|
+
export { CODE_GRAMMAR_LANGUAGES, loadCodeGrammar, resolveCodeLanguage, } from "./grammars.js";
|
|
9
|
+
export { DEFAULT_CODE_BLOCK_LABELS, fillCodeLabel, resolveCodeBlockLabels, } from "./labels.js";
|
|
10
|
+
export { activeHighlightIndex, CODE_COLOR_ROLES, CODE_TOKEN_STYLES, codeTokenClass, } from "./token-styles.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@texuli/blocks/code/tokenizer`: an owned, linear-time code tokenizer.
|
|
3
|
+
* Source becomes `{ kind, text }` tokens for ts, js, json, bash, python, html,
|
|
4
|
+
* css, sql and yaml; renderers print the text in spans colored by theme
|
|
5
|
+
* roles and never build HTML. Ported from Protoyard's code family (which
|
|
6
|
+
* replaced a 303 KB highlight.js).
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* const lines = splitTokenLines(tokenizeCode('const a = 1;', 'ts'));
|
|
10
|
+
* // [[{ kind: 'keyword', text: 'const' }, { kind: 'plain', text: ' a ' }, …]]
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* This entry bundles every grammar, for tools, servers and tests. The
|
|
14
|
+
* renderer loads only the grammar of each block's language (`loadCodeGrammar`).
|
|
15
|
+
*/
|
|
16
|
+
export { countCodeLines, type HighlightStep, parseLineSpec, resolveHighlightSteps, } from './highlight.js';
|
|
17
|
+
export { bashGrammar, cssGrammar, htmlGrammar, javascriptGrammar, jsonGrammar, pythonGrammar, sqlGrammar, tokenizeCode, typescriptGrammar, yamlGrammar, } from './tokenize-code.js';
|
|
18
|
+
export { CODE_TOKEN_KINDS, CODE_TOKENIZE_MAX_LENGTH, type CodeToken, type CodeTokenKind, type Grammar, type GrammarRule, type RuleContext, splitTokenLines, sticky, tokenize, } from './tokenizer.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@texuli/blocks/code/tokenizer`: an owned, linear-time code tokenizer.
|
|
3
|
+
* Source becomes `{ kind, text }` tokens for ts, js, json, bash, python, html,
|
|
4
|
+
* css, sql and yaml; renderers print the text in spans colored by theme
|
|
5
|
+
* roles and never build HTML. Ported from Protoyard's code family (which
|
|
6
|
+
* replaced a 303 KB highlight.js).
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* const lines = splitTokenLines(tokenizeCode('const a = 1;', 'ts'));
|
|
10
|
+
* // [[{ kind: 'keyword', text: 'const' }, { kind: 'plain', text: ' a ' }, …]]
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* This entry bundles every grammar, for tools, servers and tests. The
|
|
14
|
+
* renderer loads only the grammar of each block's language (`loadCodeGrammar`).
|
|
15
|
+
*/
|
|
16
|
+
export { countCodeLines, parseLineSpec, resolveHighlightSteps, } from "./highlight.js";
|
|
17
|
+
export { bashGrammar, cssGrammar, htmlGrammar, javascriptGrammar, jsonGrammar, pythonGrammar, sqlGrammar, tokenizeCode, typescriptGrammar, yamlGrammar, } from "./tokenize-code.js";
|
|
18
|
+
export { CODE_TOKEN_KINDS, CODE_TOKENIZE_MAX_LENGTH, splitTokenLines, sticky, tokenize, } from "./tokenizer.js";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code family meta without loaders (Svelte-free).
|
|
3
|
+
* Full tokenizer stays on `@texuli/blocks/code/tokenizer` (`./engine.ts`).
|
|
4
|
+
*/
|
|
5
|
+
import type { CodeBlock, FieldIssue } from '../core/index.js';
|
|
6
|
+
import type { HighlightStep } from './highlight.js';
|
|
7
|
+
import type { CodeBlockLabelOverrides } from './labels.js';
|
|
8
|
+
export declare const CODE_BLOCK_TYPES: readonly ["code"];
|
|
9
|
+
export type CodeFamilyBlockType = (typeof CODE_BLOCK_TYPES)[number];
|
|
10
|
+
export type CodeFamilyBlock = CodeBlock;
|
|
11
|
+
export declare function isCodeFamilyBlock(block: {
|
|
12
|
+
type: string;
|
|
13
|
+
}): block is CodeFamilyBlock;
|
|
14
|
+
export interface CodeBlockViewProps {
|
|
15
|
+
block: unknown;
|
|
16
|
+
labels?: CodeBlockLabelOverrides;
|
|
17
|
+
oninvalid?: (issues: FieldIssue[]) => void;
|
|
18
|
+
lineNumbers?: boolean;
|
|
19
|
+
highlights?: readonly HighlightStep[];
|
|
20
|
+
hostStep?: number;
|
|
21
|
+
step?: number;
|
|
22
|
+
copyable?: boolean;
|
|
23
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
import type { CodeBlockViewProps } from './family-engine.js';
|
|
3
|
+
export { CODE_BLOCK_TYPES, type CodeBlockViewProps, type CodeFamilyBlock, type CodeFamilyBlockType, isCodeFamilyBlock, } from './family-engine.js';
|
|
4
|
+
/** Lazily loads the code family renderer (its own chunk). */
|
|
5
|
+
export declare function loadCodeFamily(): Promise<{
|
|
6
|
+
default: Component<CodeBlockViewProps>;
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Grammar } from './tokenizer.js';
|
|
2
|
+
/** Languages with a grammar. Anything else renders as plain text. */
|
|
3
|
+
export declare const CODE_GRAMMAR_LANGUAGES: readonly ["ts", "js", "json", "bash", "python", "html", "css", "sql", "yaml"];
|
|
4
|
+
export type CodeGrammarLanguage = (typeof CODE_GRAMMAR_LANGUAGES)[number];
|
|
5
|
+
/** A language id or alias (`typescript`, `sh`, `yml`…) → a grammar language, or `null`. */
|
|
6
|
+
export declare function resolveCodeLanguage(lang: string): CodeGrammarLanguage | null;
|
|
7
|
+
/**
|
|
8
|
+
* The grammar for a language id or alias, loaded on demand and cached; `null`
|
|
9
|
+
* for `text` and unknown languages. A failed load is not cached, so a later
|
|
10
|
+
* render can retry.
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadCodeGrammar(lang: string | undefined): Promise<Grammar | null>;
|