@coldsmirk/inkstone-core 0.29.0 → 0.31.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/README.md +1 -0
- package/dist/index.d.ts +46 -15
- package/dist/index.js +50 -12
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Part of [inkstone](https://github.com/coldsmirk/inkstone). The editor layers ([`
|
|
|
10
10
|
- **`configureHighlighter(config)`** — extend or replace the default grammar / theme set, and choose the regex engine, once, before any editor mounts. `DEFAULT_HIGHLIGHTER_LANGS` is exported so extension is additive. `engine` takes the engine itself, a promise of one, or a factory called once when the highlighter is first built — configuration stays synchronous while the engine may be async to make: the strict JavaScript engine by default, `createJavaScriptRegexEngine({ forgiving: true })` for grammars the strict one refuses, or `onigurumaEngine`.
|
|
11
11
|
- **`onigurumaEngine`** — the Oniguruma engine on its WASM, inlined into a chunk of your own bundle (about 600 KB, nothing fetched from anywhere else) and instantiated once, in about 20 ms. Which engine: the JavaScript one translates each grammar's patterns into JavaScript at the first tokenization, on the main thread, and pays for it on whole files — a TypeScript file's first sixty lines cost it about 170 ms, against Oniguruma's 20. Colouring fenced snippets, keep the default; reading whole files in an editor (`@coldsmirk/inkstone-monaco`'s Shiki bridge, `<MonacoEditor>`), pass `onigurumaEngine` as the factory. `onigurumaEngine(wasm)` takes the WASM from elsewhere in any form Shiki's `createOnigurumaEngine` accepts, such as a `fetch("/onig.wasm")`.
|
|
12
12
|
- **`shikiThemeId(dark)`** plus the `INKSTONE_LIGHT_THEME` / `INKSTONE_DARK_THEME` constants — the theme names to pass wherever a Shiki theme id is expected.
|
|
13
|
+
- **`highlighterSources(highlighter)`** — what the shared highlighter was built from and has loaded since: every grammar registration in the order it was given them (the configured `langs` and every later `loadLanguage` / `loadLanguageSync`, which the highlighter wraps to keep them) and the kind of its engine — `oniguruma` wherever an engine was configured, `javascript` where none was. `@coldsmirk/inkstone-monaco`'s tokenization worker builds a highlighter of the same grammars from it; undefined for a highlighter inkstone did not build.
|
|
13
14
|
- **`configureEditorColors(config)`** — set every color the editors paint that is not syntax (a VS Code theme's `colors`, beside its `tokenColors`) in your design language, once, before any editor mounts. Per scheme, CSS color literals for the editor (`background` — content, gutter and the fields of its chrome — `lineHighlight`, `lineNumber`, `lineNumberActive`, `cursor`, `selection`, `scrollbar`), its floating chrome — completion, hover, parameter hints, the find widget and search panels, the context menu — (`widget`, `widgetBorder`, `widgetSelection`, `widgetForeground`, `widgetForegroundMuted`), the `accent` that rings a focused field and marks what a completion matched, and the diagnostic severities `error`, `warning`, `info` and `hint`, named as CodeMirror's `Diagnostic.severity` names them. The catppuccin pair keeps its syntax palette — token colors, the editor's default text, the completion-kind icons that mirror them — and gets these baked into its theme colors — Monaco, static rendering, and CodeMirror's chrome all read the same values — so an editor sits inside the app instead of bringing a second dark. A role or scheme left out keeps the flavor's own color, a later call replaces the schemes it names, and values must be hex colors (`#rrggbb`, `#rrggbbaa`) — Monaco parses theme colors as hex and paints red for anything else, so a `var()` reference or an `rgb(…)` is refused up front. Keep `lineHighlight` translucent: CodeMirror draws selections behind the content, so an opaque active line would hide them. `selection` shows at half its alpha while the editor is unfocused, in both engines; `scrollbar` has no hover state (CodeMirror's `scrollbar-color` has none, so Monaco's slider keeps it too). `getEditorColors(dark)` reads what was configured, and `withEditorColors(theme, colors)` applies a set of them to a Shiki theme of your own.
|
|
14
15
|
- **`CODE_FONT_FAMILY`** — the shared code font stack both editor engines render with: the page's `--inkstone-font-family-code` CSS token when defined, else the platform's native monospace.
|
|
15
16
|
- **`inkstoneRadius(size, fallback)` / `inkstoneShadow(size, fallback)`** — `var()` references to the host's `--inkstone-radius-{small,medium,large,xlarge}` and `--inkstone-shadow-{medium,large,xlarge}` tokens. Set them once on your root and both engines' chrome follows: the Monaco host maps them onto VS Code's `--vscode-cornerRadius-*` / `--vscode-shadow-*` design tokens (which the standalone editor never defines), CodeMirror's polish reads them directly; unset, each engine keeps the look it ships with.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Awaitable, HighlighterCore, HighlighterCore as HighlighterCore$1, LanguageInput, LoadWasmOptions, RegexEngine, ThemeInput, ThemeRegistration } from "shiki/core";
|
|
1
|
+
import { Awaitable, HighlighterCore, HighlighterCore as HighlighterCore$1, LanguageInput, LanguageRegistration, LoadWasmOptions, RegexEngine, ThemeInput, ThemeRegistration } from "shiki/core";
|
|
2
2
|
//#region src/code-font.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* The code font stack every inkstone editor host renders with: the page's
|
|
@@ -6,7 +6,7 @@ import { Awaitable, HighlighterCore, HighlighterCore as HighlighterCore$1, Langu
|
|
|
6
6
|
* Monaco reads it as a construction-option default, CodeMirror through the chrome theme —
|
|
7
7
|
* one token, one look across both editors.
|
|
8
8
|
*/
|
|
9
|
-
declare const CODE_FONT_FAMILY = "var(--inkstone-font-family-code, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)";
|
|
9
|
+
export declare const CODE_FONT_FAMILY = "var(--inkstone-font-family-code, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)";
|
|
10
10
|
//#endregion
|
|
11
11
|
//#region src/editor-colors.d.ts
|
|
12
12
|
/**
|
|
@@ -114,7 +114,7 @@ interface EditorColorsConfig {
|
|
|
114
114
|
* palette) are untouched, only the theme colors behind each configured role change. Returns
|
|
115
115
|
* the theme itself when there is nothing to apply.
|
|
116
116
|
*/
|
|
117
|
-
declare function withEditorColors(theme: ThemeRegistration, colors: EditorColors | undefined): ThemeRegistration;
|
|
117
|
+
export declare function withEditorColors(theme: ThemeRegistration, colors: EditorColors | undefined): ThemeRegistration;
|
|
118
118
|
//#endregion
|
|
119
119
|
//#region src/host-tokens.d.ts
|
|
120
120
|
/**
|
|
@@ -127,7 +127,7 @@ type RadiusSize = "small" | "medium" | "large" | "xlarge";
|
|
|
127
127
|
/**
|
|
128
128
|
* A `var()` reference to the host's `--inkstone-radius-<size>` token with the given fallback.
|
|
129
129
|
*/
|
|
130
|
-
declare function inkstoneRadius(size: RadiusSize, fallback: string): string;
|
|
130
|
+
export declare function inkstoneRadius(size: RadiusSize, fallback: string): string;
|
|
131
131
|
/**
|
|
132
132
|
* The elevation sizes floating chrome reads from the host: `--inkstone-shadow-<size>`, a full
|
|
133
133
|
* `box-shadow` value. Same contract as the radii: Monaco maps them onto VS Code's
|
|
@@ -138,23 +138,23 @@ type ShadowSize = "medium" | "large" | "xlarge";
|
|
|
138
138
|
/**
|
|
139
139
|
* A `var()` reference to the host's `--inkstone-shadow-<size>` token with the given fallback.
|
|
140
140
|
*/
|
|
141
|
-
declare function inkstoneShadow(size: ShadowSize, fallback: string): string;
|
|
141
|
+
export declare function inkstoneShadow(size: ShadowSize, fallback: string): string;
|
|
142
142
|
//#endregion
|
|
143
143
|
//#region src/shiki.d.ts
|
|
144
144
|
/**
|
|
145
145
|
* Shiki theme id inkstone editors use in light mode.
|
|
146
146
|
*/
|
|
147
|
-
declare const INKSTONE_LIGHT_THEME = "catppuccin-latte";
|
|
147
|
+
export declare const INKSTONE_LIGHT_THEME = "catppuccin-latte";
|
|
148
148
|
/**
|
|
149
149
|
* Shiki theme id inkstone editors use in dark mode.
|
|
150
150
|
*/
|
|
151
|
-
declare const INKSTONE_DARK_THEME = "catppuccin-macchiato";
|
|
151
|
+
export declare const INKSTONE_DARK_THEME = "catppuccin-macchiato";
|
|
152
152
|
/**
|
|
153
153
|
* The grammars the shared highlighter loads when {@link configureHighlighter} is not called:
|
|
154
154
|
* JavaScript / TypeScript / JSON / SQL — the set the coldsmirk apps actually edit and render.
|
|
155
155
|
* Spread these when adding a grammar: `configureHighlighter({ langs: [...DEFAULT_HIGHLIGHTER_LANGS, yaml] })`.
|
|
156
156
|
*/
|
|
157
|
-
declare const DEFAULT_HIGHLIGHTER_LANGS: readonly LanguageInput[];
|
|
157
|
+
export declare const DEFAULT_HIGHLIGHTER_LANGS: readonly LanguageInput[];
|
|
158
158
|
interface HighlighterConfig {
|
|
159
159
|
/**
|
|
160
160
|
* Grammars to load, replacing {@link DEFAULT_HIGHLIGHTER_LANGS}.
|
|
@@ -196,14 +196,42 @@ interface HighlighterConfig {
|
|
|
196
196
|
* `fetch("/onig.wasm")` for a deployment that would rather stream-compile the binary than
|
|
197
197
|
* decode it out of a script.
|
|
198
198
|
*/
|
|
199
|
-
declare function onigurumaEngine(wasm?: LoadWasmOptions): Promise<RegexEngine>;
|
|
199
|
+
export declare function onigurumaEngine(wasm?: LoadWasmOptions): Promise<RegexEngine>;
|
|
200
|
+
/**
|
|
201
|
+
* The regex engine a highlighter built elsewhere from the shared one's grammars tokenizes
|
|
202
|
+
* with, to tokenize as the shared one does: `javascript`, the strict JavaScript engine, where
|
|
203
|
+
* the app configured none; `oniguruma` wherever it configured one. An engine is an object of
|
|
204
|
+
* the page's that cannot be sent anywhere, and Oniguruma is the engine every grammar was
|
|
205
|
+
* written against — the one {@link onigurumaEngine} is, and the one an engine configured for
|
|
206
|
+
* the grammars the strict engine refuses stands in for.
|
|
207
|
+
*/
|
|
208
|
+
type HighlighterEngineKind = "javascript" | "oniguruma";
|
|
209
|
+
/**
|
|
210
|
+
* What the shared highlighter was built from, for a highlighter of the same grammars built
|
|
211
|
+
* elsewhere — Monaco's tokenization worker (`@coldsmirk/inkstone-monaco`) is one: every grammar
|
|
212
|
+
* registration it was given, in the order it was given them (shiki builds a grammar again when
|
|
213
|
+
* a language it embeds lazily arrives after it, so the order is part of what was built), and
|
|
214
|
+
* the kind of its engine.
|
|
215
|
+
*/
|
|
216
|
+
interface HighlighterSources {
|
|
217
|
+
readonly engine: HighlighterEngineKind;
|
|
218
|
+
/**
|
|
219
|
+
* Grows as the app loads grammars: read it when a copy is due, never once for good.
|
|
220
|
+
*/
|
|
221
|
+
readonly languages: readonly LanguageRegistration[];
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* What the shared highlighter was built from and has loaded since (see
|
|
225
|
+
* {@link HighlighterSources}); undefined for a highlighter inkstone did not build.
|
|
226
|
+
*/
|
|
227
|
+
export declare function highlighterSources(highlighter: HighlighterCore$1): HighlighterSources | undefined;
|
|
200
228
|
/**
|
|
201
229
|
* Configure the shared highlighter before its first use. The highlighter is a process-wide
|
|
202
230
|
* singleton (editors, static rendering, and markdown surfaces all share it for consistent
|
|
203
231
|
* colors), so configuration is process-global too and must happen before the first
|
|
204
232
|
* {@link getHighlighter} call — afterwards the instance is already built and this throws.
|
|
205
233
|
*/
|
|
206
|
-
declare function configureHighlighter(options: HighlighterConfig): void;
|
|
234
|
+
export declare function configureHighlighter(options: HighlighterConfig): void;
|
|
207
235
|
/**
|
|
208
236
|
* Paint the editors in the host's own colors, once, before the first {@link getHighlighter}
|
|
209
237
|
* call — the catppuccin pair is registered with these colors baked in (Monaco reads them from
|
|
@@ -216,12 +244,12 @@ declare function configureHighlighter(options: HighlighterConfig): void;
|
|
|
216
244
|
* theme colors as hex and paints red for anything else, a custom property reference or an
|
|
217
245
|
* `rgb(…)` alike, so those are refused here instead of failing on screen.
|
|
218
246
|
*/
|
|
219
|
-
declare function configureEditorColors(config: EditorColorsConfig): void;
|
|
247
|
+
export declare function configureEditorColors(config: EditorColorsConfig): void;
|
|
220
248
|
/**
|
|
221
249
|
* The editor colors configured for a color scheme through {@link configureEditorColors}, or
|
|
222
250
|
* undefined when the flavor's own colors stand.
|
|
223
251
|
*/
|
|
224
|
-
declare function getEditorColors(dark: boolean): EditorColors | undefined;
|
|
252
|
+
export declare function getEditorColors(dark: boolean): EditorColors | undefined;
|
|
225
253
|
/**
|
|
226
254
|
* The shared Shiki highlighter, built lazily on first call.
|
|
227
255
|
*
|
|
@@ -234,12 +262,15 @@ declare function getEditorColors(dark: boolean): EditorColors | undefined;
|
|
|
234
262
|
* A failed build is not cached: the slot resets so a later call retries (and
|
|
235
263
|
* {@link configureHighlighter} becomes callable again), instead of replaying one transient
|
|
236
264
|
* failure — a chunk 404 after a redeploy, a flaky network — for the rest of the session.
|
|
265
|
+
*
|
|
266
|
+
* The highlighter keeps what it was built from and every grammar loaded into it since, for a
|
|
267
|
+
* highlighter of the same grammars built elsewhere ({@link highlighterSources}).
|
|
237
268
|
*/
|
|
238
|
-
declare function getHighlighter(): Promise<HighlighterCore$1>;
|
|
269
|
+
export declare function getHighlighter(): Promise<HighlighterCore$1>;
|
|
239
270
|
/**
|
|
240
271
|
* The Shiki theme id for a color scheme — {@link INKSTONE_DARK_THEME} when `dark`, else
|
|
241
272
|
* {@link INKSTONE_LIGHT_THEME}.
|
|
242
273
|
*/
|
|
243
|
-
declare function shikiThemeId(dark: boolean): string;
|
|
274
|
+
export declare function shikiThemeId(dark: boolean): string;
|
|
244
275
|
//#endregion
|
|
245
|
-
export {
|
|
276
|
+
export type { EditorColors, EditorColorsConfig, HighlighterConfig, HighlighterCore, HighlighterEngineKind, HighlighterSources, RadiusSize, ShadowSize };
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import langSql from "@shikijs/langs/sql";
|
|
|
4
4
|
import langTypescript from "@shikijs/langs/typescript";
|
|
5
5
|
import catppuccinLatte from "@shikijs/themes/catppuccin-latte";
|
|
6
6
|
import catppuccinMacchiato from "@shikijs/themes/catppuccin-macchiato";
|
|
7
|
-
import { createHighlighterCore } from "shiki/core";
|
|
7
|
+
import { createHighlighterCore, isSpecialLang, normalizeGetter } from "shiki/core";
|
|
8
8
|
import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
9
9
|
import { createOnigurumaEngine } from "shiki/engine/oniguruma";
|
|
10
10
|
//#region src/code-font.ts
|
|
@@ -163,13 +163,47 @@ function getHighlighterRegistry() {
|
|
|
163
163
|
registry = {
|
|
164
164
|
config: {},
|
|
165
165
|
surfaces: {},
|
|
166
|
-
promise: null
|
|
166
|
+
promise: null,
|
|
167
|
+
sources: /* @__PURE__ */ new WeakMap()
|
|
167
168
|
};
|
|
168
169
|
host[HIGHLIGHTER_REGISTRY_KEY] = registry;
|
|
169
170
|
}
|
|
170
171
|
registry.surfaces ??= {};
|
|
172
|
+
registry.sources ??= /* @__PURE__ */ new WeakMap();
|
|
171
173
|
return registry;
|
|
172
174
|
}
|
|
175
|
+
async function resolveLanguages(langs) {
|
|
176
|
+
const loaded = await Promise.all(langs.filter((lang) => !isSpecialLang(lang)).map(async (lang) => {
|
|
177
|
+
const registrations = await normalizeGetter(lang);
|
|
178
|
+
return Array.isArray(registrations) ? registrations : [registrations];
|
|
179
|
+
}));
|
|
180
|
+
return [...new Set(loaded.flat())];
|
|
181
|
+
}
|
|
182
|
+
function keepingSources(highlighter, languages, engine, registry) {
|
|
183
|
+
const kept = [...languages];
|
|
184
|
+
const keep = (registrations) => {
|
|
185
|
+
for (const registration of registrations) if (!kept.includes(registration)) kept.push(registration);
|
|
186
|
+
};
|
|
187
|
+
const { loadLanguage, loadLanguageSync } = highlighter;
|
|
188
|
+
highlighter.loadLanguage = async (...langs) => {
|
|
189
|
+
const registrations = await resolveLanguages(langs);
|
|
190
|
+
await loadLanguage(...registrations);
|
|
191
|
+
keep(registrations);
|
|
192
|
+
};
|
|
193
|
+
highlighter.loadLanguageSync = (...langs) => {
|
|
194
|
+
const registrations = [...new Set(langs.flat())];
|
|
195
|
+
loadLanguageSync(...registrations);
|
|
196
|
+
keep(registrations);
|
|
197
|
+
};
|
|
198
|
+
registry.sources.set(highlighter, {
|
|
199
|
+
engine,
|
|
200
|
+
languages: kept
|
|
201
|
+
});
|
|
202
|
+
return highlighter;
|
|
203
|
+
}
|
|
204
|
+
function highlighterSources(highlighter) {
|
|
205
|
+
return getHighlighterRegistry().sources.get(highlighter);
|
|
206
|
+
}
|
|
173
207
|
function assertConfigurable(registry, caller) {
|
|
174
208
|
if (registry.promise) throw new Error(`${caller} must be called before the first getHighlighter()`);
|
|
175
209
|
}
|
|
@@ -202,15 +236,19 @@ function getHighlighter() {
|
|
|
202
236
|
const registry = getHighlighterRegistry();
|
|
203
237
|
const existing = registry.promise;
|
|
204
238
|
if (existing) return existing;
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
239
|
+
const { config, surfaces } = registry;
|
|
240
|
+
const attempt = (async () => {
|
|
241
|
+
const langs = await resolveLanguages(config.langs ?? DEFAULT_HIGHLIGHTER_LANGS);
|
|
242
|
+
return keepingSources(await createHighlighterCore({
|
|
243
|
+
themes: [
|
|
244
|
+
withEditorColors(catppuccinLatte, surfaces.light),
|
|
245
|
+
withEditorColors(catppuccinMacchiato, surfaces.dark),
|
|
246
|
+
...config.themes ?? []
|
|
247
|
+
],
|
|
248
|
+
langs,
|
|
249
|
+
engine: engineOf(config)
|
|
250
|
+
}), langs, config.engine === void 0 ? "javascript" : "oniguruma", registry);
|
|
251
|
+
})();
|
|
214
252
|
registry.promise = attempt;
|
|
215
253
|
attempt.catch(() => {
|
|
216
254
|
if (registry.promise === attempt) registry.promise = null;
|
|
@@ -221,4 +259,4 @@ function shikiThemeId(dark) {
|
|
|
221
259
|
return dark ? INKSTONE_DARK_THEME : INKSTONE_LIGHT_THEME;
|
|
222
260
|
}
|
|
223
261
|
//#endregion
|
|
224
|
-
export { CODE_FONT_FAMILY, DEFAULT_HIGHLIGHTER_LANGS, INKSTONE_DARK_THEME, INKSTONE_LIGHT_THEME, configureEditorColors, configureHighlighter, getEditorColors, getHighlighter, inkstoneRadius, inkstoneShadow, onigurumaEngine, shikiThemeId, withEditorColors };
|
|
262
|
+
export { CODE_FONT_FAMILY, DEFAULT_HIGHLIGHTER_LANGS, INKSTONE_DARK_THEME, INKSTONE_LIGHT_THEME, configureEditorColors, configureHighlighter, getEditorColors, getHighlighter, highlighterSources, inkstoneRadius, inkstoneShadow, onigurumaEngine, shikiThemeId, withEditorColors };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coldsmirk/inkstone-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"description": "Shared Shiki highlighter service for the inkstone editor toolkit: one fine-grained singleton (catppuccin themes, the JS regex engine by default or Oniguruma on request) reused by Monaco, static rendering, and markdown surfaces.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"shiki",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"dist"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@shikijs/langs": "^4.
|
|
38
|
-
"@shikijs/themes": "^4.
|
|
39
|
-
"shiki": "^4.
|
|
37
|
+
"@shikijs/langs": "^4.5.0",
|
|
38
|
+
"@shikijs/themes": "^4.5.0",
|
|
39
|
+
"shiki": "^4.5.0"
|
|
40
40
|
},
|
|
41
41
|
"engines": {
|
|
42
42
|
"node": ">=24"
|