gutterpress 0.0.0 → 0.9.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +21 -16
  2. package/dist/api/index.d.ts +6 -2
  3. package/dist/api/index.js +15 -3
  4. package/dist/{audit-1jrwwacz.js → audit-nhn2pjz3.js} +5 -5
  5. package/dist/{build-5s92kgpc.js → build-san7fv2z.js} +5 -5
  6. package/dist/checks/types.d.ts +6 -0
  7. package/dist/{cli-hvqjs342.js → cli-e5zhb0xs.js} +2 -2
  8. package/dist/{cli-7ykx7pp0.js → cli-k4bd06sd.js} +982 -331
  9. package/dist/{cli-h7phg3ka.js → cli-najycadg.js} +258 -258
  10. package/dist/{index-1tyj4bk3.js → cli-wchtvxvw.js} +4 -1
  11. package/dist/{cli-5bxx0c4y.js → cli-yzrh708h.js} +2 -2
  12. package/dist/cli.js +15 -15
  13. package/dist/{doctor-25zjt8dz.js → doctor-hrk0kxxz.js} +2 -2
  14. package/dist/{index-3h3fef7r.js → index-bynn850m.js} +1200 -410
  15. package/dist/{cli-1tyj4bk3.js → index-wchtvxvw.js} +4 -1
  16. package/dist/{index-5bxx0c4y.js → index-yzrh708h.js} +2 -2
  17. package/dist/index.js +15 -3
  18. package/dist/lib/asset-inline.d.ts +19 -0
  19. package/dist/lib/build-error.d.ts +0 -1
  20. package/dist/lib/build-fingerprint.d.ts +14 -0
  21. package/dist/lib/build-runner.d.ts +21 -1
  22. package/dist/lib/ghostscript.d.ts +0 -9
  23. package/dist/lib/manifest-config.d.ts +7 -0
  24. package/dist/lib/manifest.d.ts +8 -0
  25. package/dist/lib/markdown/assemble.d.ts +4 -6
  26. package/dist/lib/markdown/chapter-id.d.ts +1 -1
  27. package/dist/lib/markdown/index.d.ts +1 -2
  28. package/dist/lib/npm-plugin-installer.d.ts +1 -1
  29. package/dist/lib/output-paths.d.ts +0 -1
  30. package/dist/lib/pagination.d.ts +0 -20
  31. package/dist/lib/pdf-inspect.d.ts +2 -2
  32. package/dist/lib/pdf-parse.d.ts +2 -1
  33. package/dist/lib/plugin-manager.d.ts +0 -2
  34. package/dist/lib/presets.d.ts +36 -7
  35. package/dist/lib/project-scaffold.d.ts +39 -2
  36. package/dist/lib/project-source.d.ts +11 -9
  37. package/dist/lib/project-templates.d.ts +45 -7
  38. package/dist/lib/publish/providers/azure-swa.d.ts +0 -1
  39. package/dist/lib/publish/providers/itch.d.ts +0 -1
  40. package/dist/lib/publish/providers/shopify.d.ts +0 -1
  41. package/dist/lib/remote-auth/github-repos.d.ts +0 -2
  42. package/dist/lib/remote-auth/recovery/dispatch.d.ts +0 -5
  43. package/dist/lib/remote-auth/recovery/outcome-mapping.d.ts +3 -2
  44. package/dist/lib/remote-auth/recovery/recover-stale-lock.d.ts +0 -8
  45. package/dist/lib/remote-auth/sync.d.ts +2 -10
  46. package/dist/lib/remote-auth/test-access.d.ts +0 -2
  47. package/dist/lib/remote-auth/test-support/git-http-server.d.ts +0 -37
  48. package/dist/lib/source-provider.d.ts +0 -14
  49. package/dist/lib/static-serve.d.ts +39 -0
  50. package/dist/lib/style-resolver.d.ts +10 -1
  51. package/dist/lib/targets.d.ts +84 -0
  52. package/dist/lib/theme-import.d.ts +0 -2
  53. package/dist/lib/theme-manager.d.ts +1 -1
  54. package/dist/lib/validation-exec.d.ts +4 -3
  55. package/dist/{lint-f63nxjfj.js → lint-96j9hrj4.js} +5 -5
  56. package/dist/{manifest.schema-rn1dpdpn.json → manifest.schema-z61rzw44.json} +12 -6
  57. package/dist/new-8p38wavc.js +185 -0
  58. package/dist/{pagedjs-interface-80g3xa8p.js → pagedjs-interface-qxvzgwd7.js} +92 -33
  59. package/dist/{plugin-wf2cbmwc.js → plugin-ees6nhkc.js} +5 -5
  60. package/dist/{preflight-tg4mpz1h.js → preflight-1q6c2edh.js} +24 -29
  61. package/dist/preview/file-watcher.d.ts +48 -39
  62. package/dist/preview/http-server.d.ts +5 -2
  63. package/dist/preview/server-context.d.ts +24 -0
  64. package/dist/preview-shell-6dqexx1m.js +581 -0
  65. package/dist/{preview-h2bwbb2x.js → preview-y5a2zen1.js} +5 -5
  66. package/dist/{project-source-0tg0r6vm.js → project-source-p0gn1wd5.js} +3 -3
  67. package/dist/{publish-ztaet53r.js → publish-rm9yb3wh.js} +5 -5
  68. package/dist/render.js +10 -19
  69. package/dist/{repair-jkp4n3j8.js → repair-zgq7q2g6.js} +5 -5
  70. package/dist/schema/manifest.types.d.ts +14 -3
  71. package/dist/server.d.ts +2 -0
  72. package/dist/{source-provider-hp96b5jf.js → source-provider-c1rjm2c0.js} +2 -8
  73. package/dist/types.d.ts +0 -1
  74. package/dist/{validate-k2drs19j.js → validate-nr0xa6sa.js} +8 -8
  75. package/package.json +2 -2
  76. package/dist/chapter-01-jj8ftn4c.md +0 -29
  77. package/dist/lib/validation-profile.d.ts +0 -18
  78. package/dist/manifest-ryyg71nf.yaml +0 -9
  79. package/dist/new-7b08a42h.js +0 -105
  80. package/dist/preview-shell-rghtfmc8.js +0 -155
  81. package/dist/theme-0yzvthe8.css +0 -65
  82. package/dist/theme-6a2fngc8.json +0 -6
package/dist/render.js CHANGED
@@ -600,17 +600,6 @@ async function assembleBookHtml(opts) {
600
600
  throw new Error("No markdown files to render");
601
601
  }
602
602
  const md = createMarkdownRenderer(opts.plugins);
603
- let sourceChapter = null;
604
- if (opts.wrapChapters) {
605
- const renderToken = md.renderer.renderToken.bind(md.renderer);
606
- md.renderer.renderToken = (tokens, idx, options) => {
607
- const token = tokens[idx];
608
- if (sourceChapter !== null && token.level === 0 && token.map !== null && token.type.endsWith("_open")) {
609
- token.attrSet("data-chapter-src", sourceChapter);
610
- }
611
- return renderToken(tokens, idx, options);
612
- };
613
- }
614
603
  let bodyContent = "";
615
604
  const imageRefs = new Set;
616
605
  for (const file of files) {
@@ -623,20 +612,22 @@ async function assembleBookHtml(opts) {
623
612
  throw new Error(`Failed to read file ${file}: ${errorMsg}`);
624
613
  }
625
614
  const env = {};
626
- sourceChapter = opts.wrapChapters ? chapterId : null;
627
- let rendered;
628
- try {
629
- rendered = md.render(content, env);
630
- } finally {
631
- sourceChapter = null;
632
- }
615
+ const rendered = md.render(content, env);
633
616
  if (env.layoutWarnings && env.layoutWarnings.length > 0) {
634
617
  opts.onChapterWarnings?.(chapterId, env.layoutWarnings);
635
618
  }
636
619
  for (const ref of env.imageRefs ?? [])
637
620
  imageRefs.add(ref);
638
- bodyContent += rendered + `
621
+ if (opts.wrapChapters) {
622
+ const safe = chapterId.replace(/&/g, "&").replace(/"/g, """);
623
+ bodyContent += `<div class="gutterpress-chapter" data-chapter-src="${safe}">
624
+ ${rendered}
625
+ </div>
626
+ `;
627
+ } else {
628
+ bodyContent += rendered + `
639
629
  `;
630
+ }
640
631
  }
641
632
  if (opts.onImageRefs) {
642
633
  for (const ref of collectHtmlImageRefs(bodyContent))
@@ -8,18 +8,18 @@ import {
8
8
  makeManualGuidance,
9
9
  recover,
10
10
  verifyRepoReadable
11
- } from "./cli-7ykx7pp0.js";
11
+ } from "./cli-k4bd06sd.js";
12
12
  import {
13
13
  FileTokenStore,
14
14
  defaultConfigDir
15
- } from "./cli-h7phg3ka.js";
15
+ } from "./cli-najycadg.js";
16
16
  import {
17
17
  UsageError,
18
18
  rejectExtraPositionals,
19
19
  rejectUnknownFlags
20
- } from "./cli-hvqjs342.js";
21
- import"./cli-5bxx0c4y.js";
22
- import"./cli-1tyj4bk3.js";
20
+ } from "./cli-e5zhb0xs.js";
21
+ import"./cli-yzrh708h.js";
22
+ import"./cli-wchtvxvw.js";
23
23
  import"./cli-yja077f6.js";
24
24
  import"./cli-37x76zdn.js";
25
25
 
@@ -1,4 +1,4 @@
1
- export type CheckSeverity = "error" | "warning" | "info";
1
+ type CheckSeverity = "error" | "warning" | "info";
2
2
  export interface PluginConfig {
3
3
  path?: string;
4
4
  name?: string;
@@ -29,7 +29,7 @@ export interface PluginConfig {
29
29
  * absent uses the default (bare-host) credential. This book-level choice
30
30
  * overrides any project/global default.
31
31
  */
32
- export interface PublishSettings {
32
+ interface PublishSettings {
33
33
  itch?: {
34
34
  /** itch.io project as `user/game` (the butler push target). */
35
35
  target?: string;
@@ -64,7 +64,15 @@ export interface GutterpressManifest {
64
64
  title?: string;
65
65
  authors?: string[];
66
66
  publish?: PublishSettings;
67
- preset?: "dtrpg" | "book";
67
+ /** How the book is designed (ADR 0008). The registry in lib/presets.ts is authoritative. */
68
+ preset?: "dtrpg" | "book" | "custom";
69
+ /**
70
+ * Where the book is published (ADR 0008): publish-target ids whose
71
+ * validation policies this book is checked against. Absent = the preset's
72
+ * defaults (`dtrpg` -> ["dtrpg"]; `book`/`custom` -> []). The registry in
73
+ * lib/targets.ts is authoritative.
74
+ */
75
+ targets?: string[];
68
76
  styles?: string[];
69
77
  plugins?: (string | PluginConfig)[];
70
78
  source?: {
@@ -155,6 +163,8 @@ export interface ResolvedPluginConfig {
155
163
  export interface ResolvedConfig {
156
164
  title: string;
157
165
  authors: string[];
166
+ /** Validated publish-target ids for this book (may be empty). */
167
+ targets: string[];
158
168
  styles?: string[];
159
169
  plugins: ResolvedPluginConfig[];
160
170
  source: {
@@ -216,3 +226,4 @@ export interface ResolvedConfig {
216
226
  };
217
227
  };
218
228
  }
229
+ export {};
package/dist/server.d.ts CHANGED
@@ -16,6 +16,8 @@ export interface PreviewServerHandle {
16
16
  stop: () => Promise<void>;
17
17
  /** Switch the watched directory and regenerate HTML. */
18
18
  restart: (newInputPath: string) => Promise<void>;
19
+ /** Rebuild immediately after a host-managed write has fully settled. */
20
+ notifySettledWrite: (filePath: string, writtenContent: string) => void;
19
21
  }
20
22
  export interface StartPreviewServerOptions extends PreviewServerOptions {
21
23
  /**
@@ -2,9 +2,7 @@ import {
2
2
  AUTO_SNAPSHOT_MESSAGE,
3
3
  HISTORY_PAGE_LIMIT,
4
4
  RESTORE_BACKUP_MESSAGE,
5
- SNAPSHOT_STAGING_MARKER,
6
5
  __repoLockQueueSizeForTests,
7
- gitAuthor,
8
6
  gitDirFor,
9
7
  gitScopeFor,
10
8
  hasPendingChanges,
@@ -12,15 +10,14 @@ import {
12
10
  isNoChangesError,
13
11
  listWorkdirChanges,
14
12
  providerFor,
15
- readGitAuthor,
16
13
  resolveGitAuthor,
17
14
  restoreVersionWithBackup,
18
15
  snapshotStagingMarkerPath,
19
16
  snapshotWorkingTreeUnlocked,
20
17
  stageChanges,
21
18
  withRepoLock
22
- } from "./cli-5bxx0c4y.js";
23
- import"./cli-1tyj4bk3.js";
19
+ } from "./cli-yzrh708h.js";
20
+ import"./cli-wchtvxvw.js";
24
21
  import"./cli-37x76zdn.js";
25
22
  export {
26
23
  withRepoLock,
@@ -29,7 +26,6 @@ export {
29
26
  snapshotStagingMarkerPath,
30
27
  restoreVersionWithBackup,
31
28
  resolveGitAuthor,
32
- readGitAuthor,
33
29
  providerFor,
34
30
  listWorkdirChanges,
35
31
  isNoChangesError,
@@ -37,9 +33,7 @@ export {
37
33
  hasPendingChanges,
38
34
  gitScopeFor,
39
35
  gitDirFor,
40
- gitAuthor,
41
36
  __repoLockQueueSizeForTests,
42
- SNAPSHOT_STAGING_MARKER,
43
37
  RESTORE_BACKUP_MESSAGE,
44
38
  HISTORY_PAGE_LIMIT,
45
39
  AUTO_SNAPSHOT_MESSAGE
package/dist/types.d.ts CHANGED
@@ -4,7 +4,6 @@
4
4
  * Core types used by the preview server and CLI commands.
5
5
  * Build pipeline types are in src/schema/manifest.types.ts.
6
6
  */
7
- export type { GutterpressManifest, ResolvedConfig } from './schema/manifest.types';
8
7
  /**
9
8
  * Options for preview server (internal)
10
9
  */
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  executeAndReport
3
- } from "./cli-7ykx7pp0.js";
3
+ } from "./cli-k4bd06sd.js";
4
4
  import {
5
5
  log
6
- } from "./cli-h7phg3ka.js";
6
+ } from "./cli-najycadg.js";
7
7
  import {
8
8
  EXIT_CODES,
9
9
  UsageError,
10
10
  rejectExtraPositionals,
11
11
  rejectUnknownFlags
12
- } from "./cli-hvqjs342.js";
13
- import"./cli-5bxx0c4y.js";
14
- import"./cli-1tyj4bk3.js";
12
+ } from "./cli-e5zhb0xs.js";
13
+ import"./cli-yzrh708h.js";
14
+ import"./cli-wchtvxvw.js";
15
15
  import"./cli-yja077f6.js";
16
16
  import"./cli-37x76zdn.js";
17
17
 
@@ -55,9 +55,9 @@ var commandArgs = {
55
55
  type: "string",
56
56
  description: "Run checks for phase: pre | post | all | pre-build | post-build (default: all)"
57
57
  },
58
- profile: {
58
+ target: {
59
59
  type: "string",
60
- description: "Validation profile lock (currently: dtrpg)"
60
+ description: "Publish targets to validate against (comma-separated, e.g. dtrpg,itch), overriding the manifest's `targets:`"
61
61
  }
62
62
  };
63
63
  var validate_default = defineCommand({
@@ -83,7 +83,7 @@ var validate_default = defineCommand({
83
83
  only: typeof args.only === "string" ? args.only : undefined,
84
84
  skip: typeof args.skip === "string" ? args.skip : undefined,
85
85
  phase: typeof args.phase === "string" ? args.phase : undefined,
86
- profile: typeof args.profile === "string" ? args.profile : undefined
86
+ target: typeof args.target === "string" ? args.target : undefined
87
87
  }, format);
88
88
  } catch (error) {
89
89
  log.error(error instanceof Error ? error.message : String(error));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gutterpress",
3
- "version": "0.0.0",
3
+ "version": "0.9.0-alpha.2",
4
4
  "description": "Markdown-to-PDF converter for professional print layout using Paged.js and Ghostscript.",
5
5
  "author": "itlackey",
6
6
  "license": "MPL-2.0",
@@ -12,7 +12,7 @@
12
12
  "ghostscript",
13
13
  "book",
14
14
  "publishing",
15
- "ttrpg"
15
+ "zine"
16
16
  ],
17
17
  "repository": {
18
18
  "type": "git",
@@ -1,29 +0,0 @@
1
- # Introduction
2
-
3
- Welcome to {{TITLE}}, a tabletop roleplaying supplement. This first page is a
4
- starting point — replace it with your own rules, lore, and tables.
5
-
6
- ## How to use this supplement
7
-
8
- Write your rules in plain paragraphs, then break out the parts players reach for
9
- most into clearly labelled blocks. Headings keep long sections easy to scan at
10
- the table.
11
-
12
- ## A sample rule
13
-
14
- > **Quick rule:** When two characters disagree on the outcome of an action, both
15
- > roll. The higher result decides what happens, and the other player narrates
16
- > how their character reacts.
17
-
18
- ## A sample stat block
19
-
20
- Use a simple table for stat blocks so the numbers line up neatly on the page:
21
-
22
- | Attribute | Value |
23
- | --------- | ----- |
24
- | Health | 10 |
25
- | Defense | 4 |
26
- | Speed | 6 |
27
-
28
- Add more files alongside this one and they become the next sections of your
29
- supplement. Start writing and watch your rules take shape in the preview.
@@ -1,18 +0,0 @@
1
- import type { ResolvedConfig } from "../schema/manifest.types";
2
- export type ValidationProfile = "dtrpg";
3
- export declare const DTRPG_STRICT_PDF_CHECKS: readonly ["pdf.structure.qpdf", "pdf.print.pdfx-markers", "pdf.print.pdfx-metadata", "pdf.print.embedded-fonts"];
4
- /**
5
- * Fill in the strict PDF checks (structure/PDF-X markers/metadata/embedded
6
- * fonts) for any of them the config left unset, without overwriting an
7
- * author's explicit choice. ARCH finding #21: replaces the old
8
- * `applyDtrpgPdfDefaults` name, which implied a dtrpg-only opt-in even though
9
- * `executeValidation` applies it to every PDF validation regardless of
10
- * profile/preset — see validation-exec.ts.
11
- */
12
- export declare function applyDefaultPdfStrictChecks(config: ResolvedConfig): ResolvedConfig;
13
- /**
14
- * @deprecated Use {@link applyDefaultPdfStrictChecks} — kept as an alias so
15
- * existing call sites keep resolving under the old, dtrpg-branded name.
16
- */
17
- export declare const applyDtrpgPdfDefaults: typeof applyDefaultPdfStrictChecks;
18
- export declare function applyValidationProfile(config: ResolvedConfig, profile: ValidationProfile): ResolvedConfig;
@@ -1,9 +0,0 @@
1
- title: "{{TITLE}}"
2
- authors:
3
- - "{{AUTHOR}}"
4
- source:
5
- files:
6
- - chapter-01.md
7
- styles:
8
- - styles/book.css
9
- preset: dtrpg
@@ -1,105 +0,0 @@
1
- import {
2
- BUILT_IN_TEMPLATE_IDS,
3
- scaffoldProject
4
- } from "./cli-7ykx7pp0.js";
5
- import"./cli-h7phg3ka.js";
6
- import {
7
- EXIT_CODES,
8
- UsageError,
9
- rejectExtraPositionals,
10
- rejectUnknownFlags
11
- } from "./cli-hvqjs342.js";
12
- import"./cli-5bxx0c4y.js";
13
- import"./cli-1tyj4bk3.js";
14
- import"./cli-yja077f6.js";
15
- import"./cli-37x76zdn.js";
16
-
17
- // src/commands/new.ts
18
- import { defineCommand } from "citty";
19
- import { resolve } from "node:path";
20
- var newArgs = {
21
- name: {
22
- type: "positional",
23
- description: "Project name (becomes the title and folder name)",
24
- required: true
25
- },
26
- author: {
27
- type: "string",
28
- description: "Author name to record in the project"
29
- },
30
- dir: {
31
- type: "string",
32
- description: "Parent directory to create the project in (default: current directory)"
33
- },
34
- folder: {
35
- type: "string",
36
- description: "Folder name to create (default: a slug of the project name)"
37
- },
38
- template: {
39
- type: "string",
40
- description: `Starter template: ${BUILT_IN_TEMPLATE_IDS.join(", ")} (default: book)`
41
- },
42
- git: {
43
- type: "boolean",
44
- description: "Initialise local version history (default: true; use --no-git to skip)",
45
- default: true
46
- }
47
- };
48
- var new_default = defineCommand({
49
- meta: {
50
- name: "new",
51
- description: "Create a new Gutterpress project from a starter template"
52
- },
53
- args: newArgs,
54
- async run({ args, rawArgs }) {
55
- try {
56
- rejectUnknownFlags(rawArgs, newArgs, "new");
57
- rejectExtraPositionals(args._, 1, "new");
58
- } catch (error) {
59
- if (error instanceof UsageError) {
60
- console.error(error.message);
61
- process.exit(error.exitCode);
62
- }
63
- throw error;
64
- }
65
- const name = String(args.name);
66
- const parentDir = resolve(typeof args.dir === "string" && args.dir ? args.dir : process.cwd());
67
- let template;
68
- if (typeof args.template === "string" && args.template) {
69
- if (!BUILT_IN_TEMPLATE_IDS.includes(args.template)) {
70
- console.error(`Unknown template "${args.template}". Choose one of: ${BUILT_IN_TEMPLATE_IDS.join(", ")}.`);
71
- process.exit(EXIT_CODES.USAGE);
72
- }
73
- template = args.template;
74
- }
75
- try {
76
- const result = await scaffoldProject({
77
- name,
78
- author: typeof args.author === "string" ? args.author : undefined,
79
- parentDir,
80
- folderName: typeof args.folder === "string" && args.folder ? args.folder : undefined,
81
- template,
82
- versionHistory: args.git === false ? "none" : "local-git"
83
- });
84
- console.log(`Created project: ${result.projectDir}`);
85
- console.log(` manifest: ${result.manifestPath}`);
86
- console.log(` start writing in: ${result.openFile}`);
87
- if (result.versionHistory === "local-git") {
88
- console.log(" version history: enabled (local snapshots)");
89
- } else if (result.versionHistoryError) {
90
- console.log(` version history: not enabled (${result.versionHistoryError})`);
91
- }
92
- console.log("");
93
- console.log(`Next: gutterpress preview "${result.projectDir}"`);
94
- } catch (e) {
95
- const err = e;
96
- const code = err && typeof err.code === "string" ? err.code : "scaffold-io";
97
- console.error(`Could not create project: ${err?.message ?? String(e)}`);
98
- process.exit(code === "scaffold-io" ? EXIT_CODES.PIPELINE : EXIT_CODES.USAGE);
99
- }
100
- }
101
- });
102
- export {
103
- newArgs,
104
- new_default as default
105
- };
@@ -1,155 +0,0 @@
1
- // Gutterpress preview shell controller (shared by the CLI preview and Electron).
2
- // Every source change takes one correctness-first path: paginate a fresh hidden
3
- // full document, then swap it in without flashing unpaginated content.
4
- //
5
- // TRANSPORT IS ABSTRACTED: change events arrive through connectChanges(), which
6
- // uses WebSocket by default but honors window.__GUTTERPRESS_CHANGE_SOURCE so an Electron
7
- // host can feed the same full-reload signal over IPC.
8
- (function () {
9
- 'use strict';
10
- var active = document.getElementById('gutterpress-active');
11
- var building = null;
12
- if (!active) return;
13
-
14
- // Transparent bridge relay: forward host-toolbar commands to the active book
15
- // iframe and relay its replies/events back to the host.
16
- window.addEventListener('message', function (e) {
17
- try {
18
- if (window.parent !== window && e.source === window.parent) {
19
- if (active && active.contentWindow) active.contentWindow.postMessage(e.data, '*');
20
- } else if (active && e.source === active.contentWindow && window.parent !== window) {
21
- window.parent.postMessage(e.data, '*');
22
- }
23
- } catch (_) {}
24
- });
25
-
26
- function fdoc(f) { try { return f.contentDocument; } catch (_) { return null; } }
27
- function fwin(f) { try { return f.contentWindow; } catch (_) { return null; } }
28
- function chapterOf(el) {
29
- var chapter = el.closest && el.closest('[data-chapter-src]');
30
- return chapter ? chapter.getAttribute('data-chapter-src') : null;
31
- }
32
- function blocksInChapter(d, chapter) {
33
- var all = d.querySelectorAll('[data-source-line]');
34
- if (!chapter) return all;
35
- var matched = [];
36
- for (var i = 0; i < all.length; i++) {
37
- if (chapterOf(all[i]) === chapter) matched.push(all[i]);
38
- }
39
- return matched;
40
- }
41
-
42
- // data-source-line resets per source file, so preserve both chapter and line.
43
- function capture(f) {
44
- var d = fdoc(f); if (!d) return null;
45
- var els = d.querySelectorAll('[data-source-line]'), best = null, bestTop = -Infinity;
46
- for (var i = 0; i < els.length; i++) {
47
- var r = els[i].getBoundingClientRect();
48
- if (r.bottom < 0 || r.height === 0) continue;
49
- if (r.top <= 80 && r.top > bestTop) { bestTop = r.top; best = els[i]; }
50
- }
51
- if (!best) {
52
- for (var j = 0; j < els.length; j++) {
53
- var rr = els[j].getBoundingClientRect();
54
- if (rr.bottom > 0 && rr.height > 0) { best = els[j]; break; }
55
- }
56
- }
57
- if (!best) return null;
58
- return {
59
- chapter: chapterOf(best),
60
- line: best.getAttribute('data-source-line'),
61
- offset: best.getBoundingClientRect().top
62
- };
63
- }
64
-
65
- function restore(f, anchor) {
66
- if (!anchor) return;
67
- var w = fwin(f), d = fdoc(f); if (!w || !d) return;
68
- var els = blocksInChapter(d, anchor.chapter);
69
- var wanted = parseInt(anchor.line, 10), el = null, bestDiff = Infinity;
70
- for (var i = 0; i < els.length; i++) {
71
- var line = parseInt(els[i].getAttribute('data-source-line'), 10);
72
- if (String(els[i].getAttribute('data-source-line')) === String(anchor.line)) {
73
- el = els[i];
74
- break;
75
- }
76
- var diff = Math.abs(line - wanted);
77
- if (diff < bestDiff) { bestDiff = diff; el = els[i]; }
78
- }
79
- if (el) w.scrollBy(0, el.getBoundingClientRect().top - anchor.offset);
80
- }
81
-
82
- function onReady(f, callback) {
83
- var w = fwin(f), d = fdoc(f); if (!w || !d) { callback(); return; }
84
- var hasPaged = !!d.querySelector('script[src*="paged.polyfill"], script[src*="pagedjs"]');
85
- if (hasPaged) {
86
- var done = false;
87
- var finish = function () { if (!done) { done = true; callback(); } };
88
- w.addEventListener('renderingComplete', finish, { once: true });
89
- setTimeout(finish, 180000);
90
- return;
91
- }
92
- var attempts = 0;
93
- (function poll() {
94
- var current = fdoc(f);
95
- if (current && current.querySelectorAll('.pagedjs_page').length > 0) { callback(); return; }
96
- if (attempts++ < 800) setTimeout(poll, 25); else callback();
97
- })();
98
- }
99
-
100
- function swap() {
101
- // A newer reload invalidates any older hidden pagination. Its callback also
102
- // checks identity before swapping, so stale work can never win a race.
103
- if (building && building.parentNode) building.parentNode.removeChild(building);
104
- building = null;
105
-
106
- var frame = document.createElement('iframe');
107
- frame.style.visibility = 'hidden';
108
- frame.setAttribute('aria-hidden', 'true');
109
- frame.src = '/book.html?gutterpressshell=1&bust=' + Date.now();
110
- building = frame;
111
-
112
- var finished = false;
113
- function finish() {
114
- if (finished || building !== frame) return;
115
- finished = true;
116
- // Capture at swap time, not rebuild start: the visible frame stays usable
117
- // while a long book paginates and the user may scroll during that work.
118
- var anchor = capture(active);
119
- restore(frame, anchor);
120
- frame.style.visibility = 'visible';
121
- frame.removeAttribute('aria-hidden');
122
- var old = active;
123
- active = frame;
124
- building = null;
125
- requestAnimationFrame(function () {
126
- requestAnimationFrame(function () {
127
- if (old && old.parentNode) old.parentNode.removeChild(old);
128
- });
129
- });
130
- }
131
-
132
- frame.addEventListener('load', function () { onReady(frame, finish); });
133
- document.body.appendChild(frame);
134
- }
135
-
136
- function connectChanges(onMessage) {
137
- var source = window.__GUTTERPRESS_CHANGE_SOURCE;
138
- if (source && typeof source.subscribe === 'function') return source.subscribe(onMessage);
139
- var wsPath = window.__GUTTERPRESS_HMR || '/__gutterpress-hmr';
140
- var ws = new WebSocket(location.origin.replace(/^http/, 'ws') + wsPath);
141
- ws.onmessage = function (event) {
142
- var message;
143
- try { message = JSON.parse(event.data); } catch (_) { return; }
144
- onMessage(message);
145
- };
146
- return function () { try { ws.close(); } catch (_) {} };
147
- }
148
-
149
- connectChanges(function (message) {
150
- if (!message || !message.type) return;
151
- // Accept legacy content-update producers, but never perform an isolated
152
- // chapter splice: a changed boundary can affect every following page.
153
- if (message.type === 'full-reload' || message.type === 'content-update') swap();
154
- });
155
- })();
@@ -1,65 +0,0 @@
1
- /* =============================================================================
2
- TTRPG Supplement — gutterpress built-in theme (theme.css)
3
- Bold headings, parchment fills, boxed blocks. Customize the tokens in :root.
4
- ============================================================================= */
5
-
6
- :root {
7
- --color-ink: #241a12; /* body text */
8
- --color-ink-muted: #5c4a36; /* captions, secondary text */
9
- --color-accent: #7a2222; /* headings, key accents */
10
- --color-paper: #faf4e6; /* parchment page background */
11
- --color-tint: #efe3c8; /* stat-block / callout fill */
12
- --color-rule: #b89a6a; /* borders, rules */
13
-
14
- --font-body: "Palatino Linotype", "Book Antiqua", Palatino, serif;
15
- --font-display: "Trajan Pro", "Cinzel", "Georgia", serif;
16
- --font-mono: "Consolas", "Menlo", monospace;
17
-
18
- --scale-h1: 2.4rem;
19
- --scale-h2: 1.7rem;
20
- --scale-h3: 1.25rem;
21
- --leading: 1.45;
22
- }
23
-
24
- @page {
25
- size: 8.5in 11in;
26
- margin: 0.7in;
27
- }
28
-
29
- body {
30
- color: var(--color-ink);
31
- background: var(--color-paper);
32
- font-family: var(--font-body);
33
- line-height: var(--leading);
34
- font-size: 10.5pt;
35
- }
36
-
37
- h1, h2, h3 {
38
- font-family: var(--font-display);
39
- color: var(--color-accent);
40
- letter-spacing: 0.02em;
41
- text-transform: uppercase;
42
- font-weight: 700;
43
- }
44
- h1 { font-size: var(--scale-h1); margin: 0 0 0.5em; border-bottom: 3px double var(--color-rule); padding-bottom: 0.2em; }
45
- h2 { font-size: var(--scale-h2); margin: 1.3em 0 0.4em; }
46
- h3 { font-size: var(--scale-h3); margin: 1.1em 0 0.3em; letter-spacing: 0; }
47
-
48
- p { margin: 0 0 0.7em; }
49
-
50
- a { color: var(--color-accent); text-decoration: none; }
51
-
52
- blockquote {
53
- margin: 1em 0; padding: 0.6em 0.9em;
54
- background: var(--color-tint);
55
- border: 1px solid var(--color-rule);
56
- border-radius: 4px;
57
- }
58
-
59
- table {
60
- width: 100%; border-collapse: collapse; margin: 1em 0;
61
- }
62
- th, td { border: 1px solid var(--color-rule); padding: 0.35em 0.6em; text-align: left; }
63
- th { background: var(--color-tint); font-family: var(--font-display); }
64
-
65
- code, pre { font-family: var(--font-mono); font-size: 0.9em; }
@@ -1,6 +0,0 @@
1
- {
2
- "name": "TTRPG Supplement",
3
- "author": "gutterpress",
4
- "description": "Bold display headings, warm parchment fills, and boxed stat blocks for game books.",
5
- "preview": null
6
- }