@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
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { sticky } from "../tokenizer.js";
|
|
2
|
+
/**
|
|
3
|
+
* Rules several grammars share. Each opening delimiter always matches: an
|
|
4
|
+
* unterminated single-line string ends at its line break (`$` with the `m`
|
|
5
|
+
* flag), an unterminated block comment or multi-line string at the end of
|
|
6
|
+
* the input (`$` without it). See tokenizer.ts for why that keeps scanning
|
|
7
|
+
* linear.
|
|
8
|
+
*/
|
|
9
|
+
export const WHITESPACE = { re: sticky(String.raw `[ \t\r\n]+`), kind: 'plain' };
|
|
10
|
+
/** `"…"` on one line, with backslash escapes. */
|
|
11
|
+
export const DOUBLE_QUOTED = {
|
|
12
|
+
re: sticky(String.raw `"(?:[^"\\\n]|\\.?)*(?:"|$)`),
|
|
13
|
+
kind: 'string',
|
|
14
|
+
};
|
|
15
|
+
/** `'…'` on one line, with backslash escapes. */
|
|
16
|
+
export const SINGLE_QUOTED = {
|
|
17
|
+
re: sticky(String.raw `'(?:[^'\\\n]|\\.?)*(?:'|$)`),
|
|
18
|
+
kind: 'string',
|
|
19
|
+
};
|
|
20
|
+
/** `/* … *\/`, possibly across lines. */
|
|
21
|
+
export const BLOCK_COMMENT = {
|
|
22
|
+
re: sticky(String.raw `\/\*[\s\S]*?(?:\*\/|(?![\s\S]))`),
|
|
23
|
+
kind: 'comment',
|
|
24
|
+
};
|
|
25
|
+
/** `// …` to the end of the line. */
|
|
26
|
+
export const SLASH_COMMENT = { re: sticky(String.raw `\/\/[^\n]*`), kind: 'comment' };
|
|
27
|
+
/** `# …` at a line start or after whitespace (not `$#` or `a#b`). */
|
|
28
|
+
export const HASH_COMMENT = {
|
|
29
|
+
re: sticky(String.raw `(?<=^|[ \t;|&(])#[^\n]*`),
|
|
30
|
+
kind: 'comment',
|
|
31
|
+
};
|
|
32
|
+
/** Decimal, hex, octal and binary numbers with `_` separators and exponents. */
|
|
33
|
+
export const C_NUMBER = {
|
|
34
|
+
re: sticky(String.raw `0[xX][\da-fA-F_]+n?|0[bB][01_]+n?|0[oO][0-7_]+n?|\d[\d_]*(?:\.\d[\d_]*)?(?:[eE][+-]?\d+)?n?|\.\d[\d_]*(?:[eE][+-]?\d+)?`),
|
|
35
|
+
kind: 'number',
|
|
36
|
+
};
|
|
37
|
+
export const words = (...list) => new Set(list);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { classifyWord, sticky } from "../tokenizer.js";
|
|
2
|
+
import { BLOCK_COMMENT, C_NUMBER, WHITESPACE, words } from "./shared.js";
|
|
3
|
+
const word = classifyWord({
|
|
4
|
+
ignoreCase: true,
|
|
5
|
+
keywords: words('add', 'all', 'alter', 'and', 'as', 'asc', 'begin', 'between', 'by', 'case', 'commit', 'constraint', 'create', 'cross', 'default', 'delete', 'desc', 'distinct', 'drop', 'else', 'end', 'exists', 'foreign', 'from', 'full', 'group', 'having', 'if', 'in', 'index', 'inner', 'insert', 'into', 'is', 'join', 'key', 'left', 'like', 'limit', 'not', 'offset', 'on', 'or', 'order', 'outer', 'primary', 'references', 'returning', 'right', 'rollback', 'select', 'set', 'table', 'then', 'transaction', 'union', 'unique', 'update', 'values', 'view', 'when', 'where', 'with'),
|
|
6
|
+
constants: words('null', 'true', 'false'),
|
|
7
|
+
builtins: words('avg', 'cast', 'coalesce', 'count', 'lower', 'max', 'min', 'now', 'sum', 'upper'),
|
|
8
|
+
types: words('bigint', 'boolean', 'char', 'date', 'decimal', 'float', 'int', 'integer', 'json', 'jsonb', 'numeric', 'real', 'serial', 'text', 'timestamp', 'timestamptz', 'uuid', 'varchar'),
|
|
9
|
+
});
|
|
10
|
+
/** Standard SQL with the common Postgres and SQLite spellings. */
|
|
11
|
+
export const sqlGrammar = {
|
|
12
|
+
name: 'sql',
|
|
13
|
+
start: 'code',
|
|
14
|
+
states: {
|
|
15
|
+
code: [
|
|
16
|
+
WHITESPACE,
|
|
17
|
+
{ re: sticky(String.raw `--[^\n]*`), kind: 'comment' },
|
|
18
|
+
BLOCK_COMMENT,
|
|
19
|
+
// '' escapes a quote; strings may span lines.
|
|
20
|
+
{ re: sticky(String.raw `'(?:[^']|'')*(?:'|(?![\s\S]))`), kind: 'string' },
|
|
21
|
+
{
|
|
22
|
+
re: sticky(String.raw `"(?:[^"]|"")*(?:"|(?![\s\S]))|\x60[^\x60\n]*\x60?`),
|
|
23
|
+
kind: 'property',
|
|
24
|
+
},
|
|
25
|
+
{ re: sticky(String.raw `\$\d+|[:@][A-Za-z_]\w*|\?`), kind: 'variable' },
|
|
26
|
+
{ re: sticky(String.raw `[A-Za-z_]\w*`), kind: word },
|
|
27
|
+
C_NUMBER,
|
|
28
|
+
{ re: sticky(String.raw `[<>=!|+\-*/%]+`), kind: 'operator' },
|
|
29
|
+
{ re: sticky('[(),;.]'), kind: 'punctuation' },
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Grammar } from '../tokenizer.js';
|
|
2
|
+
/**
|
|
3
|
+
* YAML: keys, scalars, anchors, tags, comments and document markers. A key is
|
|
4
|
+
* only looked for after a line's indentation (and an optional `- `), so each
|
|
5
|
+
* line is scanned for a key once.
|
|
6
|
+
*/
|
|
7
|
+
export declare const yamlGrammar: Grammar;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { sticky } from "../tokenizer.js";
|
|
2
|
+
import { WHITESPACE, words } from "./shared.js";
|
|
3
|
+
const CONSTANTS = words('true', 'false', 'yes', 'no', 'on', 'off', 'null', 'True', 'False', 'Null', 'TRUE', 'FALSE', 'NULL', '~');
|
|
4
|
+
/**
|
|
5
|
+
* YAML: keys, scalars, anchors, tags, comments and document markers. A key is
|
|
6
|
+
* only looked for after a line's indentation (and an optional `- `), so each
|
|
7
|
+
* line is scanned for a key once.
|
|
8
|
+
*/
|
|
9
|
+
export const yamlGrammar = {
|
|
10
|
+
name: 'yaml',
|
|
11
|
+
start: 'code',
|
|
12
|
+
states: {
|
|
13
|
+
code: [
|
|
14
|
+
WHITESPACE,
|
|
15
|
+
{ re: sticky(String.raw `(?<=^|[ \t])#[^\n]*`), kind: 'comment' },
|
|
16
|
+
{ re: sticky(String.raw `^(?:---|\.\.\.)(?=[ \t]|$)`), kind: 'punctuation' },
|
|
17
|
+
{
|
|
18
|
+
re: sticky(String.raw `(?<=^[ \t]*(?:-[ \t]+)?)(?:"[^"\n]*"|'[^'\n]*'|[^\s#'"\-?:,[\]{}][^\n#]*?)(?=:(?:[ \t]|$))`),
|
|
19
|
+
kind: 'property',
|
|
20
|
+
},
|
|
21
|
+
{ re: sticky(String.raw `"(?:[^"\\\n]|\\.?)*(?:"|$)`), kind: 'string' },
|
|
22
|
+
{ re: sticky(String.raw `'(?:[^'\n]|'')*(?:'|$)`), kind: 'string' },
|
|
23
|
+
{ re: sticky(String.raw `[&*][\w-]+`), kind: 'variable' },
|
|
24
|
+
{ re: sticky(String.raw `![\w!/.-]*`), kind: 'type' },
|
|
25
|
+
{
|
|
26
|
+
re: sticky(String.raw `[-+]?(?:\d[\d_]*(?:\.\d*)?(?:[eE][+-]?\d+)?|\.(?:inf|nan|Inf|NaN))(?![\w.-])`),
|
|
27
|
+
kind: 'number',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
re: sticky(String.raw `[A-Za-z_~][\w.~-]*`),
|
|
31
|
+
kind: (text) => (CONSTANTS.has(text) ? 'constant' : 'plain'),
|
|
32
|
+
},
|
|
33
|
+
{ re: sticky(String.raw `[:{}[\],?|>-]`), kind: 'punctuation' },
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CodeTokenKind } from './tokenizer.js';
|
|
2
|
+
/**
|
|
3
|
+
* Theme roles a token kind may be drawn in. Each one reaches WCAG AA (4.5:1)
|
|
4
|
+
* as text on `surface` (the code background) and on `muted` (a highlighted
|
|
5
|
+
* line) in every library theme, light and dark; `code.test.ts` checks all of
|
|
6
|
+
* them. `secondary` and `accent` are fills, never text, so they are not used.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CODE_COLOR_ROLES: readonly ["surfaceForeground", "mutedForeground", "primary", "success", "info", "destructive"];
|
|
9
|
+
export type CodeColorRole = (typeof CODE_COLOR_ROLES)[number];
|
|
10
|
+
export interface CodeTokenStyle {
|
|
11
|
+
color: CodeColorRole;
|
|
12
|
+
/** A non-color cue, so kinds differ without color too. */
|
|
13
|
+
emphasis?: 'bold' | 'italic';
|
|
14
|
+
}
|
|
15
|
+
/** One style per token kind; keywords and tags are also bold, comments italic. */
|
|
16
|
+
export declare const CODE_TOKEN_STYLES: Readonly<Record<CodeTokenKind, CodeTokenStyle>>;
|
|
17
|
+
/** The CSS class of a token kind (`tx-code-tok--keyword`); plain text has none. */
|
|
18
|
+
export declare function codeTokenClass(kind: CodeTokenKind): string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Which highlight step a block shows. An explicit `step` wins. Otherwise a
|
|
21
|
+
* host step (a deck fragment index) counts from the block's own `blockStep`:
|
|
22
|
+
* host step `blockStep + i` shows step `i`, clamped to the first and last.
|
|
23
|
+
* With neither, the first step shows as static emphasis. `-1` means none.
|
|
24
|
+
*/
|
|
25
|
+
export declare function activeHighlightIndex(stepCount: number, step: number | undefined, hostStep?: number, blockStep?: number): number;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme roles a token kind may be drawn in. Each one reaches WCAG AA (4.5:1)
|
|
3
|
+
* as text on `surface` (the code background) and on `muted` (a highlighted
|
|
4
|
+
* line) in every library theme, light and dark; `code.test.ts` checks all of
|
|
5
|
+
* them. `secondary` and `accent` are fills, never text, so they are not used.
|
|
6
|
+
*/
|
|
7
|
+
export const CODE_COLOR_ROLES = [
|
|
8
|
+
'surfaceForeground',
|
|
9
|
+
'mutedForeground',
|
|
10
|
+
'primary',
|
|
11
|
+
'success',
|
|
12
|
+
'info',
|
|
13
|
+
'destructive',
|
|
14
|
+
];
|
|
15
|
+
/** One style per token kind; keywords and tags are also bold, comments italic. */
|
|
16
|
+
export const CODE_TOKEN_STYLES = Object.freeze({
|
|
17
|
+
plain: { color: 'surfaceForeground' },
|
|
18
|
+
punctuation: { color: 'surfaceForeground' },
|
|
19
|
+
operator: { color: 'surfaceForeground' },
|
|
20
|
+
variable: { color: 'surfaceForeground' },
|
|
21
|
+
property: { color: 'surfaceForeground' },
|
|
22
|
+
keyword: { color: 'primary', emphasis: 'bold' },
|
|
23
|
+
tag: { color: 'primary', emphasis: 'bold' },
|
|
24
|
+
string: { color: 'success' },
|
|
25
|
+
attr: { color: 'success' },
|
|
26
|
+
number: { color: 'destructive' },
|
|
27
|
+
constant: { color: 'destructive' },
|
|
28
|
+
function: { color: 'info' },
|
|
29
|
+
type: { color: 'info' },
|
|
30
|
+
comment: { color: 'mutedForeground', emphasis: 'italic' },
|
|
31
|
+
});
|
|
32
|
+
/** The CSS class of a token kind (`tx-code-tok--keyword`); plain text has none. */
|
|
33
|
+
export function codeTokenClass(kind) {
|
|
34
|
+
return kind === 'plain' ? undefined : `tx-code-tok--${kind}`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Which highlight step a block shows. An explicit `step` wins. Otherwise a
|
|
38
|
+
* host step (a deck fragment index) counts from the block's own `blockStep`:
|
|
39
|
+
* host step `blockStep + i` shows step `i`, clamped to the first and last.
|
|
40
|
+
* With neither, the first step shows as static emphasis. `-1` means none.
|
|
41
|
+
*/
|
|
42
|
+
export function activeHighlightIndex(stepCount, step, hostStep, blockStep = 0) {
|
|
43
|
+
if (stepCount <= 0)
|
|
44
|
+
return -1;
|
|
45
|
+
const clamp = (value) => Math.min(stepCount - 1, Math.max(0, Math.floor(value)));
|
|
46
|
+
if (step !== undefined && Number.isFinite(step))
|
|
47
|
+
return clamp(step);
|
|
48
|
+
if (hostStep !== undefined && Number.isFinite(hostStep))
|
|
49
|
+
return clamp(hostStep - blockStep);
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { bashGrammar } from './languages/bash.js';
|
|
2
|
+
import { cssGrammar } from './languages/css.js';
|
|
3
|
+
import { htmlGrammar } from './languages/html.js';
|
|
4
|
+
import { javascriptGrammar, typescriptGrammar } from './languages/javascript.js';
|
|
5
|
+
import { jsonGrammar } from './languages/json.js';
|
|
6
|
+
import { pythonGrammar } from './languages/python.js';
|
|
7
|
+
import { sqlGrammar } from './languages/sql.js';
|
|
8
|
+
import { yamlGrammar } from './languages/yaml.js';
|
|
9
|
+
import { type CodeToken } from './tokenizer.js';
|
|
10
|
+
/**
|
|
11
|
+
* Tokenizes `code` for `lang` (an id or alias) with every grammar bundled, for
|
|
12
|
+
* servers, tests and tools. Renderers use `loadCodeGrammar` + `tokenize`, which
|
|
13
|
+
* load one grammar on demand. Unknown languages, including `text`, give one
|
|
14
|
+
* plain token. Concatenated texts always equal the input.
|
|
15
|
+
*/
|
|
16
|
+
export declare function tokenizeCode(code: string, lang: string): CodeToken[];
|
|
17
|
+
export { bashGrammar, cssGrammar, htmlGrammar, javascriptGrammar, jsonGrammar, pythonGrammar, sqlGrammar, typescriptGrammar, yamlGrammar, };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { resolveCodeLanguage } from "./grammars.js";
|
|
2
|
+
import { bashGrammar } from "./languages/bash.js";
|
|
3
|
+
import { cssGrammar } from "./languages/css.js";
|
|
4
|
+
import { htmlGrammar } from "./languages/html.js";
|
|
5
|
+
import { javascriptGrammar, typescriptGrammar } from "./languages/javascript.js";
|
|
6
|
+
import { jsonGrammar } from "./languages/json.js";
|
|
7
|
+
import { pythonGrammar } from "./languages/python.js";
|
|
8
|
+
import { sqlGrammar } from "./languages/sql.js";
|
|
9
|
+
import { yamlGrammar } from "./languages/yaml.js";
|
|
10
|
+
import { tokenize } from "./tokenizer.js";
|
|
11
|
+
const GRAMMARS = {
|
|
12
|
+
ts: typescriptGrammar,
|
|
13
|
+
js: javascriptGrammar,
|
|
14
|
+
json: jsonGrammar,
|
|
15
|
+
bash: bashGrammar,
|
|
16
|
+
python: pythonGrammar,
|
|
17
|
+
html: htmlGrammar,
|
|
18
|
+
css: cssGrammar,
|
|
19
|
+
sql: sqlGrammar,
|
|
20
|
+
yaml: yamlGrammar,
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Tokenizes `code` for `lang` (an id or alias) with every grammar bundled, for
|
|
24
|
+
* servers, tests and tools. Renderers use `loadCodeGrammar` + `tokenize`, which
|
|
25
|
+
* load one grammar on demand. Unknown languages, including `text`, give one
|
|
26
|
+
* plain token. Concatenated texts always equal the input.
|
|
27
|
+
*/
|
|
28
|
+
export function tokenizeCode(code, lang) {
|
|
29
|
+
const resolved = resolveCodeLanguage(lang);
|
|
30
|
+
if (resolved === null)
|
|
31
|
+
return code === '' ? [] : [{ kind: 'plain', text: code }];
|
|
32
|
+
return tokenize(code, GRAMMARS[resolved]);
|
|
33
|
+
}
|
|
34
|
+
export { bashGrammar, cssGrammar, htmlGrammar, javascriptGrammar, jsonGrammar, pythonGrammar, sqlGrammar, typescriptGrammar, yamlGrammar, };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A tiny, linear-time code tokenizer. A grammar is a small state machine of
|
|
3
|
+
* sticky regular expressions; at each position the first rule that matches
|
|
4
|
+
* wins, and text no rule claims becomes `plain`. Tokens are data
|
|
5
|
+
* (`{ kind, text }`) that renderers map to theme-token classes: nothing is
|
|
6
|
+
* ever turned into HTML.
|
|
7
|
+
*
|
|
8
|
+
* Linear time comes from how the grammars are written, and the tests hold
|
|
9
|
+
* them to it with pathological inputs:
|
|
10
|
+
*
|
|
11
|
+
* - every rule starts with a distinctive character and consumes at least one;
|
|
12
|
+
* - an opening delimiter always matches: an unterminated string runs to the
|
|
13
|
+
* end of its line, and an unterminated block comment or multi-line string
|
|
14
|
+
* runs to the end of the input, so no position is scanned twice;
|
|
15
|
+
* - no rule nests quantifiers over overlapping alternatives;
|
|
16
|
+
* - keywords are looked up in sets after matching a word, never by a long
|
|
17
|
+
* alternation.
|
|
18
|
+
*/
|
|
19
|
+
export declare const CODE_TOKEN_KINDS: readonly ["plain", "keyword", "string", "number", "comment", "operator", "punctuation", "function", "type", "property", "constant", "variable", "tag", "attr"];
|
|
20
|
+
export type CodeTokenKind = (typeof CODE_TOKEN_KINDS)[number];
|
|
21
|
+
export interface CodeToken {
|
|
22
|
+
kind: CodeTokenKind;
|
|
23
|
+
text: string;
|
|
24
|
+
}
|
|
25
|
+
/** Characters tokenized per call; the rest is returned as one plain token. */
|
|
26
|
+
export declare const CODE_TOKENIZE_MAX_LENGTH = 20000;
|
|
27
|
+
/** What a classifier knows about the match. */
|
|
28
|
+
export interface RuleContext {
|
|
29
|
+
input: string;
|
|
30
|
+
/** Index just after the match. */
|
|
31
|
+
end: number;
|
|
32
|
+
/** The previous token that is not whitespace, if any. */
|
|
33
|
+
prev: CodeToken | null;
|
|
34
|
+
}
|
|
35
|
+
export interface GrammarRule {
|
|
36
|
+
/** Built with `sticky`; must not match the empty string or capture groups. */
|
|
37
|
+
re: RegExp;
|
|
38
|
+
kind: CodeTokenKind | ((text: string, ctx: RuleContext) => CodeTokenKind);
|
|
39
|
+
/** State to switch to after this rule matches. */
|
|
40
|
+
next?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface Grammar {
|
|
43
|
+
/** Language id, for diagnostics and tests. */
|
|
44
|
+
name: string;
|
|
45
|
+
start: string;
|
|
46
|
+
states: Readonly<Record<string, readonly GrammarRule[]>>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Rule regex from a source string; every grammar builds rules with it. All
|
|
50
|
+
* rules share the `m` flag (`^`/`$` are line anchors; write end of input as
|
|
51
|
+
* `(?![\s\S])`) so a state's rules compile into one alternation.
|
|
52
|
+
*/
|
|
53
|
+
export declare function sticky(source: string): RegExp;
|
|
54
|
+
/** The next character after `index` that is not a space or tab (bounded lookahead). */
|
|
55
|
+
export declare function nextNonBlank(input: string, index: number): string;
|
|
56
|
+
export interface WordSets {
|
|
57
|
+
keywords?: ReadonlySet<string>;
|
|
58
|
+
builtins?: ReadonlySet<string>;
|
|
59
|
+
constants?: ReadonlySet<string>;
|
|
60
|
+
types?: ReadonlySet<string>;
|
|
61
|
+
/** Words after which the next word names a type (`class`, `interface`…). */
|
|
62
|
+
typeIntroducers?: ReadonlySet<string>;
|
|
63
|
+
/** Words after which the next word names a function (`function`, `def`). */
|
|
64
|
+
functionIntroducers?: ReadonlySet<string>;
|
|
65
|
+
/** Case-insensitive lookup (SQL). */
|
|
66
|
+
ignoreCase?: boolean;
|
|
67
|
+
}
|
|
68
|
+
/** A classifier for identifiers: set lookups plus a little context. */
|
|
69
|
+
export declare function classifyWord(sets: WordSets): (text: string, ctx: RuleContext) => CodeTokenKind;
|
|
70
|
+
/**
|
|
71
|
+
* Tokenizes `code` with `grammar`. The concatenated token texts always equal
|
|
72
|
+
* the input. Input beyond `CODE_TOKENIZE_MAX_LENGTH` is one plain token.
|
|
73
|
+
*/
|
|
74
|
+
export declare function tokenize(code: string, grammar: Grammar): CodeToken[];
|
|
75
|
+
/** Splits tokens into lines; line breaks are dropped, empty lines are `[]`. */
|
|
76
|
+
export declare function splitTokenLines(tokens: readonly CodeToken[]): CodeToken[][];
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A tiny, linear-time code tokenizer. A grammar is a small state machine of
|
|
3
|
+
* sticky regular expressions; at each position the first rule that matches
|
|
4
|
+
* wins, and text no rule claims becomes `plain`. Tokens are data
|
|
5
|
+
* (`{ kind, text }`) that renderers map to theme-token classes: nothing is
|
|
6
|
+
* ever turned into HTML.
|
|
7
|
+
*
|
|
8
|
+
* Linear time comes from how the grammars are written, and the tests hold
|
|
9
|
+
* them to it with pathological inputs:
|
|
10
|
+
*
|
|
11
|
+
* - every rule starts with a distinctive character and consumes at least one;
|
|
12
|
+
* - an opening delimiter always matches: an unterminated string runs to the
|
|
13
|
+
* end of its line, and an unterminated block comment or multi-line string
|
|
14
|
+
* runs to the end of the input, so no position is scanned twice;
|
|
15
|
+
* - no rule nests quantifiers over overlapping alternatives;
|
|
16
|
+
* - keywords are looked up in sets after matching a word, never by a long
|
|
17
|
+
* alternation.
|
|
18
|
+
*/
|
|
19
|
+
export const CODE_TOKEN_KINDS = [
|
|
20
|
+
'plain',
|
|
21
|
+
'keyword',
|
|
22
|
+
'string',
|
|
23
|
+
'number',
|
|
24
|
+
'comment',
|
|
25
|
+
'operator',
|
|
26
|
+
'punctuation',
|
|
27
|
+
'function',
|
|
28
|
+
'type',
|
|
29
|
+
'property',
|
|
30
|
+
'constant',
|
|
31
|
+
'variable',
|
|
32
|
+
'tag',
|
|
33
|
+
'attr',
|
|
34
|
+
];
|
|
35
|
+
/** Characters tokenized per call; the rest is returned as one plain token. */
|
|
36
|
+
export const CODE_TOKENIZE_MAX_LENGTH = 20_000;
|
|
37
|
+
/**
|
|
38
|
+
* Rule regex from a source string; every grammar builds rules with it. All
|
|
39
|
+
* rules share the `m` flag (`^`/`$` are line anchors; write end of input as
|
|
40
|
+
* `(?![\s\S])`) so a state's rules compile into one alternation.
|
|
41
|
+
*/
|
|
42
|
+
export function sticky(source) {
|
|
43
|
+
return new RegExp(source, 'my');
|
|
44
|
+
}
|
|
45
|
+
const LOOKAHEAD = 64;
|
|
46
|
+
/** The next character after `index` that is not a space or tab (bounded lookahead). */
|
|
47
|
+
export function nextNonBlank(input, index) {
|
|
48
|
+
const limit = Math.min(input.length, index + LOOKAHEAD);
|
|
49
|
+
for (let i = index; i < limit; i += 1) {
|
|
50
|
+
const char = input[i];
|
|
51
|
+
if (char !== ' ' && char !== '\t')
|
|
52
|
+
return char;
|
|
53
|
+
}
|
|
54
|
+
return '';
|
|
55
|
+
}
|
|
56
|
+
/** A classifier for identifiers: set lookups plus a little context. */
|
|
57
|
+
export function classifyWord(sets) {
|
|
58
|
+
return (text, ctx) => {
|
|
59
|
+
const word = sets.ignoreCase ? text.toLowerCase() : text;
|
|
60
|
+
const prev = ctx.prev?.text;
|
|
61
|
+
if (prev === '.' || prev === '?.')
|
|
62
|
+
return 'property';
|
|
63
|
+
if (prev !== undefined && sets.functionIntroducers?.has(prev))
|
|
64
|
+
return 'function';
|
|
65
|
+
if (prev !== undefined && sets.typeIntroducers?.has(prev))
|
|
66
|
+
return 'type';
|
|
67
|
+
if (sets.keywords?.has(word))
|
|
68
|
+
return 'keyword';
|
|
69
|
+
if (sets.constants?.has(word))
|
|
70
|
+
return 'constant';
|
|
71
|
+
if (sets.types?.has(word))
|
|
72
|
+
return 'type';
|
|
73
|
+
if (nextNonBlank(ctx.input, ctx.end) === '(')
|
|
74
|
+
return 'function';
|
|
75
|
+
if (sets.builtins?.has(word))
|
|
76
|
+
return 'function';
|
|
77
|
+
return 'plain';
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function push(tokens, kind, text) {
|
|
81
|
+
if (text === '')
|
|
82
|
+
return;
|
|
83
|
+
const last = tokens[tokens.length - 1];
|
|
84
|
+
if (last && last.kind === kind)
|
|
85
|
+
last.text += text;
|
|
86
|
+
else
|
|
87
|
+
tokens.push({ kind, text });
|
|
88
|
+
}
|
|
89
|
+
const compiled = new WeakMap();
|
|
90
|
+
function captureGroups(source) {
|
|
91
|
+
return new RegExp(`${source}|`).exec('').length - 1;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Compiles a state once per grammar. One `exec` per position replaces one per
|
|
95
|
+
* rule, and alternation order keeps the first-rule-wins semantics.
|
|
96
|
+
*/
|
|
97
|
+
function compileState(grammar, state) {
|
|
98
|
+
let states = compiled.get(grammar);
|
|
99
|
+
if (!states) {
|
|
100
|
+
states = new Map();
|
|
101
|
+
compiled.set(grammar, states);
|
|
102
|
+
}
|
|
103
|
+
const cached = states.get(state);
|
|
104
|
+
if (cached)
|
|
105
|
+
return cached;
|
|
106
|
+
const rules = grammar.states[state] ?? [];
|
|
107
|
+
for (const rule of rules) {
|
|
108
|
+
if (captureGroups(rule.re.source) > 0) {
|
|
109
|
+
throw new Error(`${grammar.name}.${state}: use (?:…) instead of capture groups in ${rule.re.source}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const source = rules.length > 0 ? rules.map((rule) => `(${rule.re.source})`).join('|') : '(?!)';
|
|
113
|
+
const entry = { re: new RegExp(source, 'my'), rules };
|
|
114
|
+
states.set(state, entry);
|
|
115
|
+
return entry;
|
|
116
|
+
}
|
|
117
|
+
const BLANK = /^[ \t\r\n]+$/;
|
|
118
|
+
/**
|
|
119
|
+
* Tokenizes `code` with `grammar`. The concatenated token texts always equal
|
|
120
|
+
* the input. Input beyond `CODE_TOKENIZE_MAX_LENGTH` is one plain token.
|
|
121
|
+
*/
|
|
122
|
+
export function tokenize(code, grammar) {
|
|
123
|
+
const tokens = [];
|
|
124
|
+
const input = code.length > CODE_TOKENIZE_MAX_LENGTH ? code.slice(0, CODE_TOKENIZE_MAX_LENGTH) : code;
|
|
125
|
+
let current = compileState(grammar, grammar.start);
|
|
126
|
+
let prev = null;
|
|
127
|
+
let index = 0;
|
|
128
|
+
while (index < input.length) {
|
|
129
|
+
current.re.lastIndex = index;
|
|
130
|
+
const match = current.re.exec(input);
|
|
131
|
+
const text = match?.[0] ?? '';
|
|
132
|
+
if (match === null || text.length === 0) {
|
|
133
|
+
// No rule claims this character: it is plain text.
|
|
134
|
+
const char = input[index];
|
|
135
|
+
push(tokens, 'plain', char);
|
|
136
|
+
if (!BLANK.test(char))
|
|
137
|
+
prev = { kind: 'plain', text: char };
|
|
138
|
+
index += 1;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
let which = 1;
|
|
142
|
+
while (match[which] === undefined)
|
|
143
|
+
which += 1;
|
|
144
|
+
const rule = current.rules[which - 1];
|
|
145
|
+
const end = index + text.length;
|
|
146
|
+
const kind = typeof rule.kind === 'function' ? rule.kind(text, { input, end, prev }) : rule.kind;
|
|
147
|
+
push(tokens, kind, text);
|
|
148
|
+
if (!BLANK.test(text))
|
|
149
|
+
prev = { kind, text };
|
|
150
|
+
if (rule.next)
|
|
151
|
+
current = compileState(grammar, rule.next);
|
|
152
|
+
index = end;
|
|
153
|
+
}
|
|
154
|
+
if (input.length < code.length)
|
|
155
|
+
push(tokens, 'plain', code.slice(input.length));
|
|
156
|
+
return tokens;
|
|
157
|
+
}
|
|
158
|
+
/** Splits tokens into lines; line breaks are dropped, empty lines are `[]`. */
|
|
159
|
+
export function splitTokenLines(tokens) {
|
|
160
|
+
const lines = [[]];
|
|
161
|
+
for (const token of tokens) {
|
|
162
|
+
const parts = token.text.split('\n');
|
|
163
|
+
parts.forEach((part, i) => {
|
|
164
|
+
if (i > 0)
|
|
165
|
+
lines.push([]);
|
|
166
|
+
if (part !== '')
|
|
167
|
+
lines[lines.length - 1].push({ kind: token.kind, text: part });
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return lines;
|
|
171
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BlockCatalogEntry } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Catalog metadata for the core block vocabulary. Later family tasks extend
|
|
4
|
+
* this list; agents and the website read useWhen / dontUseWhen / limits / a11y.
|
|
5
|
+
*/
|
|
6
|
+
export declare const BLOCK_CATALOG: readonly BlockCatalogEntry[];
|
|
7
|
+
/** Look up catalog metadata for a block type. */
|
|
8
|
+
export declare function getCatalogEntry(type: string): BlockCatalogEntry | undefined;
|
|
9
|
+
/** All catalog types in registration order. */
|
|
10
|
+
export declare function listCatalogTypes(): BlockCatalogEntry['type'][];
|