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
@@ -593,7 +593,7 @@ import { fileURLToPath } from "node:url";
593
593
  var favicon_default = "./favicon-wkbm9cjn.ico";
594
594
 
595
595
  // src/assets/manifest.schema.json
596
- var manifest_schema_default = "./manifest.schema-1q35yxs9.json";
596
+ var manifest_schema_default = "./manifest.schema-hsn8rsgr.json";
597
597
 
598
598
  // src/assets/preview/scripts/preview-interface.js
599
599
  var preview_interface_default = "./preview-interface-9rwz21yw.js";
@@ -638,7 +638,7 @@ var gutterpress_default = "./gutterpress-m98e2w2d.json";
638
638
  var package_default = "./package-rm9d2tht.json";
639
639
 
640
640
  // src/assets/extension-templates/plugin/README.md
641
- var README_default = "./README-mdq34cmb.md";
641
+ var README_default = "./README-jntqyj1z.md";
642
642
 
643
643
  // src/assets/extension-templates/plugin/plugin.js.tpl
644
644
  var plugin_js_default = "./plugin.js-aszz0zgh.tpl";
@@ -656,13 +656,13 @@ var fixture_default = "./fixture-2qnyqgb5.md";
656
656
  var expected_default = "./expected-ycmacem8.html";
657
657
 
658
658
  // src/assets/extension-templates/plugin/test/plugin.test.js.tpl
659
- var plugin_test_js_default = "./plugin.test.js-mv0ghv5n.tpl";
659
+ var plugin_test_js_default = "./plugin.test.js-npnfhyca.tpl";
660
660
 
661
661
  // src/assets/extension-templates/theme/gutterpress.json
662
662
  var gutterpress_default2 = "./gutterpress-x2ycdsb8.json";
663
663
 
664
664
  // src/assets/extension-templates/theme/README.md
665
- var README_default2 = "./README-aaqapam1.md";
665
+ var README_default2 = "./README-6c7fda1s.md";
666
666
 
667
667
  // src/assets/extension-templates/theme/components.yaml
668
668
  var components_default = "./components-0mmswvvm.yaml";
@@ -692,19 +692,19 @@ var book_default = "./book-ted3tzrp.css";
692
692
  var theme_default = "./theme-j2bagrfx.css";
693
693
 
694
694
  // src/assets/themes/clean-book/theme.json
695
- var theme_default2 = "./theme-xt0hjb1b.json";
695
+ var theme_default2 = "./theme-vktedy8q.json";
696
696
 
697
697
  // src/assets/themes/zine/theme.css
698
698
  var theme_default3 = "./theme-h5recz6c.css";
699
699
 
700
700
  // src/assets/themes/zine/theme.json
701
- var theme_default4 = "./theme-cd6efzjw.json";
701
+ var theme_default4 = "./theme-etj4gxck.json";
702
702
 
703
703
  // src/assets/themes/technical-doc/theme.css
704
704
  var theme_default5 = "./theme-nn6d53zy.css";
705
705
 
706
706
  // src/assets/themes/technical-doc/theme.json
707
- var theme_default6 = "./theme-tqfypc1w.json";
707
+ var theme_default6 = "./theme-s2rg7xe7.json";
708
708
 
709
709
  // src/lib/embedded-assets.ts
710
710
  var __libdir = dirname(fileURLToPath(import.meta.url));
package/dist/index.js CHANGED
@@ -6,8 +6,10 @@ import {
6
6
  AUTO_SYNC_MAX_MINUTES,
7
7
  AUTO_SYNC_MIN_MINUTES,
8
8
  AUTO_SYNC_PUSH_INTERVAL_MINUTES,
9
+ BUILT_IN_STYLE_SET_IDS,
9
10
  BUILT_IN_TEMPLATE_IDS,
10
- BUILT_IN_THEME_IDS,
11
+ BUNDLED_EXTENSIONS,
12
+ EXTENSIONS_DIR,
11
13
  EXTENSION_KINDS,
12
14
  EXTENSION_MANIFEST_FILENAME,
13
15
  FileTokenStore,
@@ -22,17 +24,15 @@ import {
22
24
  PRESETS,
23
25
  PRESET_IDS,
24
26
  PublishSelectionsStore,
25
- RECOMMENDED_PLUGINS,
27
+ RECOMMENDED_EXTENSIONS,
26
28
  RESERVED_PREFIX,
27
29
  SNIPPETS_DIR,
28
30
  SYNC_SNAPSHOT_MESSAGE,
29
31
  TARGETS,
30
32
  TARGET_IDS,
31
- THEMES_DIR,
32
- addLocalPlugin,
33
- addNpmPlugin,
33
+ addBuiltInStyleSet,
34
+ addExtension,
34
35
  adoptFolder,
35
- applyTheme,
36
36
  assertExtensionContained,
37
37
  autoSnapshotDelayMs,
38
38
  autoSyncDelayMs,
@@ -44,41 +44,40 @@ import {
44
44
  connectPublishProvider,
45
45
  defaultConfigDir,
46
46
  deleteSnippet,
47
- detectLegacyForkedTheme,
47
+ describeExtension,
48
48
  diagnoseProjectRemote,
49
49
  disconnectPublishCredential,
50
50
  escapeYamlScalar,
51
51
  executeAndReport,
52
52
  executeValidation,
53
- extensionEngineStyleList,
53
+ extensionCarries,
54
54
  extensionStyleList,
55
+ extensionStyleListWithDefault,
55
56
  extractUrlCredential,
56
57
  extractVariables,
57
58
  forgeKindForHost,
58
59
  formatReport,
59
- getActiveTheme,
60
60
  getCheckById,
61
61
  getChecks,
62
- getPreviousTheme,
63
62
  getSystemDiagnostics,
64
63
  hasProjectManifest,
64
+ importExtensionFromFile,
65
+ importExtensionFromUrl,
65
66
  importTemplateFromFolder,
66
- importThemeFromFile,
67
- importThemeFromFolder,
68
- importThemeFromUrl,
69
67
  inspectImage,
68
+ isBundledExtension,
70
69
  isGitInternalPath,
70
+ isPathSpecifier,
71
71
  isSshRemoteUrl,
72
72
  knownForgeTokenUrl,
73
+ listBuiltInStyleSets,
73
74
  listBuiltInTemplates,
74
- listBuiltInThemes,
75
75
  listCustomTemplates,
76
76
  listGitHubBranches,
77
77
  listGitHubRepositories,
78
78
  listMergedSnippets,
79
- listProjectPlugins,
79
+ listProjectExtensions,
80
80
  listProjectStyles,
81
- listProjectThemes,
82
81
  listPublishAccounts,
83
82
  listPublishProviders,
84
83
  listRepoBooks,
@@ -87,6 +86,7 @@ import {
87
86
  loadManifestWithPath,
88
87
  log,
89
88
  openPath,
89
+ parseExtensionSpecifier,
90
90
  parseRemoteOrigin,
91
91
  pathEscapesFolder,
92
92
  pkceChallengeFromVerifier,
@@ -94,17 +94,17 @@ import {
94
94
  publishCredentialKey,
95
95
  publishProviderFor,
96
96
  publishTargetFor,
97
+ readExtensionCss,
97
98
  readExtensionMeta,
98
99
  readExtensionSnippet,
99
100
  readManifestFields,
100
101
  readPublishSettings,
101
102
  readSnippet,
102
- readThemeCss,
103
- removeProjectTheme,
103
+ removeExtension,
104
+ reorderExtensions,
104
105
  reportMissingTools,
105
106
  requireGoogleClientCredentials,
106
107
  resolveActiveStyles,
107
- resolveBuiltInTheme,
108
108
  resolveCheckSelectors,
109
109
  resolveConfig,
110
110
  resolveExtension,
@@ -114,7 +114,6 @@ import {
114
114
  resolveGoogleClientSecret,
115
115
  resolvePublishFormat,
116
116
  resolvePublishRequest,
117
- revertTheme,
118
117
  revokeGoogleCredential,
119
118
  ruleRemoteUrls,
120
119
  ruleRiskyProps,
@@ -123,14 +122,15 @@ import {
123
122
  runChecks,
124
123
  runLint,
125
124
  runPublish,
125
+ sameExtension,
126
126
  sanitizeCloneFolderName,
127
127
  saveProjectAsTemplate,
128
128
  saveSnippet,
129
129
  scaffoldExtension,
130
130
  scaffoldProject,
131
131
  setActiveStyles,
132
+ setExtensionEnabled,
132
133
  setManifestFields,
133
- setPluginEnabled,
134
134
  setPublishProviderConfig,
135
135
  slugifyProjectName,
136
136
  splitOutPath,
@@ -138,16 +138,16 @@ import {
138
138
  substituteVariables,
139
139
  syncProject,
140
140
  testRemoteAccess,
141
- validateProjectPlugins
142
- } from "./index-dfw8hn8q.js";
141
+ validateProjectExtensions
142
+ } from "./index-mey5vraz.js";
143
143
  import {
144
144
  BUILD_DIAGNOSTIC_CODES
145
- } from "./index-rpt4vtby.js";
145
+ } from "./index-dhgzkhs7.js";
146
146
  import {
147
147
  BuildError,
148
148
  DEFAULT_PRINT_OPTS,
149
149
  readyProbeExpr
150
- } from "./index-9pbaznww.js";
150
+ } from "./index-zwcv1njy.js";
151
151
  import {
152
152
  AUTO_SNAPSHOT_MESSAGE,
153
153
  HISTORY_PAGE_LIMIT,
@@ -166,7 +166,7 @@ import {
166
166
  } from "./index-v5mp7a6q.js";
167
167
  import"./index-37x76zdn.js";
168
168
  export {
169
- validateProjectPlugins,
169
+ validateProjectExtensions,
170
170
  testRemoteAccess,
171
171
  syncProject,
172
172
  substituteVariables,
@@ -174,14 +174,15 @@ export {
174
174
  splitOutPath,
175
175
  slugifyProjectName,
176
176
  setPublishProviderConfig,
177
- setPluginEnabled,
178
177
  setManifestFields,
178
+ setExtensionEnabled,
179
179
  setActiveStyles,
180
180
  scaffoldProject,
181
181
  scaffoldExtension,
182
182
  saveSnippet,
183
183
  saveProjectAsTemplate,
184
184
  sanitizeCloneFolderName,
185
+ sameExtension,
185
186
  runPublish,
186
187
  runLint,
187
188
  runChecks,
@@ -190,7 +191,6 @@ export {
190
191
  ruleRiskyProps,
191
192
  ruleRemoteUrls,
192
193
  revokeGoogleCredential,
193
- revertTheme,
194
194
  restoreVersionWithBackup,
195
195
  resolvePublishRequest,
196
196
  resolvePublishFormat,
@@ -202,20 +202,20 @@ export {
202
202
  resolveExtension,
203
203
  resolveConfig,
204
204
  resolveCheckSelectors,
205
- resolveBuiltInTheme,
206
205
  resolveActiveStyles,
207
206
  requireGoogleClientCredentials,
208
207
  reportMissingTools,
209
208
  repoSubPath,
210
209
  repoRootForSource,
211
- removeProjectTheme,
210
+ reorderExtensions,
211
+ removeExtension,
212
212
  readyProbeExpr,
213
- readThemeCss,
214
213
  readSnippet,
215
214
  readPublishSettings,
216
215
  readManifestFields,
217
216
  readExtensionSnippet,
218
217
  readExtensionMeta,
218
+ readExtensionCss,
219
219
  publishTargetFor,
220
220
  publishProviderFor,
221
221
  publishCredentialKey,
@@ -224,6 +224,7 @@ export {
224
224
  pkceChallengeFromVerifier,
225
225
  pathEscapesFolder,
226
226
  parseRemoteOrigin,
227
+ parseExtensionSpecifier,
227
228
  openPath,
228
229
  log,
229
230
  loadManifestWithPath,
@@ -232,44 +233,43 @@ export {
232
233
  listRepoBooks,
233
234
  listPublishProviders,
234
235
  listPublishAccounts,
235
- listProjectThemes,
236
236
  listProjectStyles,
237
- listProjectPlugins,
237
+ listProjectExtensions,
238
238
  listMergedSnippets,
239
239
  listGitHubRepositories,
240
240
  listGitHubBranches,
241
241
  listCustomTemplates,
242
- listBuiltInThemes,
243
242
  listBuiltInTemplates,
243
+ listBuiltInStyleSets,
244
244
  knownForgeTokenUrl,
245
245
  isSshRemoteUrl,
246
+ isPathSpecifier,
246
247
  isNoChangesError,
247
248
  isGitInternalPath,
249
+ isBundledExtension,
248
250
  inspectImage,
249
- importThemeFromUrl,
250
- importThemeFromFolder,
251
- importThemeFromFile,
252
251
  importTemplateFromFolder,
252
+ importExtensionFromUrl,
253
+ importExtensionFromFile,
253
254
  hasProjectManifest,
254
255
  getSystemDiagnostics,
255
- getPreviousTheme,
256
256
  getChecks,
257
257
  getCheckById,
258
- getActiveTheme,
259
258
  formatReport,
260
259
  forgeKindForHost,
261
260
  findEnclosingRepoDir,
262
261
  extractVariables,
263
262
  extractUrlCredential,
263
+ extensionStyleListWithDefault,
264
264
  extensionStyleList,
265
- extensionEngineStyleList,
265
+ extensionCarries,
266
266
  executeValidation,
267
267
  executeAndReport,
268
268
  escapeYamlScalar,
269
269
  disconnectPublishCredential,
270
270
  diagnoseProjectRemote,
271
271
  detectProjectSource,
272
- detectLegacyForkedTheme,
272
+ describeExtension,
273
273
  deleteSnippet,
274
274
  defaultConfigDir,
275
275
  connectPublishProvider,
@@ -282,18 +282,16 @@ export {
282
282
  autoSyncDelayMs,
283
283
  autoSnapshotDelayMs,
284
284
  assertExtensionContained,
285
- applyTheme,
286
285
  adoptFolder,
287
- addNpmPlugin,
288
- addLocalPlugin,
289
- THEMES_DIR,
286
+ addExtension,
287
+ addBuiltInStyleSet,
290
288
  TARGET_IDS,
291
289
  TARGETS,
292
290
  SYNC_SNAPSHOT_MESSAGE,
293
291
  SNIPPETS_DIR,
294
292
  RESTORE_BACKUP_MESSAGE,
295
293
  RESERVED_PREFIX,
296
- RECOMMENDED_PLUGINS,
294
+ RECOMMENDED_EXTENSIONS,
297
295
  PublishSelectionsStore,
298
296
  PRESET_IDS,
299
297
  PRESETS,
@@ -309,10 +307,12 @@ export {
309
307
  FileTokenStore,
310
308
  EXTENSION_MANIFEST_FILENAME,
311
309
  EXTENSION_KINDS,
310
+ EXTENSIONS_DIR,
312
311
  DEFAULT_PRINT_OPTS,
313
312
  BuildError,
314
- BUILT_IN_THEME_IDS,
313
+ BUNDLED_EXTENSIONS,
315
314
  BUILT_IN_TEMPLATE_IDS,
315
+ BUILT_IN_STYLE_SET_IDS,
316
316
  BUILD_DIAGNOSTIC_CODES,
317
317
  AUTO_SYNC_PUSH_INTERVAL_MINUTES,
318
318
  AUTO_SYNC_MIN_MINUTES,
@@ -39,13 +39,6 @@ export interface BuildRunnerOptions {
39
39
  */
40
40
  keepBrowserAlive?: boolean;
41
41
  rawArgs: Record<string, unknown>;
42
- /**
43
- * CLI `--engine` override. The native engine is the only engine, so this is
44
- * a deprecated no-op accepted for backward compatibility only: `"paged"`
45
- * triggers a one-line warning (`manifest.ts`'s resolution) and the build
46
- * proceeds natively regardless.
47
- */
48
- engine?: "paged" | "native";
49
42
  /**
50
43
  * Optional injected engine-Chromium factory for native builds
51
44
  * (`engine.ts`'s `buildNativePdf`). When omitted (the CLI's default), the
@@ -58,7 +58,43 @@ export declare function stageBookAssets(options: {
58
58
  cssAssets: AssetCopy[];
59
59
  /** Called once with the copy plan; throw here to abort before copying. */
60
60
  onPlan?: (plan: StagingPlan) => void;
61
+ /**
62
+ * Strip every relative `<a href>` from the staged book — for print
63
+ * artifacts only, where no relative target can be opened (see
64
+ * {@link dropRelativeLinkHrefs}). Off by default: `--format html` ships its
65
+ * images beside `book.html` and keeps every href.
66
+ */
67
+ dropRelativeLinks?: boolean;
61
68
  }): Promise<StagedAssets>;
69
+ /**
70
+ * Can a PDF reader follow this href? Only an in-document `#fragment` (a GoTo
71
+ * destination) or a URL with a scheme other than `file:`. Everything else —
72
+ * a relative or absolute path, `file:`, and the shapes that name no asset to
73
+ * copy (empty, `?query`, `//host`) — Chromium resolves against the staged
74
+ * document's `file://` base URL, which is dead for every reader.
75
+ */
76
+ export declare function isPrintResolvableHref(href: string): boolean;
77
+ /**
78
+ * Remove the `href` of every `<a>` that {@link isPrintResolvableHref}
79
+ * rejects, keeping the element and its text. Print-production tooling
80
+ * (permanent, not a shim).
81
+ *
82
+ * The staged `book.html` is printed from a `file://` URL in a per-build temp
83
+ * dir (engine/compiler/build.ts navigates `pathToFileURL(input)`), and
84
+ * Chromium writes the ABSOLUTE resolved URL of every relative `href` into
85
+ * the PDF's link annotation: `file:///tmp/gutterpress-build-<random>/docs/
86
+ * constitution.md`. That leaks the build machine's layout, is dead for every
87
+ * reader, and makes two builds of the same sources differ in bytes (#263).
88
+ * A PDF has no files beside it, so NO relative target is openable from one —
89
+ * not a same-book chapter file, not even an image the build copied into the
90
+ * work dir. The href is therefore dropped rather than rewritten.
91
+ * `source.links.dangling` tells the author which links this affects before
92
+ * the build.
93
+ *
94
+ * Comments and raw-text / literal-content elements (a `<pre>` showing HTML)
95
+ * stay untouched — see {@link rewriteActiveHtml}.
96
+ */
97
+ export declare function dropRelativeLinkHrefs(html: string): string;
62
98
  /**
63
99
  * Create a unique scratch directory under the OS temp dir. Used only for
64
100
  * PDF/X intermediates (`raw.pdf`, Ghostscript work files) — never for staging
@@ -41,13 +41,6 @@ export declare function parseFormat(raw: unknown, opts: {
41
41
  }): BuildFormat;
42
42
  /** Parse `--pdfx-flavor`, which is only valid alongside `--format pdfx`. */
43
43
  export declare function parsePdfxFlavor(raw: unknown, format: BuildFormat): PdfxFlavor | undefined;
44
- /**
45
- * Parse `--engine`, shared by `build` and `preview`. The native engine is the
46
- * only engine, so this flag is an accepted-but-ignored no-op: `--engine paged`
47
- * produces a one-line warning (`manifest.ts`'s resolution) and the build
48
- * proceeds natively regardless. `undefined` means "not passed".
49
- */
50
- export declare function parseEngine(raw: unknown): "paged" | "native" | undefined;
51
44
  /** Parse `--port`, defaulting to {@link NETWORK.DEFAULT_PORT} (0 = OS-assigned). */
52
45
  export declare function resolvePort(raw: unknown): number;
53
46
  /**
@@ -1,16 +1,16 @@
1
1
  import { type PrintSafeWarning } from "./printsafe.ts";
2
- import { type ThemeInfo } from "./theme-manager.ts";
2
+ import { type ProjectExtensionEntry } from "./extension-manager.ts";
3
3
  /** Reject a raw archive larger than this before unzipping (zip-bomb surface). */
4
4
  export declare const MAX_THEME_ARCHIVE_BYTES: number;
5
5
  /** A non-fatal issue surfaced to the author after a successful import. */
6
- export interface ThemeImportWarning {
6
+ export interface ExtensionImportWarning {
7
7
  code: "print-safety" | "no-theme-json" | "unnamed-theme" | "extra-files";
8
8
  message: string;
9
9
  }
10
- /** Outcome of a `.zip` / `.css` import: the imported theme + any warnings. */
11
- export interface ThemeImportResult {
12
- theme: ThemeInfo;
13
- warnings: ThemeImportWarning[];
10
+ /** Outcome of a `.zip` / `.css` / URL import: the added entry + any warnings. */
11
+ export interface ExtensionImportResult {
12
+ entry: ProjectExtensionEntry;
13
+ warnings: ExtensionImportWarning[];
14
14
  }
15
15
  /**
16
16
  * Is this zip entry name unsafe to extract? Rejects absolute paths (posix or
@@ -53,19 +53,28 @@ export declare function unexpectedThemeFiles(relPaths: string[], declaredExtras?
53
53
  * Import a theme from a `.zip` package. Unzips in memory (fflate), rejects
54
54
  * unsafe paths and over-cap archives, locates the theme root (at the archive
55
55
  * root or one folder down), extracts just that subtree to a temp folder, and
56
- * finalizes via {@link finalizeThemeImport}. Lands the theme in
56
+ * finalizes via {@link finalizeExtensionImport}. Lands the theme in
57
57
  * `themes/<uniqueId>/` (never overwrites an existing theme).
58
58
  */
59
- export declare function importThemeFromZip(projectDir: string, archive: Uint8Array): Promise<ThemeImportResult>;
59
+ export declare function importExtensionFromZip(projectDir: string, archive: Uint8Array): Promise<ExtensionImportResult>;
60
60
  /**
61
61
  * Import a theme from a bare `.css` file by wrapping it into a one-file theme
62
62
  * folder (`theme.css` + a synthesized `theme.json` naming it). REJECTS a CSS
63
63
  * that fails to parse.
64
64
  */
65
- export declare function importThemeFromCssText(projectDir: string, css: string, name: string): Promise<ThemeImportResult>;
65
+ export declare function importExtensionFromCssText(projectDir: string, css: string, name: string): Promise<ExtensionImportResult>;
66
66
  /**
67
67
  * Import a theme from a local file path, dispatched by extension: `.zip` →
68
- * {@link importThemeFromZip}, `.css` → {@link importThemeFromCssText}. The
68
+ * {@link importExtensionFromZip}, `.css` → {@link importExtensionFromCssText}. The
69
69
  * desktop's host reads the path from a native file picker and calls this.
70
70
  */
71
- export declare function importThemeFromFile(projectDir: string, filePath: string): Promise<ThemeImportResult>;
71
+ export declare function importExtensionFromFile(projectDir: string, filePath: string): Promise<ExtensionImportResult>;
72
+ /**
73
+ * Import a look from a URL using the global `fetch` (bundle-safe — no node
74
+ * http client). Two shapes: a `.css` URL is the look's `theme.css`, with
75
+ * metadata synthesised from the URL; a base URL (no `.css`) fetches
76
+ * `<base>/theme.json` (optional) and `<base>/theme.css` (required). Bundled
77
+ * fonts are not followed — authors wanting bundled assets use a folder or a
78
+ * `.zip`. The fetched files go through the same validation as a `.zip`.
79
+ */
80
+ export declare function importExtensionFromUrl(projectDir: string, url: string): Promise<ExtensionImportResult>;
@@ -0,0 +1,170 @@
1
+ import { type NpmPluginInstallOptions } from "./npm-plugin-installer.ts";
2
+ import { type ExtensionCarries } from "./extension-manifest.ts";
3
+ import type { ResolvedExtensionConfig } from "../schema/manifest.types.ts";
4
+ /** Folder (relative to the project root) extensions this module COPIES land in
5
+ * (built-in looks, zip/URL imports). Path entries elsewhere are never moved. */
6
+ export declare const EXTENSIONS_DIR = "extensions";
7
+ /** The vendored npm tree stays where 0.10.8 put it. */
8
+ export { PLUGINS_DIR } from "./plugin-vendor.ts";
9
+ /** How an `extensions:` entry resolves — the form of its specifier decides. */
10
+ export type ExtensionSourceKind = "bundled" | "path" | "npm";
11
+ /** One configured extension, as surfaced to the CLI and the desktop. */
12
+ export interface ProjectExtensionEntry {
13
+ /** The specifier exactly as written in the manifest — the stable ref every
14
+ * other call takes (`setExtensionEnabled`, `removeExtension`, …). */
15
+ use: string;
16
+ kind: ExtensionSourceKind;
17
+ /** Package name (bundled, npm) or the path specifier (path). */
18
+ name: string;
19
+ /** Exact pinned version, from the specifier, for an npm entry. */
20
+ version?: string;
21
+ /** Named module export selected as the plugin function. */
22
+ export?: string;
23
+ /** Per-project enable flag. Absent in the manifest defaults to `true`. */
24
+ enabled: boolean;
25
+ /** Display name: the metadata's `name`, else the package name / folder name. */
26
+ label: string;
27
+ description?: string;
28
+ author?: string;
29
+ /** Preview image path relative to the extension folder, when declared. */
30
+ preview?: string | null;
31
+ /** The sheet carrying the `:root` token surface, relative to the folder,
32
+ * when declared (`gutterpress.json`'s `tokensFile`). */
33
+ tokensFile?: string;
34
+ /** Declared stylesheets relative to the folder, in cascade order (`theme.css`
35
+ * by default for a metadata-less look). Absent when there is no folder. */
36
+ styles?: string[];
37
+ /** What the extension declares — the desktop shows styles-carrying entries
38
+ * in its Look view and markdown-carrying ones in Features; one list. */
39
+ carries: ExtensionCarries;
40
+ /** Absolute folder its metadata was read from. Absent for a bundled name,
41
+ * a bare JS file, an uninstalled npm entry, or a missing path. */
42
+ dir?: string;
43
+ /** Non-fatal notices: not installed, not found, unparseable specifier, … */
44
+ warnings?: string[];
45
+ }
46
+ /** Result of attempting to load one configured extension. */
47
+ export interface ExtensionValidationResult {
48
+ use: string;
49
+ kind: ExtensionSourceKind;
50
+ /** Mirrors the manifest enable flag. Disabled extensions are not load-tested. */
51
+ enabled: boolean;
52
+ /** `true` when the extension loaded OK (or is disabled and skipped). */
53
+ ok: boolean;
54
+ /** The loader's fail-fast error message when `ok` is `false`. */
55
+ error?: string;
56
+ }
57
+ /** A curated, bundled markdown feature an author can turn on. */
58
+ export interface RecommendedExtension {
59
+ /** The bundled name — also the manifest entry "Add" writes. */
60
+ use: string;
61
+ /** Short, plain-language feature name shown as the row title. */
62
+ label: string;
63
+ /** One-line author-friendly description. */
64
+ description: string;
65
+ }
66
+ /**
67
+ * The bundled markdown features (`BUNDLED_EXTENSIONS`): adding one writes the
68
+ * manifest entry AND the feature works immediately — no terminal, no install,
69
+ * offline. The always-on defaults (attrs/footnote/deflist, applied
70
+ * unconditionally in renderer.ts) are deliberately NOT listed: recommending
71
+ * the author "add" something already active is pure confusion.
72
+ */
73
+ export declare const RECOMMENDED_EXTENSIONS: RecommendedExtension[];
74
+ /** The built-in looks shipped as embedded assets (ids are folder names). */
75
+ export declare const BUILT_IN_STYLE_SET_IDS: readonly ["clean-book", "zine", "technical-doc"];
76
+ export type BuiltInStyleSetId = (typeof BUILT_IN_STYLE_SET_IDS)[number];
77
+ /** A built-in look, as listed for the desktop's Look view. */
78
+ export interface BuiltInStyleSet {
79
+ id: BuiltInStyleSetId;
80
+ name: string;
81
+ description: string;
82
+ }
83
+ /**
84
+ * Two specifiers name the same extension when they are equal, or when both
85
+ * are npm specifiers for the same package (a re-install re-pins the version
86
+ * in place instead of adding a second entry).
87
+ */
88
+ export declare function sameExtension(a: string, b: string): boolean;
89
+ /**
90
+ * List the project's configured extensions, in manifest (= cascade) order,
91
+ * each described from its metadata. Returns `[]` when there is no manifest
92
+ * or no `extensions:` list. Tolerant: a missing folder, an uninstalled npm
93
+ * entry, or an unparseable specifier is reported in `warnings`, never thrown
94
+ * — one bad entry must not blank the list for every other one.
95
+ */
96
+ export declare function listProjectExtensions(projectDir: string): Promise<ProjectExtensionEntry[]>;
97
+ /** One configured extension by specifier, or `null`. */
98
+ export declare function describeExtension(projectDir: string, use: string): Promise<ProjectExtensionEntry | null>;
99
+ /** The loader config for a listed entry — what `validate` and `add` load-test. */
100
+ export declare function extensionConfigFor(entry: {
101
+ use: string;
102
+ kind: ExtensionSourceKind;
103
+ name: string;
104
+ version?: string;
105
+ export?: string;
106
+ }): ResolvedExtensionConfig;
107
+ /**
108
+ * Set the per-project enabled flag for the extension `use` names. A bare
109
+ * string entry becomes the object form to carry `enabled: false`; re-enabling
110
+ * drops the flag and collapses an object with nothing else left back to the
111
+ * bare string. Persists to the manifest (preserving comments). Throws when no
112
+ * entry matches.
113
+ */
114
+ export declare function setExtensionEnabled(projectDir: string, use: string, enabled: boolean): Promise<void>;
115
+ /**
116
+ * Rewrite the list in the given order — the author's cascade. `order` must
117
+ * name every current entry exactly once (by its `use`); anything else is a
118
+ * stale view and is refused rather than guessed at.
119
+ */
120
+ export declare function reorderExtensions(projectDir: string, order: string[]): Promise<void>;
121
+ /**
122
+ * Remove an extension's manifest entry. An npm entry's vendored tree is
123
+ * deleted too (it is Gutterpress's own private copy); a path entry's folder
124
+ * is the author's and is never touched. Throws when no entry matches.
125
+ */
126
+ export declare function removeExtension(projectDir: string, use: string): Promise<void>;
127
+ export interface AddExtensionOptions extends NpmPluginInstallOptions {
128
+ /** Named module export to use as the plugin function (npm and path only). */
129
+ exportName?: string;
130
+ /** @internal fault injection for the installer tests. */
131
+ __testFailBeforeManifestCommit?: () => void | Promise<void>;
132
+ }
133
+ /**
134
+ * Add an extension to the project, one way whatever it is:
135
+ *
136
+ * - a bundled name is written as-is;
137
+ * - a path (`./x`, `../x`, or absolute) is load-tested and written relative
138
+ * to the project, referenced in place;
139
+ * - an npm specifier (`name`, `name@version`) is downloaded, verified,
140
+ * vendored with a receipt, load-tested, and written back as
141
+ * `name@<exact version>`. On any failure the vendor tree is rolled back.
142
+ *
143
+ * Idempotent: adding what is already listed re-pins/updates that entry.
144
+ * Returns the described entry.
145
+ */
146
+ export declare function addExtension(projectDir: string, specifier: string, options?: AddExtensionOptions): Promise<ProjectExtensionEntry>;
147
+ /** List the built-in looks (metadata read from the embedded assets). */
148
+ export declare function listBuiltInStyleSets(): Promise<BuiltInStyleSet[]>;
149
+ /** A folder name under `extensions/` not yet taken: `<slug>`, `<slug>-2`, … */
150
+ export declare function uniqueExtensionId(projectDir: string, base: string): Promise<string>;
151
+ /**
152
+ * Copy a built-in look into `extensions/<id>/` and add it to the manifest as
153
+ * `./extensions/<id>`. The copy is deliberate (see the module doc): the look
154
+ * becomes the author's own editable files. Idempotent — a folder already
155
+ * there is kept as it is and only (re-)referenced.
156
+ */
157
+ export declare function addBuiltInStyleSet(projectDir: string, id: string): Promise<ProjectExtensionEntry>;
158
+ /**
159
+ * Validate every configured extension by attempting to LOAD it through the
160
+ * one loader. Disabled entries are reported as disabled and NOT load-tested.
161
+ * Loader errors are caught and surfaced per entry (never thrown through) so
162
+ * the desktop can flag an erroring extension with its fix instructions.
163
+ */
164
+ export declare function validateProjectExtensions(projectDir: string): Promise<ExtensionValidationResult[]>;
165
+ /**
166
+ * Read an extension's stylesheets, concatenated in cascade order, for a
167
+ * preview thumbnail (the renderer never touches fs). Read-only, so
168
+ * concatenation is fine even though it would not be for an editable surface.
169
+ */
170
+ export declare function readExtensionCss(projectDir: string, use: string): Promise<string>;