gutterpress 0.10.8 → 0.10.9

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.
Files changed (59) hide show
  1. package/README.md +93 -90
  2. package/dist/{README-aaqapam1.md → README-6c7fda1s.md} +9 -7
  3. package/dist/{README-mdq34cmb.md → README-jntqyj1z.md} +19 -10
  4. package/dist/api/index.d.ts +8 -8
  5. package/dist/api/index.js +46 -46
  6. package/dist/{audit-m3ce9pvq.js → audit-fq7s1gsz.js} +4 -4
  7. package/dist/{build-qnecb3a1.js → build-dyat3fqd.js} +5 -8
  8. package/dist/checks/source/dangling-links.d.ts +18 -0
  9. package/dist/checks/source/index.d.ts +1 -0
  10. package/dist/checks/source/local-ref-parser.d.ts +13 -0
  11. package/dist/{cli-1hx14cgk.js → cli-8pg731p5.js} +2941 -2927
  12. package/dist/{cli-4vtgt2ng.js → cli-bx950tb4.js} +7 -7
  13. package/dist/{cli-pfpb1t3h.js → cli-bzxqtvrk.js} +1 -2
  14. package/dist/{cli-vcv5ewqj.js → cli-rh05byck.js} +2 -9
  15. package/dist/{cli-vxf151ea.js → cli-ypfvnhag.js} +1 -1
  16. package/dist/cli.js +24 -53
  17. package/dist/{doctor-qvqye1wd.js → doctor-yr85n080.js} +2 -2
  18. package/dist/{engine-daad33qn.js → engine-19mshh07.js} +2 -2
  19. package/dist/{engine-db8632w6.js → engine-d46wgg99.js} +3 -3
  20. package/dist/ext-jrg8c48s.js +268 -0
  21. package/dist/{index-rpt4vtby.js → index-dhgzkhs7.js} +1 -1
  22. package/dist/{index-dfw8hn8q.js → index-mey5vraz.js} +5852 -5899
  23. package/dist/{index-9pbaznww.js → index-zwcv1njy.js} +7 -7
  24. package/dist/index.js +47 -47
  25. package/dist/lib/build-runner.d.ts +0 -7
  26. package/dist/lib/build-staging.d.ts +36 -0
  27. package/dist/lib/cli-args.d.ts +0 -7
  28. package/dist/lib/{theme-import.d.ts → extension-import.d.ts} +20 -11
  29. package/dist/lib/extension-manager.d.ts +170 -0
  30. package/dist/lib/extension-manifest.d.ts +25 -17
  31. package/dist/lib/extension-specifier.d.ts +31 -0
  32. package/dist/lib/manifest-doc.d.ts +3 -9
  33. package/dist/lib/markdown/markers.d.ts +2 -2
  34. package/dist/lib/markdown/plugins.d.ts +4 -4
  35. package/dist/lib/missing-asset-placeholder.d.ts +11 -0
  36. package/dist/lib/presets.d.ts +1 -1
  37. package/dist/lib/publish/providers/gdrive.d.ts +1 -1
  38. package/dist/lib/snippets.d.ts +11 -15
  39. package/dist/lib/style-resolver.d.ts +1 -1
  40. package/dist/{lint-3p1s8agk.js → lint-enasqbqx.js} +4 -4
  41. package/dist/{manifest.schema-1q35yxs9.json → manifest.schema-hsn8rsgr.json} +18 -53
  42. package/dist/{new-xffarp40.js → new-r9c99wsn.js} +11 -12
  43. package/dist/{plugin.test.js-mv0ghv5n.tpl → plugin.test.js-npnfhyca.tpl} +0 -1
  44. package/dist/{preflight-z6xpty9w.js → preflight-shk7vqbs.js} +4 -4
  45. package/dist/preview/file-watcher.d.ts +4 -4
  46. package/dist/preview/lifecycle.d.ts +1 -1
  47. package/dist/{preview-xa1n7why.js → preview-52918mxk.js} +6 -10
  48. package/dist/{publish-zgmr99kd.js → publish-8s60vhx7.js} +4 -4
  49. package/dist/schema/manifest.types.d.ts +28 -44
  50. package/dist/{theme-cd6efzjw.json → theme-etj4gxck.json} +4 -1
  51. package/dist/{theme-tqfypc1w.json → theme-s2rg7xe7.json} +4 -1
  52. package/dist/{theme-xt0hjb1b.json → theme-vktedy8q.json} +4 -1
  53. package/dist/types.d.ts +0 -7
  54. package/dist/{validate-43hqgzz5.js → validate-awccx32r.js} +4 -4
  55. package/package.json +1 -1
  56. package/dist/lib/plugin-manager.d.ts +0 -106
  57. package/dist/lib/theme-manager.d.ts +0 -242
  58. package/dist/plugin-b05ck7zd.js +0 -126
  59. package/dist/theme-m9dtxkhj.js +0 -323
@@ -11,17 +11,17 @@ import {
11
11
  publishProviderFor,
12
12
  resolvePublishFormat,
13
13
  runPublish
14
- } from "./cli-1hx14cgk.js";
14
+ } from "./cli-8pg731p5.js";
15
15
  import {
16
16
  FileTokenStore,
17
17
  log
18
- } from "./cli-vxf151ea.js";
18
+ } from "./cli-ypfvnhag.js";
19
19
  import {
20
20
  UsageError,
21
21
  rejectExtraPositionals,
22
22
  rejectUnknownFlags
23
- } from "./cli-vcv5ewqj.js";
24
- import"./cli-4vtgt2ng.js";
23
+ } from "./cli-rh05byck.js";
24
+ import"./cli-bx950tb4.js";
25
25
  import"./cli-c41yr7he.js";
26
26
  import {
27
27
  EXIT_CODES
@@ -1,19 +1,19 @@
1
1
  type CheckSeverity = "error" | "warning" | "info";
2
- export interface PluginConfig {
3
- path?: string;
4
- name?: string;
5
- /** Exact project-local npm version. Legacy ranges remain informational. */
6
- version?: string;
7
- /** Named module export to use when the package has no default plugin export. */
8
- export?: string;
2
+ /**
3
+ * The object form of an `extensions:` entry (#265) — only for an entry that
4
+ * has to say more than its specifier. The common case is the bare string;
5
+ * `use` carries the same specifier in both forms.
6
+ */
7
+ export interface ExtensionConfig {
9
8
  /**
10
- * Load order (default 100). Higher loads first; built-in plugins always
11
- * load before any user plugin regardless of this value. Advanced/rarely
12
- * needed: a plugin needs the LOWER of two priorities to see tokens the
13
- * other one produces, since it must load (and run) after it — see
14
- * examples/gutterpress-user-guide/05-plugins.md#plugin-load-order.
9
+ * The specifier: a bundled feature name, a `./`/`../`/`/` path (relative
10
+ * to the manifest), or an npm package name, optionally pinned as
11
+ * `name@version`. See `lib/extension-specifier.ts` for the three rules.
15
12
  */
16
- priority?: number;
13
+ use: string;
14
+ /** Named module export to use when the package has no default plugin export. */
15
+ export?: string;
16
+ /** Options passed straight through to the markdown-it plugin. */
17
17
  options?: Record<string, unknown>;
18
18
  /**
19
19
  * Per-project enable flag (#30). Absent or `true` = active; `false` = the
@@ -93,24 +93,6 @@ export interface GutterpressManifest {
93
93
  publish?: PublishSettings;
94
94
  /** How the book is designed (ADR 0008). The registry in lib/presets.ts is authoritative. */
95
95
  preset?: "dtrpg" | "book" | "custom";
96
- /**
97
- * Pagination engine. The Gutterpress engine (`src/engine/`, native Chromium
98
- * pagination) is the only engine. This field and `--engine` on the CLI are
99
- * accepted-but-ignored for backward compatibility: an explicit "paged"
100
- * produces a one-line warning and the build proceeds natively regardless.
101
- */
102
- engine?: "paged" | "native";
103
- /**
104
- * Engine-conditional stylesheets, appended AFTER `styles`. `.native` is the
105
- * only list this type declares; a manifest that still carries `.paged`
106
- * (dual-engine era, pre-0.10.7) keeps parsing — `resolveConfig` reads it
107
- * through a widened cast and warns once — but it is no longer part of the
108
- * authored/autocompleted shape (see `manifest.schema.json`, which drops it
109
- * too), since the native engine is the only engine.
110
- */
111
- engineStyles?: {
112
- native?: string[];
113
- };
114
96
  /**
115
97
  * Where the book is published (ADR 0008): publish-target ids whose
116
98
  * validation policies this book is checked against. Absent = the preset's
@@ -119,7 +101,13 @@ export interface GutterpressManifest {
119
101
  */
120
102
  targets?: string[];
121
103
  styles?: string[];
122
- plugins?: (string | PluginConfig)[];
104
+ /**
105
+ * Extensions, in load order (#265): markdown-it plugins, looks, component
106
+ * libraries — from npm, a folder, or the bundled set. A later entry loads
107
+ * later, sees earlier entries' markdown output, and its CSS wins ties; the
108
+ * project's own `styles` always load after every extension.
109
+ */
110
+ extensions?: (string | ExtensionConfig)[];
123
111
  source?: {
124
112
  files?: string[] | null;
125
113
  };
@@ -203,14 +191,17 @@ export interface GutterpressManifest {
203
191
  };
204
192
  };
205
193
  }
206
- export interface ResolvedPluginConfig {
194
+ export interface ResolvedExtensionConfig {
195
+ /** The specifier exactly as written (pin included) — for messages and manifest edits. */
196
+ use: string;
197
+ /** The path specifier, for a path entry; the loader resolves it against the manifest directory. */
207
198
  path?: string;
199
+ /** The package name, for a bundled or npm entry. */
208
200
  name?: string;
209
- /** Exact version for a project-local vendored npm plugin. */
201
+ /** Exact version for a project-local vendored npm extension. */
210
202
  version?: string;
211
203
  /** Named module export selected as the plugin function. */
212
204
  export?: string;
213
- priority: number;
214
205
  options: Record<string, unknown>;
215
206
  }
216
207
  /**
@@ -224,18 +215,11 @@ export interface ResolvedPluginConfig {
224
215
  export interface ResolvedConfig {
225
216
  title: string;
226
217
  authors: string[];
227
- /**
228
- * Resolved pagination engine. Always `"native"` — `manifest.ts` assigns the
229
- * literal, warning and ignoring an author's `engine: paged`. Typed as the
230
- * single value it can hold so no consumer can branch on an engine that
231
- * cannot run. The INPUT union ({@link GutterpressManifest.engine}) keeps
232
- * both spellings, because old manifests must go on parsing.
233
- */
234
- engine: "native";
235
218
  /** Validated publish-target ids for this book (may be empty). */
236
219
  targets: string[];
237
220
  styles?: string[];
238
- plugins: ResolvedPluginConfig[];
221
+ /** Enabled extensions, in manifest (= load = cascade) order. */
222
+ extensions: ResolvedExtensionConfig[];
239
223
  source: {
240
224
  files: string[] | null;
241
225
  };
@@ -2,5 +2,8 @@
2
2
  "name": "Zine",
3
3
  "author": "gutterpress",
4
4
  "description": "High-contrast, punchy sans-serif look for short photocopier-friendly zines.",
5
- "preview": null
5
+ "preview": null,
6
+ "styles": [
7
+ "theme.css"
8
+ ]
6
9
  }
@@ -2,5 +2,8 @@
2
2
  "name": "Technical Document",
3
3
  "author": "gutterpress",
4
4
  "description": "Clean sans-serif manual look with clear hierarchy, code styling, and tidy tables.",
5
- "preview": null
5
+ "preview": null,
6
+ "styles": [
7
+ "theme.css"
8
+ ]
6
9
  }
@@ -2,5 +2,8 @@
2
2
  "name": "Clean Book",
3
3
  "author": "gutterpress",
4
4
  "description": "A calm, classic book look: serif body, generous margins, restrained accents.",
5
- "preview": null
5
+ "preview": null,
6
+ "styles": [
7
+ "theme.css"
8
+ ]
6
9
  }
package/dist/types.d.ts CHANGED
@@ -27,11 +27,4 @@ export interface PreviewServerOptions {
27
27
  noWatch: boolean;
28
28
  /** Automatically open browser (default: true) */
29
29
  openBrowser: boolean;
30
- /**
31
- * CLI `--engine` override, fed into {@link resolveConfig} the same way
32
- * `build`'s does — preview and PDF switch together, per project, behind
33
- * one flag. `undefined` defers to the
34
- * manifest's `engine:` field (or the "native" default).
35
- */
36
- engine?: "paged" | "native";
37
30
  }
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  executeAndReport
3
- } from "./cli-1hx14cgk.js";
3
+ } from "./cli-8pg731p5.js";
4
4
  import {
5
5
  log
6
- } from "./cli-vxf151ea.js";
6
+ } from "./cli-ypfvnhag.js";
7
7
  import {
8
8
  UsageError,
9
9
  rejectExtraPositionals,
10
10
  rejectUnknownFlags
11
- } from "./cli-vcv5ewqj.js";
12
- import"./cli-4vtgt2ng.js";
11
+ } from "./cli-rh05byck.js";
12
+ import"./cli-bx950tb4.js";
13
13
  import"./cli-c41yr7he.js";
14
14
  import {
15
15
  EXIT_CODES
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gutterpress",
3
- "version": "0.10.8",
3
+ "version": "0.10.9",
4
4
  "description": "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
5
5
  "author": "itlackey",
6
6
  "license": "MPL-2.0",
@@ -1,106 +0,0 @@
1
- import { type NpmPluginInstallOptions } from "./npm-plugin-installer";
2
- /** Folder (relative to the project root) imported local plugins are copied to. */
3
- export { PLUGINS_DIR } from "./plugin-vendor";
4
- /** How a plugin entry is referenced in the manifest. */
5
- export type PluginKind = "local" | "npm";
6
- /** One configured plugin, as surfaced to the manager UI. */
7
- export interface ProjectPluginEntry {
8
- /** Stable reference: the manifest `path` (local) or `name` (npm). */
9
- ref: string;
10
- /** `"local"` when referenced by file path, `"npm"` when by package name. */
11
- kind: PluginKind;
12
- /** Per-project enable flag. Absent in the manifest defaults to `true`. */
13
- enabled: boolean;
14
- /** Exact installed npm version. Absent for local, built-in, and legacy entries. */
15
- version?: string;
16
- /** Named module export selected as the plugin function. */
17
- export?: string;
18
- /** Non-fatal install notices, currently used for legacy SHA-1 registry entries. */
19
- warnings?: string[];
20
- }
21
- interface PluginManagerInstallOptions extends NpmPluginInstallOptions {
22
- exportName?: string;
23
- __testFailBeforeManifestCommit?: () => void | Promise<void>;
24
- }
25
- /** Result of attempting to load one configured plugin. */
26
- export interface PluginValidationResult {
27
- ref: string;
28
- kind: PluginKind;
29
- /** Mirrors the manifest enable flag. Disabled plugins are not load-tested. */
30
- enabled: boolean;
31
- /** `true` when the plugin loaded OK (or is disabled and skipped). */
32
- ok: boolean;
33
- /** The loader's fail-fast error message when `ok` is `false`. */
34
- error?: string;
35
- }
36
- /** A curated markdown feature an author can turn on. */
37
- export interface RecommendedPlugin {
38
- /** The npm package name (also the manifest entry that "Add" writes). */
39
- name: string;
40
- /** Short, plain-language feature name shown as the row title (the package
41
- * name is demoted to secondary text — a non-technical author shouldn't have
42
- * to read `markdown-it-*` ids to pick a feature). */
43
- label: string;
44
- /** One-line author-friendly description. */
45
- description: string;
46
- /**
47
- * True when gutterpress ships this plugin (see `BUILTIN_OPTIONAL_PLUGINS`):
48
- * "Add" enables it instantly, no install, works offline. All entries below
49
- * are built-in — the always-on defaults (attrs/footnote/deflist, applied
50
- * unconditionally in renderer.ts) are intentionally NOT listed here, since
51
- * recommending the author "add" something already active is pure confusion.
52
- */
53
- builtin: true;
54
- }
55
- /**
56
- * Curated, BUILT-IN opt-in markdown features. Each is bundled with gutterpress
57
- * (`BUILTIN_OPTIONAL_PLUGINS`), so clicking "Add" writes the manifest entry AND
58
- * the feature works immediately — no terminal, no install, offline. This is the
59
- * non-technical-author happy path: "turn on a feature → it works".
60
- */
61
- export declare const RECOMMENDED_PLUGINS: RecommendedPlugin[];
62
- /**
63
- * List the project's configured plugins with their per-project enable flag.
64
- * Returns `[]` when there is no manifest or no `plugins:` list.
65
- *
66
- * For object-form entries, the kind is taken from the manifest KEY (`path:` →
67
- * local, `name:` → npm) rather than guessing from the value. This correctly
68
- * classifies e.g. `path: plugins/foo.js` (no `./` prefix) as local. Scalar
69
- * (string) entries fall back to the {@link isLocalRef} heuristic.
70
- */
71
- export declare function listProjectPlugins(projectDir: string): Promise<ProjectPluginEntry[]>;
72
- /**
73
- * Set the per-project enabled flag for the plugin identified by `ref` (its
74
- * path or npm name). A string-form entry is normalised to an object entry so
75
- * the flag can be attached. Persists to the manifest (preserving comments).
76
- * Throws when no entry matches `ref`.
77
- */
78
- export declare function setPluginEnabled(projectDir: string, ref: string, enabled: boolean): Promise<void>;
79
- /**
80
- * Install an npm markdown-it plugin without an external package manager, then
81
- * record its exact version in the manifest. Built-in optional plugins remain
82
- * offline: adding one only records its bundled package name.
83
- */
84
- export declare function addNpmPlugin(projectDir: string, packageSpec: string, exportName?: string): Promise<ProjectPluginEntry>;
85
- /** @internal Dependency/fault injection for focused installer tests. */
86
- export declare function addNpmPluginWithOptions(projectDir: string, packageSpec: string, options?: PluginManagerInstallOptions): Promise<ProjectPluginEntry>;
87
- /**
88
- * Import a local plugin by copying the source file OR folder into the
89
- * project's `plugins/` directory and adding a manifest entry that references
90
- * the copied path (so the plugin travels with the project). Returns the new
91
- * entry. Throws when `sourcePath` does not exist.
92
- */
93
- export declare function addLocalPlugin(projectDir: string, sourcePath: string): Promise<ProjectPluginEntry & {
94
- path: string;
95
- }>;
96
- /**
97
- * Validate every configured plugin by attempting to LOAD it through the
98
- * existing lib loader (`loadPlugin`). Disabled plugins are reported as
99
- * disabled and NOT load-tested. Loader errors are CAUGHT and surfaced per
100
- * plugin (never thrown through) so the UI can flag erroring plugins clearly.
101
- *
102
- * This reuses the fail-fast loader path verbatim (§5) — including its
103
- * "not found / install it" message for unresolvable npm packages — rather than
104
- * reimplementing resolution.
105
- */
106
- export declare function validateProjectPlugins(projectDir: string): Promise<PluginValidationResult[]>;
@@ -1,242 +0,0 @@
1
- import { type ExtensionMetadata, assertExtensionContained, extensionEngineStyleList } from "./extension-manifest.ts";
2
- /** Folder (relative to the project root) themes are copied into on apply/import. */
3
- export declare const THEMES_DIR = "themes";
4
- /** The built-in themes shipped as embedded assets (ids are folder names). */
5
- export declare const BUILT_IN_THEME_IDS: readonly ["clean-book", "zine", "technical-doc"];
6
- export type BuiltInThemeId = (typeof BUILT_IN_THEME_IDS)[number];
7
- /**
8
- * Parsed theme metadata (every field optional in the file) — #241: a theme is
9
- * the degenerate "styles only" case of {@link ExtensionMetadata}, so this is
10
- * now a plain alias rather than a separate type. A `theme.json` file only
11
- * ever populates the fields this comment used to document in full
12
- * (`name`/`author`/`description`/`preview`/`styles`/`engineStyles`/
13
- * `tokensFile`, #239); the three newer fields (`markdown`/`components`/
14
- * `snippets`) are only reachable via a `gutterpress.json` — see
15
- * `extension-manifest.ts` for the complete, current field-by-field doc.
16
- */
17
- export type ThemeMetadata = ExtensionMetadata;
18
- /** Author-friendly metadata for one theme (built-in or project). */
19
- export interface ThemeInfo {
20
- /** Stable id (a built-in id, or a slug for imported/applied themes). */
21
- id: string;
22
- /** Display name. */
23
- name: string;
24
- /** Theme author, when known. */
25
- author?: string;
26
- /** One-line description. */
27
- description: string;
28
- /** `"builtin"` (embedded) or `"project"` (copied into the project). */
29
- kind: "builtin" | "project";
30
- /** Optional preview image path relative to the theme folder. */
31
- preview?: string | null;
32
- /**
33
- * #239 — the theme's declared stylesheets, relative to the theme folder, in
34
- * cascade order. Always populated (defaults to `["theme.css"]` when the
35
- * theme declares no `styles`), so a caller never needs to re-derive the
36
- * default itself.
37
- */
38
- styles: string[];
39
- /**
40
- * #239 — which declared sheet (relative to the theme folder) carries the
41
- * `:root` token surface — see {@link ThemeMetadata.tokensFile}. ALWAYS
42
- * populated: an explicit `theme.json` declaration wins, otherwise this
43
- * defaults to the theme's primary sheet (`styles[0]`) — `"theme.css"` for
44
- * every legacy single-sheet theme, exactly as `styles` itself defaults.
45
- * A caller (the Design panel) never needs its own "guess the token sheet"
46
- * fallback.
47
- */
48
- tokensFile: string;
49
- /**
50
- * #241 — declared-relative path to a markdown-it entry, when this theme's
51
- * `gutterpress.json` names one (never possible via the legacy `theme.json`
52
- * shape). Informational only: the theme verbs (list/apply/import/revert/
53
- * remove) do not load or wire it — a full extension carrying both styles
54
- * AND markdown installs via `gutterpress plugin add <folder>` instead (see
55
- * `plugins.ts`'s `loadExtensionFromDir`), which resolves and applies this
56
- * SAME field. Surfaced here so a theme card can at least show "this
57
- * extension also has a plugin half" rather than staying silent about it.
58
- */
59
- markdown?: string;
60
- /** #241 — declared-relative path to a component catalog file, informational
61
- * only (same rationale as {@link markdown}); no catalog reader consumes
62
- * this yet. */
63
- components?: string;
64
- /** #241 — declared-relative path to a snippets folder, informational only
65
- * on THIS type (same rationale as {@link markdown} — the theme verbs
66
- * don't load it). The active theme's copy of this field IS consumed,
67
- * by `snippets.ts`'s `listMergedSnippets` (#242) via `getActiveTheme`. */
68
- snippets?: string;
69
- }
70
- /** A built-in theme resolved to disk (extracted from the embedded assets). */
71
- export interface ResolvedTheme {
72
- info: ThemeInfo;
73
- /** Absolute path to the theme's `theme.css` (in the extracted assets dir). */
74
- cssPath: string;
75
- /** Absolute path to the theme's folder. */
76
- dir: string;
77
- }
78
- /** Which theme to apply: a built-in id, or a project theme already on disk. */
79
- export type ApplyThemeTarget = {
80
- kind: "builtin";
81
- id: BuiltInThemeId | string;
82
- } | {
83
- kind: "project";
84
- id: string;
85
- };
86
- /**
87
- * A theme's declared stylesheets, relative to its folder, in cascade order
88
- * (#239). An absent OR empty `styles` defaults to `["theme.css"]` — the
89
- * single-file format every theme published before this field existed already
90
- * satisfies, so nothing needs migrating. Exported for `theme-import.ts`,
91
- * which validates every one of these (existence + print-safety) at import
92
- * time, the same way it always validated the lone `theme.css`.
93
- *
94
- * #241: layers that theme-only default on top of `extension-manifest.ts`'s
95
- * {@link extensionStyleList}, which does NOT apply it (a markdown-only
96
- * extension has no reason to require a `theme.css` it never declared) — one
97
- * shared "read the declared list" body, two defaulting rules.
98
- */
99
- export declare function themeStyleList(meta: ThemeMetadata): string[];
100
- /**
101
- * Every declared sheet (styles/engineStyles — #241: now also `markdown`/
102
- * `components`/`snippets`/`tokensFile`, since a `gutterpress.json`-formatted
103
- * theme can declare them too) must live INSIDE the theme folder. A theme is
104
- * self-contained by contract (apply copies the whole folder), and an imported
105
- * package is untrusted input: a `../` or absolute entry would make apply read
106
- * a file from anywhere on disk into the book.
107
- *
108
- * This is a WRITE-BOUNDARY guard — `applyTheme`, `importThemeFromFolder` and
109
- * `theme-import.ts`'s package validation call it before copying anything or
110
- * wiring a manifest. It deliberately does NOT live in {@link themeStyleList},
111
- * which the read paths (`listProjectThemes`, `getActiveTheme`,
112
- * `getPreviousTheme`, `readThemeCss`) also call: one hand-edited theme.json
113
- * must not take down listing every theme, the same reason `readThemeMeta`
114
- * returns `{}` for an unparseable file instead of throwing.
115
- *
116
- * #241 — a straight re-export of the extension-generic
117
- * {@link assertExtensionContained}: a theme's containment rule is not a
118
- * separate implementation, it IS the extension rule, applied to a metadata
119
- * object that (for a `theme.json`-only folder) never has the three newer
120
- * fields populated in the first place.
121
- */
122
- export declare const assertThemeSheetsContained: typeof assertExtensionContained;
123
- /**
124
- * A theme's declared engine-conditional sheets, relative to its folder
125
- * (#239). `Array.isArray` guards a malformed `engineStyles.native` (authored
126
- * as something other than a list) — treated as "none declared" rather than a
127
- * hard crash on a JSON author's typo. Exported for `theme-import.ts`'s import
128
- * validation, same reuse rationale as {@link themeStyleList}.
129
- *
130
- * #241 — a straight alias: `engineStyles.native` never had a theme-specific
131
- * default to layer on, so this is byte-identical to
132
- * {@link extensionEngineStyleList}.
133
- */
134
- export declare const themeEngineStyleList: typeof extensionEngineStyleList;
135
- /** List the built-in themes (metadata read from the extracted embedded assets). */
136
- export declare function listBuiltInThemes(): Promise<ThemeInfo[]>;
137
- /** Resolve a built-in theme to its extracted `theme.css` + parsed metadata. */
138
- export declare function resolveBuiltInTheme(id: string): Promise<ResolvedTheme>;
139
- /**
140
- * List the themes that live inside the project under `themes/<id>/` (each a
141
- * folder whose PRIMARY declared sheet exists — `theme.css` by default, or
142
- * `theme.json`'s `styles[0]` for a multi-sheet theme, #239). Returns `[]`
143
- * when there is no `themes/` folder.
144
- */
145
- export declare function listProjectThemes(projectDir: string): Promise<ThemeInfo[]>;
146
- /**
147
- * Read the project's currently active theme (the theme with an entry under
148
- * `themes/<id>/…` in the manifest `styles:` list AND whose folder's PRIMARY
149
- * declared sheet still exists on disk, #239). Returns `null` when no theme is
150
- * applied.
151
- */
152
- export declare function getActiveTheme(projectDir: string): Promise<ThemeInfo | null>;
153
- /**
154
- * Read the project's "previous theme" — the theme active immediately before the
155
- * current one (#106). Returns `null` when there is no recorded previous theme,
156
- * when its folder no longer exists, or when it is (somehow) already the active
157
- * theme. The reference is persisted indefinitely (no timer) in the manifest.
158
- */
159
- export declare function getPreviousTheme(projectDir: string): Promise<ThemeInfo | null>;
160
- /**
161
- * Revert to the previously active theme (#106): re-apply the theme recorded by
162
- * {@link getPreviousTheme}. Because {@link applyTheme} itself records the
163
- * now-current theme as the new "previous", reverting is a toggle — revert again
164
- * returns to where you were. Throws when there is no previous theme to revert to.
165
- */
166
- export declare function revertTheme(projectDir: string): Promise<ThemeInfo>;
167
- /**
168
- * Apply a theme to the project: COPY the theme folder into `themes/<id>/`
169
- * (built-in themes are copied out of the embedded assets; project themes are
170
- * already present) and wire the manifest so its `theme.css` is the active
171
- * stylesheet. Returns the applied {@link ThemeInfo}.
172
- *
173
- * No-data-loss mandate (UX review M6): a project theme's `theme.css` is the
174
- * exact file the Design panel writes token edits into, so re-copying a
175
- * built-in over an EXISTING `themes/<id>/` would silently discard every
176
- * customization the author made after the first apply. Applying a built-in
177
- * therefore never overwrites an existing project theme folder — if one is
178
- * already there (customized or not), the built-in is copied into a fresh id
179
- * (via the same {@link uniqueThemeId} helper import/folder-import uses) and
180
- * THAT becomes active, leaving the original folder untouched. In the normal UI flow the
181
- * Appearance grid hides a built-in card once its project copy exists (so this
182
- * path isn't reachable by clicking Apply twice); this guard is the
183
- * defense-in-depth backstop for any other caller of this function.
184
- */
185
- export declare function applyTheme(projectDir: string, target: ApplyThemeTarget): Promise<ThemeInfo>;
186
- /**
187
- * Import a theme from a local folder by copying it into the project's
188
- * `themes/<id>/`. The folder MUST contain its PRIMARY declared stylesheet —
189
- * `theme.css` by default, or `theme.json`'s `styles[0]` for a multi-sheet
190
- * theme (#239) that ships no `theme.css` at all. Metadata comes from its
191
- * `theme.json` (synthesised from the folder name when absent). The imported
192
- * theme becomes available to {@link applyTheme}; importing does NOT auto-apply.
193
- */
194
- export declare function importThemeFromFolder(projectDir: string, sourceDir: string): Promise<ThemeInfo>;
195
- /**
196
- * Import a theme from a URL using the global `fetch` (bundle-safe — no node http
197
- * client). Two shapes are supported:
198
- *
199
- * - RAW CSS: a `…/theme.css` (or any `.css`) URL → fetched as the theme's
200
- * `theme.css`; metadata is synthesised from the URL.
201
- * - THEME FOLDER: a base URL (no `.css`) → we fetch `<base>/theme.json`
202
- * (optional) and `<base>/theme.css` (required) to assemble the theme.
203
- *
204
- * The fetched theme is written into the project's `themes/<id>/`. Bundled fonts
205
- * are NOT followed for URL imports (a single CSS file + optional metadata) to
206
- * keep the fetch surface small and predictable; authors wanting bundled assets
207
- * use folder import.
208
- */
209
- export declare function importThemeFromUrl(projectDir: string, url: string): Promise<ThemeInfo>;
210
- /**
211
- * Read a theme's CSS for previewing. Built-in themes read from the embedded
212
- * assets; project themes read and concatenate EVERY sheet `theme.json`
213
- * declares (#239 — `theme.css` alone when it declares none), in cascade
214
- * order. Used by the host to feed the renderer a sample-render thumbnail (the
215
- * renderer never touches fs) — a read-only preview, so concatenation is fine
216
- * even though it wouldn't be for an editable surface.
217
- */
218
- export declare function readThemeCss(projectDir: string | null, source: {
219
- kind: "builtin" | "project";
220
- id: string;
221
- }): Promise<string>;
222
- /**
223
- * Remove an imported/applied project theme folder. If it was the active theme,
224
- * its ENTIRE block is dropped too — every `styles:` entry AND every
225
- * `engineStyles.native` entry under `themes/<id>/…` (#239), not just a single
226
- * `theme.css` href. Never touches built-in (embedded) themes.
227
- */
228
- export declare function removeProjectTheme(projectDir: string, id: string): Promise<void>;
229
- /**
230
- * #236 follow-through: detect a project whose `styles/book.css` is
231
- * byte-identical to a built-in theme's `theme.css` while NO theme is tracked
232
- * as active. That shape is exactly what a pre-0.10.7 `gutterpress new` (or
233
- * "set up as a book") produced — a real, working stylesheet that is simply
234
- * invisible to this command and to the desktop's Theme panel, and which will
235
- * keep loading AFTER (so silently override) whatever theme is applied next.
236
- * Read-only: this never modifies the project — it only surfaces a note so the
237
- * author can decide whether to run `theme apply` themselves.
238
- */
239
- export declare function detectLegacyForkedTheme(projectDir: string, active: ThemeInfo | null): Promise<{
240
- id: string;
241
- name: string;
242
- } | null>;