@rse/specbook 0.9.2
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/README.md +199 -0
- package/dst/specbook-api.d.ts +47 -0
- package/dst/specbook-api.js +77 -0
- package/dst/specbook-cli.d.ts +2 -0
- package/dst/specbook-cli.js +156 -0
- package/dst/specbook-cmd-describe.d.ts +2 -0
- package/dst/specbook-cmd-describe.js +97 -0
- package/dst/specbook-cmd-edit.d.ts +9 -0
- package/dst/specbook-cmd-edit.js +69 -0
- package/dst/specbook-cmd-export.d.ts +9 -0
- package/dst/specbook-cmd-export.js +78 -0
- package/dst/specbook-cmd-import.d.ts +9 -0
- package/dst/specbook-cmd-import.js +73 -0
- package/dst/specbook-cmd-init.d.ts +7 -0
- package/dst/specbook-cmd-init.js +37 -0
- package/dst/specbook-cmd-lint.d.ts +15 -0
- package/dst/specbook-cmd-lint.js +46 -0
- package/dst/specbook-config.d.ts +6 -0
- package/dst/specbook-config.js +91 -0
- package/dst/specbook-diagnostic.d.ts +8 -0
- package/dst/specbook-diagnostic.js +23 -0
- package/dst/specbook-diagram.d.ts +9 -0
- package/dst/specbook-diagram.js +271 -0
- package/dst/specbook-export-ast.d.ts +4 -0
- package/dst/specbook-export-ast.js +36 -0
- package/dst/specbook-export-common.d.ts +33 -0
- package/dst/specbook-export-common.js +142 -0
- package/dst/specbook-export-html-search.js +272 -0
- package/dst/specbook-export-html.css +259 -0
- package/dst/specbook-export-html.d.ts +9 -0
- package/dst/specbook-export-html.js +452 -0
- package/dst/specbook-export-logo.svg +35 -0
- package/dst/specbook-export-md.d.ts +3 -0
- package/dst/specbook-export-md.js +74 -0
- package/dst/specbook-export-pdf.d.ts +7 -0
- package/dst/specbook-export-pdf.js +198 -0
- package/dst/specbook-link.d.ts +17 -0
- package/dst/specbook-link.js +113 -0
- package/dst/specbook-llm.d.ts +13 -0
- package/dst/specbook-llm.js +61 -0
- package/dst/specbook-mcp.d.ts +1 -0
- package/dst/specbook-mcp.js +163 -0
- package/dst/specbook-parse-common.d.ts +26 -0
- package/dst/specbook-parse-common.js +33 -0
- package/dst/specbook-parse-semantic.d.ts +5 -0
- package/dst/specbook-parse-semantic.js +254 -0
- package/dst/specbook-parse-syntax.d.ts +3 -0
- package/dst/specbook-parse-syntax.js +363 -0
- package/dst/specbook-parse-value.d.ts +19 -0
- package/dst/specbook-parse-value.js +123 -0
- package/dst/specbook-parse.d.ts +8 -0
- package/dst/specbook-parse.js +43 -0
- package/dst/specbook-struct-schema.d.ts +35 -0
- package/dst/specbook-struct-schema.js +47 -0
- package/dst/specbook-struct-spec.d.ts +31 -0
- package/dst/specbook-struct-spec.js +36 -0
- package/dst/specbook-theme.d.ts +16 -0
- package/dst/specbook-theme.js +38 -0
- package/package.json +77 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/*
|
|
2
|
+
** Specification Book (SpecBook)
|
|
3
|
+
** Copyright (c) 2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
+
** Licensed under Apache 2.0 <https://spdx.org/licenses/Apache-2.0>
|
|
5
|
+
*/
|
|
6
|
+
import { Tokenizr } from "tokenizr";
|
|
7
|
+
/* the lexer for value expressions (members are either bare words or
|
|
8
|
+
double-quoted strings, mirroring the quoting of reference segments) */
|
|
9
|
+
const lexer = new Tokenizr();
|
|
10
|
+
lexer.rule(/\/((?:\\.|[^\\/])*)\//, (ctx, match) => {
|
|
11
|
+
ctx.accept("regex", match[1]);
|
|
12
|
+
});
|
|
13
|
+
lexer.rule(/\[\[([^[\]]+)\]\]/, (ctx, match) => {
|
|
14
|
+
ctx.accept("reference", match[1].trim());
|
|
15
|
+
});
|
|
16
|
+
lexer.rule(/(enum|tags|list)\(/, (ctx, match) => {
|
|
17
|
+
ctx.accept("opener", match[1]);
|
|
18
|
+
});
|
|
19
|
+
lexer.rule(/"((?:\\"|[^"])*)"/, (ctx, match) => {
|
|
20
|
+
ctx.accept("member", match[1].replace(/\\"/g, "\""));
|
|
21
|
+
});
|
|
22
|
+
lexer.rule(/[^,()/[\]"\s]+/, (ctx, match) => {
|
|
23
|
+
ctx.accept("member", match[0]);
|
|
24
|
+
});
|
|
25
|
+
lexer.rule(/,/, (ctx) => {
|
|
26
|
+
ctx.accept("comma");
|
|
27
|
+
});
|
|
28
|
+
lexer.rule(/\)/, (ctx) => {
|
|
29
|
+
ctx.accept("close");
|
|
30
|
+
});
|
|
31
|
+
lexer.rule(/\s+/, (ctx) => {
|
|
32
|
+
ctx.ignore();
|
|
33
|
+
});
|
|
34
|
+
/* describe a token for error messages */
|
|
35
|
+
const tokenName = (token) => token.type === "EOF" ? "end of expression" : `"${token.text}"`;
|
|
36
|
+
/* parse a single expression from the token stream */
|
|
37
|
+
const parseValueExpr = (tokens, pos, nested) => {
|
|
38
|
+
const token = tokens[pos.i++];
|
|
39
|
+
if (token.type === "regex") {
|
|
40
|
+
const source = String(token.value);
|
|
41
|
+
try {
|
|
42
|
+
return { kind: "regex", regex: new RegExp(source), source };
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
throw new Error(`invalid regular expression "/${source}/": ` +
|
|
46
|
+
(err instanceof Error ? err.message : String(err)));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else if (token.type === "reference")
|
|
50
|
+
return { kind: "reference", pattern: String(token.value) };
|
|
51
|
+
else if (token.type === "opener" && (token.value === "enum" || token.value === "tags")) {
|
|
52
|
+
const members = new Array();
|
|
53
|
+
for (;;) {
|
|
54
|
+
const member = tokens[pos.i++];
|
|
55
|
+
if (member.type !== "member")
|
|
56
|
+
throw new Error(`expected member in "${token.value}(...)", got ${tokenName(member)}`);
|
|
57
|
+
members.push(String(member.value));
|
|
58
|
+
const sep = tokens[pos.i++];
|
|
59
|
+
if (sep.type === "close")
|
|
60
|
+
break;
|
|
61
|
+
if (sep.type !== "comma")
|
|
62
|
+
throw new Error(`expected "," or ")" in "${token.value}(...)", got ${tokenName(sep)}`);
|
|
63
|
+
}
|
|
64
|
+
return { kind: token.value === "enum" ? "enum" : "tags", members };
|
|
65
|
+
}
|
|
66
|
+
else if (token.type === "opener" && token.value === "list") {
|
|
67
|
+
if (nested)
|
|
68
|
+
throw new Error("nested \"list(...)\" is not allowed");
|
|
69
|
+
const alternatives = new Array();
|
|
70
|
+
for (;;) {
|
|
71
|
+
alternatives.push(parseValueExpr(tokens, pos, true));
|
|
72
|
+
const sep = tokens[pos.i++];
|
|
73
|
+
if (sep.type === "close")
|
|
74
|
+
break;
|
|
75
|
+
if (sep.type !== "comma")
|
|
76
|
+
throw new Error(`expected "," or ")" in "list(...)", got ${tokenName(sep)}`);
|
|
77
|
+
}
|
|
78
|
+
return { kind: "list", alternatives };
|
|
79
|
+
}
|
|
80
|
+
else
|
|
81
|
+
throw new Error(`expected value expression, got ${tokenName(token)}`);
|
|
82
|
+
};
|
|
83
|
+
/* compile a value expression, memoized per source string */
|
|
84
|
+
const cache = new Map();
|
|
85
|
+
export const compileValueExpr = (source) => {
|
|
86
|
+
let expr = cache.get(source);
|
|
87
|
+
if (expr === undefined) {
|
|
88
|
+
const tokens = lexer.reset().input(source).tokens();
|
|
89
|
+
const pos = { i: 0 };
|
|
90
|
+
expr = parseValueExpr(tokens, pos, false);
|
|
91
|
+
if (tokens[pos.i].type !== "EOF")
|
|
92
|
+
throw new Error(`unexpected trailing ${tokenName(tokens[pos.i])}`);
|
|
93
|
+
cache.set(source, expr);
|
|
94
|
+
}
|
|
95
|
+
return expr;
|
|
96
|
+
};
|
|
97
|
+
/* split a property value at top-level commas, honoring double-quoted
|
|
98
|
+
sections and "[[...]]" reference bracketing */
|
|
99
|
+
export const splitItems = (text) => {
|
|
100
|
+
const parts = [""];
|
|
101
|
+
let quoted = false;
|
|
102
|
+
let bracketed = false;
|
|
103
|
+
for (let i = 0; i < text.length; i++) {
|
|
104
|
+
const char = text[i];
|
|
105
|
+
if (char === "\"")
|
|
106
|
+
quoted = !quoted;
|
|
107
|
+
if (!quoted && !bracketed && char === "[" && text[i + 1] === "[") {
|
|
108
|
+
parts[parts.length - 1] += "[[";
|
|
109
|
+
bracketed = true;
|
|
110
|
+
i++;
|
|
111
|
+
}
|
|
112
|
+
else if (!quoted && bracketed && char === "]" && text[i + 1] === "]") {
|
|
113
|
+
parts[parts.length - 1] += "]]";
|
|
114
|
+
bracketed = false;
|
|
115
|
+
i++;
|
|
116
|
+
}
|
|
117
|
+
else if (!quoted && !bracketed && char === ",")
|
|
118
|
+
parts.push("");
|
|
119
|
+
else
|
|
120
|
+
parts[parts.length - 1] += char;
|
|
121
|
+
}
|
|
122
|
+
return parts.map((part) => part.trim()).filter((part) => part !== "");
|
|
123
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type SchemaSpecification } from "./specbook-struct-schema.js";
|
|
2
|
+
import { type SourceFile, type ParseResult } from "./specbook-parse-common.js";
|
|
3
|
+
export { type SourceFile, type ParseResult, embeddingRegex, embeddingMimeType } from "./specbook-parse-common.js";
|
|
4
|
+
export declare class Parser {
|
|
5
|
+
private ctx;
|
|
6
|
+
parse(sources: SourceFile[], config?: SchemaSpecification): ParseResult;
|
|
7
|
+
}
|
|
8
|
+
export declare const parseSpecification: (sources: SourceFile[], config?: SchemaSpecification) => ParseResult;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/*
|
|
2
|
+
** Specification Book (SpecBook)
|
|
3
|
+
** Copyright (c) 2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
+
** Licensed under Apache 2.0 <https://spdx.org/licenses/Apache-2.0>
|
|
5
|
+
*/
|
|
6
|
+
import * as v from "valibot";
|
|
7
|
+
import { Specification } from "./specbook-struct-spec.js";
|
|
8
|
+
import { buildLinkIndex } from "./specbook-link.js";
|
|
9
|
+
import { ParseContext } from "./specbook-parse-common.js";
|
|
10
|
+
import { parseFile } from "./specbook-parse-syntax.js";
|
|
11
|
+
import { validate, validateReferences } from "./specbook-parse-semantic.js";
|
|
12
|
+
import { validateDiagrams } from "./specbook-diagram.js";
|
|
13
|
+
/* re-export the public parsing types and embedding helpers */
|
|
14
|
+
export { embeddingRegex, embeddingMimeType } from "./specbook-parse-common.js";
|
|
15
|
+
/* parser for a set of specification Markdown files */
|
|
16
|
+
export class Parser {
|
|
17
|
+
ctx = new ParseContext();
|
|
18
|
+
/* parse all source files into the specification AST and
|
|
19
|
+
optionally validate the result against a configuration */
|
|
20
|
+
parse(sources, config) {
|
|
21
|
+
const artifacts = new Array();
|
|
22
|
+
for (const source of sources)
|
|
23
|
+
artifacts.push(...parseFile(this.ctx, source));
|
|
24
|
+
const specification = { artifacts };
|
|
25
|
+
if (artifacts.length > 0) {
|
|
26
|
+
this.ctx.linkIndex = buildLinkIndex(specification);
|
|
27
|
+
if (config !== undefined)
|
|
28
|
+
validate(this.ctx, specification, config);
|
|
29
|
+
validateReferences(this.ctx, specification);
|
|
30
|
+
if (config !== undefined)
|
|
31
|
+
validateDiagrams(this.ctx, specification, config);
|
|
32
|
+
const result = v.safeParse(Specification, specification);
|
|
33
|
+
if (!result.success)
|
|
34
|
+
for (const issue of result.issues) {
|
|
35
|
+
const path = (issue.path ?? []).map((item) => String(item.key)).join(".");
|
|
36
|
+
this.ctx.diagnose(sources[0]?.file ?? "", 1, `internal AST invalid at "${path}": ${issue.message}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { specification, diagnostics: this.ctx.diagnostics };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/* convenience wrapper for one-shot parsing */
|
|
43
|
+
export const parseSpecification = (sources, config) => new Parser().parse(sources, config);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as v from "valibot";
|
|
2
|
+
import { type Config as GradiaConfig } from "@rse/gradia";
|
|
3
|
+
export type SchemaSpecification = SchemaObject[];
|
|
4
|
+
export type SchemaObject = {
|
|
5
|
+
kind: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
id?: string;
|
|
8
|
+
file?: string;
|
|
9
|
+
desc?: string;
|
|
10
|
+
optional?: boolean;
|
|
11
|
+
diagram?: SchemaDiagram;
|
|
12
|
+
props?: SchemaProperty[];
|
|
13
|
+
childs?: SchemaObject[];
|
|
14
|
+
};
|
|
15
|
+
export type SchemaDiagram = {
|
|
16
|
+
type?: "graph" | "hub" | "grid";
|
|
17
|
+
nodes?: string;
|
|
18
|
+
edges?: string;
|
|
19
|
+
center?: string;
|
|
20
|
+
links?: "props" | "all";
|
|
21
|
+
edgeTarget?: string;
|
|
22
|
+
edgeArity?: string;
|
|
23
|
+
hierarchy?: boolean;
|
|
24
|
+
onlyConnected?: boolean;
|
|
25
|
+
collapse?: boolean;
|
|
26
|
+
qualified?: boolean;
|
|
27
|
+
config?: Partial<GradiaConfig>;
|
|
28
|
+
};
|
|
29
|
+
export type SchemaProperty = {
|
|
30
|
+
name: string;
|
|
31
|
+
desc?: string;
|
|
32
|
+
value?: string;
|
|
33
|
+
optional?: boolean;
|
|
34
|
+
};
|
|
35
|
+
export declare const SchemaSpecification: v.GenericSchema<SchemaSpecification>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/*
|
|
2
|
+
** Specification Book (SpecBook)
|
|
3
|
+
** Copyright (c) 2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
+
** Licensed under Apache 2.0 <https://spdx.org/licenses/Apache-2.0>
|
|
5
|
+
*/
|
|
6
|
+
import * as v from "valibot";
|
|
7
|
+
import { Gradia } from "@rse/gradia";
|
|
8
|
+
/* ==== Schema ==== */
|
|
9
|
+
/* the Gradia rendering options, derived from the configuration
|
|
10
|
+
defaults of Gradia: the option names constrain the allowed keys and
|
|
11
|
+
the types of the default values constrain the allowed value types */
|
|
12
|
+
const GradiaConfigSchemas = Object.fromEntries(Object.entries(Gradia.config).map(([key, value]) => [key,
|
|
13
|
+
typeof value === "boolean" ? v.boolean() : typeof value === "number" ? v.number() : v.string()]));
|
|
14
|
+
const SchemaDiagramConfig = v.partial(v.strictObject(GradiaConfigSchemas, (issue) => issue.expected === "never" ? "unknown Gradia rendering option" :
|
|
15
|
+
"expected a map of Gradia rendering options"));
|
|
16
|
+
const SchemaProperty = v.object({
|
|
17
|
+
name: v.string(),
|
|
18
|
+
desc: v.optional(v.string()),
|
|
19
|
+
value: v.optional(v.string()),
|
|
20
|
+
optional: v.optional(v.boolean())
|
|
21
|
+
});
|
|
22
|
+
const SchemaDiagram = v.object({
|
|
23
|
+
type: v.optional(v.picklist(["graph", "hub", "grid"])),
|
|
24
|
+
nodes: v.optional(v.string()),
|
|
25
|
+
edges: v.optional(v.string()),
|
|
26
|
+
center: v.optional(v.string()),
|
|
27
|
+
links: v.optional(v.picklist(["props", "all"])),
|
|
28
|
+
edgeTarget: v.optional(v.string()),
|
|
29
|
+
edgeArity: v.optional(v.string()),
|
|
30
|
+
hierarchy: v.optional(v.boolean()),
|
|
31
|
+
onlyConnected: v.optional(v.boolean()),
|
|
32
|
+
collapse: v.optional(v.boolean()),
|
|
33
|
+
qualified: v.optional(v.boolean()),
|
|
34
|
+
config: v.optional(SchemaDiagramConfig)
|
|
35
|
+
});
|
|
36
|
+
const SchemaObject = v.object({
|
|
37
|
+
kind: v.string(),
|
|
38
|
+
name: v.optional(v.string()),
|
|
39
|
+
id: v.optional(v.string()),
|
|
40
|
+
file: v.optional(v.string()),
|
|
41
|
+
desc: v.optional(v.string()),
|
|
42
|
+
optional: v.optional(v.boolean()),
|
|
43
|
+
diagram: v.optional(SchemaDiagram),
|
|
44
|
+
props: v.optional(v.array(SchemaProperty)),
|
|
45
|
+
childs: v.optional(v.array(v.lazy(() => SchemaObject)))
|
|
46
|
+
});
|
|
47
|
+
export const SchemaSpecification = v.pipe(v.array(SchemaObject), v.minLength(1));
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as v from "valibot";
|
|
2
|
+
export type Specification = {
|
|
3
|
+
artifacts: Artifact[];
|
|
4
|
+
};
|
|
5
|
+
export type Artifact = {
|
|
6
|
+
created: Date;
|
|
7
|
+
modified: Date;
|
|
8
|
+
objects: Object[];
|
|
9
|
+
};
|
|
10
|
+
export type Object = {
|
|
11
|
+
kind: string;
|
|
12
|
+
id: string;
|
|
13
|
+
anchor?: string;
|
|
14
|
+
paren?: string;
|
|
15
|
+
name: string;
|
|
16
|
+
primary?: boolean;
|
|
17
|
+
description?: Description;
|
|
18
|
+
properties: Property[];
|
|
19
|
+
childs: Object[];
|
|
20
|
+
};
|
|
21
|
+
export type Description = {
|
|
22
|
+
description: string;
|
|
23
|
+
rationale?: string;
|
|
24
|
+
embedding?: string[];
|
|
25
|
+
};
|
|
26
|
+
export type Property = {
|
|
27
|
+
key: string;
|
|
28
|
+
value: string;
|
|
29
|
+
embedding?: string[];
|
|
30
|
+
};
|
|
31
|
+
export declare const Specification: v.GenericSchema<Specification>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
** Specification Book (SpecBook)
|
|
3
|
+
** Copyright (c) 2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
+
** Licensed under Apache 2.0 <https://spdx.org/licenses/Apache-2.0>
|
|
5
|
+
*/
|
|
6
|
+
import * as v from "valibot";
|
|
7
|
+
/* ==== Schema ==== */
|
|
8
|
+
const Property = v.object({
|
|
9
|
+
key: v.string(),
|
|
10
|
+
value: v.string(),
|
|
11
|
+
embedding: v.optional(v.array(v.string()))
|
|
12
|
+
});
|
|
13
|
+
const Description = v.object({
|
|
14
|
+
description: v.string(),
|
|
15
|
+
rationale: v.optional(v.string()),
|
|
16
|
+
embedding: v.optional(v.array(v.string()))
|
|
17
|
+
});
|
|
18
|
+
const Object = v.object({
|
|
19
|
+
kind: v.string(),
|
|
20
|
+
id: v.string(),
|
|
21
|
+
anchor: v.optional(v.string()),
|
|
22
|
+
paren: v.optional(v.string()),
|
|
23
|
+
name: v.string(),
|
|
24
|
+
primary: v.optional(v.boolean()),
|
|
25
|
+
description: v.optional(Description),
|
|
26
|
+
properties: v.array(Property),
|
|
27
|
+
childs: v.array(v.lazy(() => Object))
|
|
28
|
+
});
|
|
29
|
+
const Artifact = v.object({
|
|
30
|
+
created: v.date(),
|
|
31
|
+
modified: v.date(),
|
|
32
|
+
objects: v.pipe(v.array(Object), v.minLength(1))
|
|
33
|
+
});
|
|
34
|
+
export const Specification = v.object({
|
|
35
|
+
artifacts: v.pipe(v.array(Artifact), v.minLength(1))
|
|
36
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type ThemeStyle = "light" | "dark";
|
|
2
|
+
export type ThemeColors = {
|
|
3
|
+
base: string[];
|
|
4
|
+
accent: string[];
|
|
5
|
+
search: string[];
|
|
6
|
+
};
|
|
7
|
+
export declare const themeColors: (tone: string) => ThemeColors;
|
|
8
|
+
export declare const themeStylesheet: (colors: ThemeColors) => string;
|
|
9
|
+
export type ThemeMapping = {
|
|
10
|
+
text: string;
|
|
11
|
+
muted: string;
|
|
12
|
+
symbol: string;
|
|
13
|
+
border: string;
|
|
14
|
+
accent: string;
|
|
15
|
+
};
|
|
16
|
+
export declare const themeMapping: (colors: ThemeColors, style: ThemeStyle) => ThemeMapping;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
** Specification Book (SpecBook)
|
|
3
|
+
** Copyright (c) 2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
+
** Licensed under Apache 2.0 <https://spdx.org/licenses/Apache-2.0>
|
|
5
|
+
*/
|
|
6
|
+
import { parse, generate } from "@rse/mrcs";
|
|
7
|
+
/* generate the theme color spreads from the theme color tone:
|
|
8
|
+
a grey base spread, a tone-derived accent spread, and a search
|
|
9
|
+
spread derived from the complement-transposed tone */
|
|
10
|
+
export const themeColors = (tone) => {
|
|
11
|
+
const transposed = tone.endsWith("^") ? tone.slice(0, -1) : `${tone}^`;
|
|
12
|
+
return {
|
|
13
|
+
base: generate(parse("#000000/32")),
|
|
14
|
+
accent: generate(parse(`${tone}+40-5/32`)),
|
|
15
|
+
search: generate(parse(`${transposed}+40-5/32`))
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
/* render the theme colors into the layer-1 ":root" CSS variable
|
|
19
|
+
block "--theme-color-{base,accent,search}-{1..32}" */
|
|
20
|
+
export const themeStylesheet = (colors) => ":root {\n" +
|
|
21
|
+
colors.base.map((color, i) => ` --theme-color-base-${i + 1}: ${color};\n`).join("") +
|
|
22
|
+
colors.accent.map((color, i) => ` --theme-color-accent-${i + 1}: ${color};\n`).join("") +
|
|
23
|
+
colors.search.map((color, i) => ` --theme-color-search-${i + 1}: ${color};\n`).join("") +
|
|
24
|
+
"}\n";
|
|
25
|
+
/* map the theme color spreads onto the semantic theme colors,
|
|
26
|
+
aligned with the layer-2 CSS mapping of the HTML rendering */
|
|
27
|
+
export const themeMapping = (colors, style) => {
|
|
28
|
+
const idx = style === "dark" ?
|
|
29
|
+
{ text: 28, muted: 22, symbol: 20, border: 16, accent: 24 } :
|
|
30
|
+
{ text: 5, muted: 22, symbol: 24, border: 28, accent: 12 };
|
|
31
|
+
return {
|
|
32
|
+
text: colors.base[idx.text - 1],
|
|
33
|
+
muted: colors.base[idx.muted - 1],
|
|
34
|
+
symbol: colors.base[idx.symbol - 1],
|
|
35
|
+
border: colors.base[idx.border - 1],
|
|
36
|
+
accent: colors.accent[idx.accent - 1]
|
|
37
|
+
};
|
|
38
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rse/specbook",
|
|
3
|
+
"version": "0.9.2",
|
|
4
|
+
"publishConfig": { "access": "public" },
|
|
5
|
+
"description": "Markdown-based Specification Format (API, CLI and MCP service)",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Dr. Ralf S. Engelschall",
|
|
9
|
+
"email": "rse@engelschall.com",
|
|
10
|
+
"url": "http://engelschall.com"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"main": "dst/specbook-api.js",
|
|
14
|
+
"types": "dst/specbook-api.d.ts",
|
|
15
|
+
"bin": {
|
|
16
|
+
"specbook": "dst/specbook-cli.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [ "dst" ],
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"marked": "18.0.10",
|
|
21
|
+
"marked-smartypants": "1.1.12",
|
|
22
|
+
"yaml": "2.9.0",
|
|
23
|
+
"json5": "2.2.3",
|
|
24
|
+
"pdf-lib": "1.17.1",
|
|
25
|
+
"subset-font": "2.5.0",
|
|
26
|
+
"@rse/mrcs": "0.9.1",
|
|
27
|
+
"@rse/gradia": "0.9.4",
|
|
28
|
+
"@toon-format/toon": "4.1.1",
|
|
29
|
+
"valibot": "1.4.2",
|
|
30
|
+
"nunjucks": "3.2.4",
|
|
31
|
+
"@rse/nunjucks-addons": "2.2.0",
|
|
32
|
+
"textframe": "1.2.1",
|
|
33
|
+
"@swc/html": "1.16.1",
|
|
34
|
+
"tokenizr": "2.2.1",
|
|
35
|
+
"playwright": "1.62.1",
|
|
36
|
+
"ai": "7.0.68",
|
|
37
|
+
"@ai-sdk/anthropic": "4.0.39",
|
|
38
|
+
"@ai-sdk/openai": "4.0.43",
|
|
39
|
+
"@openrouter/ai-sdk-provider": "3.0.0",
|
|
40
|
+
"ollama-ai-provider-v2": "4.0.1",
|
|
41
|
+
"source-code-error": "1.2.0",
|
|
42
|
+
"commander": "15.0.0",
|
|
43
|
+
"@modelcontextprotocol/sdk": "1.30.0",
|
|
44
|
+
"zod": "4.4.3"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"eslint": "9.39.5",
|
|
48
|
+
"@eslint/js": "9.39.5",
|
|
49
|
+
"typescript-eslint": "8.67.0",
|
|
50
|
+
"eslint-plugin-promise": "7.3.0",
|
|
51
|
+
"eslint-plugin-import": "2.32.0",
|
|
52
|
+
"neostandard": "0.13.0",
|
|
53
|
+
"globals": "17.11.0",
|
|
54
|
+
"@types/node": "26.2.0",
|
|
55
|
+
"@types/subset-font": "2.3.0",
|
|
56
|
+
"@types/nunjucks": "3.2.6",
|
|
57
|
+
"typescript": "6.0.3",
|
|
58
|
+
"tsx": "4.23.12",
|
|
59
|
+
"source-sans": "3.52.0",
|
|
60
|
+
"source-code-pro": "2.42.0",
|
|
61
|
+
"@fontsource/noto-sans-symbols": "5.3.0",
|
|
62
|
+
"postcss": "8.5.26",
|
|
63
|
+
"postcss-cli": "11.0.1",
|
|
64
|
+
"postcss-url": "10.1.4",
|
|
65
|
+
"@rse/stx": "1.1.6",
|
|
66
|
+
"shx": "0.4.0",
|
|
67
|
+
"nodemon": "3.1.14"
|
|
68
|
+
},
|
|
69
|
+
"upd": [ "!eslint", "!@eslint/js", "!typescript" ],
|
|
70
|
+
"allowScripts": {
|
|
71
|
+
"fsevents": true,
|
|
72
|
+
"esbuild": true
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"start": "stx -v4 -l warning -c etc/stx.conf"
|
|
76
|
+
}
|
|
77
|
+
}
|