@coldsmirk/inkstone-core 0.18.0 → 0.19.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 CHANGED
@@ -9,7 +9,9 @@ Part of [inkstone](https://github.com/coldsmirk/inkstone). The editor layers ([`
9
9
  - **`getHighlighter()`** — the process-wide `HighlighterCore` promise. JavaScript / TypeScript / JSON / SQL grammars and the catppuccin theme pair load by default.
10
10
  - **`configureHighlighter(config)`** — extend or replace the default grammar / theme set, once, before any editor mounts. `DEFAULT_HIGHLIGHTER_LANGS` is exported so extension is additive.
11
11
  - **`shikiThemeId(dark)`** plus the `INKSTONE_LIGHT_THEME` / `INKSTONE_DARK_THEME` constants — the theme names to pass wherever a Shiki theme id is expected.
12
+ - **`configureEditorSurface(surfaces)`** — re-light the editors' own surfaces in your design language, once, before any editor mounts: per-scheme CSS color literals for `background` (content and gutter), `lineHighlight`, `lineNumber`, `widget` (completion, hover, parameter hints, the find widget and search panels), `widgetBorder`, and `widgetSelection`. The catppuccin pair keeps its syntax palette 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 color literals (a `var()` reference is refused — Monaco cannot resolve one). Keep `lineHighlight` translucent: CodeMirror draws selections behind the content, so an opaque active line would hide them. `getEditorSurface(dark)` reads what was configured.
12
13
  - **`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.
14
+ - **`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.
13
15
 
14
16
  ## Install
15
17
 
@@ -22,12 +24,18 @@ pnpm add @coldsmirk/inkstone-core
22
24
  ## Quick start
23
25
 
24
26
  ```ts
25
- import { configureHighlighter, DEFAULT_HIGHLIGHTER_LANGS, getHighlighter, shikiThemeId } from "@coldsmirk/inkstone-core";
27
+ import { configureEditorSurface, configureHighlighter, DEFAULT_HIGHLIGHTER_LANGS, getHighlighter, shikiThemeId } from "@coldsmirk/inkstone-core";
26
28
  import yaml from "@shikijs/langs/yaml";
27
29
 
28
30
  // Optional: add grammars/themes beyond the defaults — once, before any editor mounts.
29
31
  configureHighlighter({ langs: [...DEFAULT_HIGHLIGHTER_LANGS, yaml] });
30
32
 
33
+ // Optional: paint the editors' surfaces in your own tokens (syntax colors stay catppuccin's).
34
+ configureEditorSurface({
35
+ light: { background: "#f7f7fa", lineHighlight: "#ffffff", widget: "#ffffff", widgetBorder: "#ced4da" },
36
+ dark: { background: "#1d1b22", lineHighlight: "#232129", widget: "#302e38", widgetBorder: "#423f4b" }
37
+ });
38
+
31
39
  const highlighter = await getHighlighter();
32
40
  const html = highlighter.codeToHtml("const x = 1;", {
33
41
  lang: "javascript",
@@ -37,6 +45,16 @@ const html = highlighter.codeToHtml("const x = 1;", {
37
45
 
38
46
  The same instance is the right one for non-editor surfaces (markdown code blocks, JSON inspectors, Mantine's `@mantine/code-highlight` adapter) — sharing it keeps their colors identical to the editors'.
39
47
 
48
+ Corners and elevation are CSS tokens rather than configuration — declare them once and both engines follow:
49
+
50
+ ```css
51
+ :root {
52
+ --inkstone-radius-medium: 8px; /* menu rows, completion rows */
53
+ --inkstone-radius-large: 8px; /* the floating widgets themselves */
54
+ --inkstone-shadow-large: 0 8px 24px rgba(0, 0, 0, 0.12);
55
+ }
56
+ ```
57
+
40
58
  ## License
41
59
 
42
60
  UNLICENSED — proprietary. All rights reserved; no use, copying, or redistribution without the author's permission.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { HighlighterCore, HighlighterCore as HighlighterCore$1, LanguageInput, ThemeInput } from "shiki/core";
1
+ import { HighlighterCore, HighlighterCore as HighlighterCore$1, LanguageInput, 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
@@ -8,6 +8,81 @@ import { HighlighterCore, HighlighterCore as HighlighterCore$1, LanguageInput, T
8
8
  */
9
9
  declare const CODE_FONT_FAMILY = "var(--inkstone-font-family-code, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)";
10
10
  //#endregion
11
+ //#region src/surface.d.ts
12
+ /**
13
+ * The surfaces an editor paints, as a host's design language names them. A host that
14
+ * configures these keeps the catppuccin flavors' syntax palette and gets the editor's own
15
+ * chrome — background, gutter, current line, the floating completion / hover / find widgets —
16
+ * in its own tokens, so an editor sits inside the app instead of bringing a second dark.
17
+ *
18
+ * Every value is a CSS color literal (`#rrggbb`, `rgb(…)`): Monaco resolves its theme colors
19
+ * itself and cannot follow a custom property. An omitted role keeps the flavor's own color.
20
+ */
21
+ interface EditorSurface {
22
+ /**
23
+ * The editor's own background: the content area and the gutter.
24
+ */
25
+ background?: string;
26
+ /**
27
+ * The current line's highlight. Prefer a translucent color: CodeMirror draws selections
28
+ * behind the content, so an opaque fill on the active line hides a selection there (Monaco
29
+ * paints selections above it and is indifferent).
30
+ */
31
+ lineHighlight?: string;
32
+ /**
33
+ * Line numbers.
34
+ */
35
+ lineNumber?: string;
36
+ /**
37
+ * Floating chrome: completion, hover and parameter hints, the find widget and the search
38
+ * panels.
39
+ */
40
+ widget?: string;
41
+ /**
42
+ * The hairline around floating chrome and its fields.
43
+ */
44
+ widgetBorder?: string;
45
+ /**
46
+ * A hovered or selected row inside floating chrome.
47
+ */
48
+ widgetSelection?: string;
49
+ }
50
+ /**
51
+ * One surface per color scheme; an omitted scheme keeps the flavor's own surfaces.
52
+ */
53
+ interface EditorSurfaces {
54
+ light?: EditorSurface;
55
+ dark?: EditorSurface;
56
+ }
57
+ /**
58
+ * The corner-radius sizes both editors read from the host: `--inkstone-radius-<size>`. Monaco
59
+ * maps them onto VS Code's own `--vscode-cornerRadius-*` design tokens (which the standalone
60
+ * editor never defines — its chrome ships square by omission), CodeMirror's chrome polish
61
+ * reads them directly; each falls back to the look it ships with when the host sets none.
62
+ */
63
+ type RadiusSize = "small" | "medium" | "large" | "xlarge";
64
+ /**
65
+ * A `var()` reference to the host's `--inkstone-radius-<size>` token with the given fallback.
66
+ */
67
+ declare function inkstoneRadius(size: RadiusSize, fallback: string): string;
68
+ /**
69
+ * The elevation sizes floating chrome reads from the host: `--inkstone-shadow-<size>`, a full
70
+ * `box-shadow` value. Same contract as the radii: Monaco maps them onto VS Code's
71
+ * `--vscode-shadow-*` tokens, CodeMirror's tooltips read them directly, each with its own
72
+ * fallback.
73
+ */
74
+ type ShadowSize = "medium" | "large" | "xlarge";
75
+ /**
76
+ * A `var()` reference to the host's `--inkstone-shadow-<size>` token with the given fallback.
77
+ */
78
+ declare function inkstoneShadow(size: ShadowSize, fallback: string): string;
79
+ /**
80
+ * A flavor with its surfaces repainted from `surface`: the token colors (the syntax palette)
81
+ * are untouched, only the theme colors behind each configured role change. Returns the theme
82
+ * itself when there is nothing to apply.
83
+ */
84
+ declare function surfacedTheme(theme: ThemeRegistration, surface: EditorSurface | undefined): ThemeRegistration;
85
+ //#endregion
11
86
  //#region src/shiki.d.ts
12
87
  /**
13
88
  * Shiki theme id inkstone editors use in light mode.
@@ -41,6 +116,23 @@ interface HighlighterConfig {
41
116
  * {@link getHighlighter} call — afterwards the instance is already built and this throws.
42
117
  */
43
118
  declare function configureHighlighter(options: HighlighterConfig): void;
119
+ /**
120
+ * Re-light the editors' surfaces in the host's own colors, once, before the first
121
+ * {@link getHighlighter} call — the catppuccin pair is registered with these surfaces baked in
122
+ * (Monaco reads them from the theme, static rendering too), and CodeMirror's chrome reads them
123
+ * at mount. A scheme left out keeps the flavor's own surfaces; a role left out keeps that
124
+ * role's flavor color; a later call replaces the schemes it names. Process-global like the
125
+ * highlighter itself, for the same reason: one palette across every editor and rendered block.
126
+ *
127
+ * Every value must be a CSS color literal — Monaco resolves theme colors itself, so a custom
128
+ * property reference would silently paint the wrong color; it is refused here instead.
129
+ */
130
+ declare function configureEditorSurface(surfaces: EditorSurfaces): void;
131
+ /**
132
+ * The surface configured for a color scheme through {@link configureEditorSurface}, or
133
+ * undefined when the flavor's own surfaces stand.
134
+ */
135
+ declare function getEditorSurface(dark: boolean): EditorSurface | undefined;
44
136
  /**
45
137
  * The shared Shiki highlighter, built lazily on first call.
46
138
  *
@@ -60,4 +152,4 @@ declare function getHighlighter(): Promise<HighlighterCore$1>;
60
152
  */
61
153
  declare function shikiThemeId(dark: boolean): string;
62
154
  //#endregion
63
- export { CODE_FONT_FAMILY, DEFAULT_HIGHLIGHTER_LANGS, type HighlighterConfig, type HighlighterCore, INKSTONE_DARK_THEME, INKSTONE_LIGHT_THEME, configureHighlighter, getHighlighter, shikiThemeId };
155
+ export { CODE_FONT_FAMILY, DEFAULT_HIGHLIGHTER_LANGS, type EditorSurface, type EditorSurfaces, type HighlighterConfig, type HighlighterCore, INKSTONE_DARK_THEME, INKSTONE_LIGHT_THEME, type RadiusSize, type ShadowSize, configureEditorSurface, configureHighlighter, getEditorSurface, getHighlighter, inkstoneRadius, inkstoneShadow, shikiThemeId, surfacedTheme };
package/dist/index.js CHANGED
@@ -9,6 +9,63 @@ import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
9
9
  //#region src/code-font.ts
10
10
  const CODE_FONT_FAMILY = "var(--inkstone-font-family-code, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace)";
11
11
  //#endregion
12
+ //#region src/surface.ts
13
+ function inkstoneRadius(size, fallback) {
14
+ return `var(--inkstone-radius-${size}, ${fallback})`;
15
+ }
16
+ function inkstoneShadow(size, fallback) {
17
+ return `var(--inkstone-shadow-${size}, ${fallback})`;
18
+ }
19
+ const SURFACE_COLORS = {
20
+ background: [
21
+ "editor.background",
22
+ "editorGutter.background",
23
+ "editorCursor.background",
24
+ "editorOverviewRuler.background",
25
+ "editorGroup.emptyBackground",
26
+ "editorStickyScroll.background",
27
+ "minimap.background",
28
+ "input.background"
29
+ ],
30
+ lineHighlight: ["editor.lineHighlightBackground", "editorStickyScrollHover.background"],
31
+ lineNumber: ["editorLineNumber.foreground"],
32
+ widget: [
33
+ "editorWidget.background",
34
+ "editorSuggestWidget.background",
35
+ "editorHoverWidget.background",
36
+ "dropdown.background"
37
+ ],
38
+ widgetBorder: [
39
+ "editorWidget.border",
40
+ "editorWidget.resizeBorder",
41
+ "editorSuggestWidget.border",
42
+ "editorHoverWidget.border",
43
+ "dropdown.border"
44
+ ],
45
+ widgetSelection: [
46
+ "editorSuggestWidget.selectedBackground",
47
+ "list.activeSelectionBackground",
48
+ "list.inactiveSelectionBackground",
49
+ "list.focusBackground",
50
+ "list.hoverBackground"
51
+ ]
52
+ };
53
+ function surfacedTheme(theme, surface) {
54
+ const roles = Object.entries(SURFACE_COLORS);
55
+ const colors = { ...theme.colors };
56
+ let changed = false;
57
+ for (const [role, keys] of roles) {
58
+ const value = surface?.[role];
59
+ if (value === void 0) continue;
60
+ for (const key of keys) colors[key] = value;
61
+ changed = true;
62
+ }
63
+ return changed ? {
64
+ ...theme,
65
+ colors
66
+ } : theme;
67
+ }
68
+ //#endregion
12
69
  //#region src/shiki.ts
13
70
  const INKSTONE_LIGHT_THEME = "catppuccin-latte";
14
71
  const INKSTONE_DARK_THEME = "catppuccin-macchiato";
@@ -25,28 +82,49 @@ function getHighlighterRegistry() {
25
82
  if (!registry) {
26
83
  registry = {
27
84
  config: {},
85
+ surfaces: {},
28
86
  promise: null
29
87
  };
30
88
  host[HIGHLIGHTER_REGISTRY_KEY] = registry;
31
89
  }
90
+ registry.surfaces ??= {};
32
91
  return registry;
33
92
  }
93
+ function assertConfigurable(registry, caller) {
94
+ if (registry.promise) throw new Error(`${caller} must be called before the first getHighlighter()`);
95
+ }
34
96
  function configureHighlighter(options) {
35
97
  const registry = getHighlighterRegistry();
36
- if (registry.promise) throw new Error("configureHighlighter must be called before the first getHighlighter()");
98
+ assertConfigurable(registry, "configureHighlighter");
37
99
  registry.config = {
38
100
  ...registry.config,
39
101
  ...options
40
102
  };
41
103
  }
104
+ function configureEditorSurface(surfaces) {
105
+ const registry = getHighlighterRegistry();
106
+ assertConfigurable(registry, "configureEditorSurface");
107
+ for (const [scheme, surface] of Object.entries(surfaces)) {
108
+ const roles = Object.entries(surface ?? {});
109
+ for (const [role, value] of roles) if (typeof value === "string" && value.includes("var(")) throw new Error(`configureEditorSurface: ${scheme}.${role} must be a CSS color literal, not a custom property reference (${value})`);
110
+ }
111
+ registry.surfaces = {
112
+ ...registry.surfaces,
113
+ ...surfaces
114
+ };
115
+ }
116
+ function getEditorSurface(dark) {
117
+ const { surfaces } = getHighlighterRegistry();
118
+ return dark ? surfaces.dark : surfaces.light;
119
+ }
42
120
  function getHighlighter() {
43
121
  const registry = getHighlighterRegistry();
44
122
  const existing = registry.promise;
45
123
  if (existing) return existing;
46
124
  const attempt = createHighlighterCore({
47
125
  themes: [
48
- catppuccinLatte,
49
- catppuccinMacchiato,
126
+ surfacedTheme(catppuccinLatte, registry.surfaces.light),
127
+ surfacedTheme(catppuccinMacchiato, registry.surfaces.dark),
50
128
  ...registry.config.themes ?? []
51
129
  ],
52
130
  langs: registry.config.langs ?? [...DEFAULT_HIGHLIGHTER_LANGS],
@@ -62,4 +140,4 @@ function shikiThemeId(dark) {
62
140
  return dark ? INKSTONE_DARK_THEME : INKSTONE_LIGHT_THEME;
63
141
  }
64
142
  //#endregion
65
- export { CODE_FONT_FAMILY, DEFAULT_HIGHLIGHTER_LANGS, INKSTONE_DARK_THEME, INKSTONE_LIGHT_THEME, configureHighlighter, getHighlighter, shikiThemeId };
143
+ export { CODE_FONT_FAMILY, DEFAULT_HIGHLIGHTER_LANGS, INKSTONE_DARK_THEME, INKSTONE_LIGHT_THEME, configureEditorSurface, configureHighlighter, getEditorSurface, getHighlighter, inkstoneRadius, inkstoneShadow, shikiThemeId, surfacedTheme };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coldsmirk/inkstone-core",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "Shared Shiki highlighter service for the inkstone editor toolkit: one fine-grained singleton (catppuccin themes, JS regex engine) reused by Monaco, static rendering, and markdown surfaces.",
5
5
  "keywords": [
6
6
  "shiki",