gutterpress 0.11.23-alpha.7 → 0.11.23-beta.1

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 +2 -0
  2. package/dist/{README-syg2hn83.md → README-8rct3axy.md} +2 -2
  3. package/dist/api/index.d.ts +3 -3
  4. package/dist/api/index.js +7 -5
  5. package/dist/{build-w572wq10.js → build-dg8a566j.js} +8 -7
  6. package/dist/{cli-eatdqx3z.js → cli-1fy1pm9d.js} +1 -1
  7. package/dist/cli-65gy8ask.js +82 -0
  8. package/dist/{cli-3cvq3n1c.js → cli-989eg8jz.js} +186 -83
  9. package/dist/{cli-6pv84kf3.js → cli-e31jshz6.js} +6 -84
  10. package/dist/{cli-58fsk6d7.js → cli-k087gj0p.js} +66 -34
  11. package/dist/{cli-5ba6bm6z.js → cli-s1zt779w.js} +1 -1
  12. package/dist/{cli-wyg7qstd.js → cli-ttj95aex.js} +3 -3
  13. package/dist/{cli-mmgtqnmr.js → cli-vhav8g82.js} +1 -1
  14. package/dist/cli.js +12 -12
  15. package/dist/{doctor-2f3zqmza.js → doctor-g4a42tcz.js} +3 -3
  16. package/dist/engine/shared/page-vars.d.ts +32 -0
  17. package/dist/{engine-4rabsgwq.js → engine-jvkcahr1.js} +2 -1
  18. package/dist/{engine-wm4fggac.js → engine-wgkaqjsc.js} +3 -3
  19. package/dist/{ext-0cgky8n3.js → ext-w9d5ftcg.js} +8 -7
  20. package/dist/{extension-restore-vghc58vx.js → extension-restore-5x4mj79d.js} +3 -3
  21. package/dist/{extension-restore-e7je32pr.js → extension-restore-as0g38p1.js} +5 -4
  22. package/dist/{gutterpress-viewer-mafsby5k.js → gutterpress-viewer-ze691rrd.js} +52 -13
  23. package/dist/{index-2vxzxyyj.js → index-798jt0qc.js} +9 -9
  24. package/dist/{index-fp7xwvpr.js → index-a4j3xcfp.js} +3 -3
  25. package/dist/{index-nfysshcc.js → index-c6y0p7jy.js} +249 -139
  26. package/dist/{index-wm4x7xbf.js → index-fk2kpf2t.js} +58 -26
  27. package/dist/{index-hphcn7th.js → index-rj5vrhz6.js} +5 -85
  28. package/dist/index-tayzfr9c.js +99 -0
  29. package/dist/index.d.ts +2 -0
  30. package/dist/index.js +233 -6
  31. package/dist/lib/book-css.d.ts +56 -0
  32. package/dist/lib/markdown/assemble.d.ts +58 -0
  33. package/dist/lib/publish/google-drive.d.ts +12 -4
  34. package/dist/lib/publish/manifest-publish.d.ts +6 -0
  35. package/dist/lib/publish/providers/gdrive-folder.d.ts +30 -0
  36. package/dist/lib/publish/run-publish.d.ts +6 -1
  37. package/dist/lib/publish/types.d.ts +52 -5
  38. package/dist/lib/remote-auth/converge-merge.d.ts +3 -3
  39. package/dist/lib/remote-auth/sync-messages.d.ts +2 -2
  40. package/dist/lib/root-tokens.d.ts +76 -0
  41. package/dist/lib/version-changes.d.ts +12 -12
  42. package/dist/{manifest-79zsgkpn.yaml → manifest-0vwaskj2.yaml} +1 -1
  43. package/dist/{manifest-2kc56wvw.yaml → manifest-aya31jwt.yaml} +1 -1
  44. package/dist/{manifest-41ddzm13.yaml → manifest-mffah3hr.yaml} +1 -1
  45. package/dist/{manifest-sqzm4681.yaml → manifest-wbcv8cbp.yaml} +2 -2
  46. package/dist/{manifest.schema-93sd7zje.json → manifest.schema-0jgj0jt1.json} +1 -1
  47. package/dist/{new-88rkrv71.js → new-jtk028rf.js} +8 -7
  48. package/dist/{page-rules-ytj9p04v.css → page-rules-vnja6grt.css} +2 -2
  49. package/dist/{plugins-3aj0wc5h.js → plugins-bcbe7xs8.js} +1 -1
  50. package/dist/{plugins-sprxsz5d.js → plugins-zsm6wv79.js} +3 -3
  51. package/dist/{preflight-11dg1qkk.js → preflight-cs6mj1d6.js} +8 -7
  52. package/dist/{preview-nftp782j.js → preview-d1j1g4qf.js} +8 -7
  53. package/dist/{publish-6tgxfck9.js → publish-7yw4jyrt.js} +24 -8
  54. package/dist/render.js +65 -24
  55. package/dist/schema/manifest.types.d.ts +2 -1
  56. package/dist/{tokens-n82t4k9h.css → tokens-a7grv940.css} +4 -5
  57. package/dist/{validate-pktqrye5.js → validate-rkcdnzhw.js} +8 -7
  58. package/package.json +1 -1
  59. package/dist/index-46ycxe6r.js +0 -18
@@ -8,7 +8,7 @@ import {
8
8
  stampCreator,
9
9
  stripAnnotations,
10
10
  transparentPages
11
- } from "./cli-mmgtqnmr.js";
11
+ } from "./cli-vhav8g82.js";
12
12
  import {
13
13
  blackTextToGray
14
14
  } from "./cli-ht1bcqdd.js";
@@ -17,7 +17,7 @@ import {
17
17
  MARGIN_BOX_IGNORED_PROPERTIES,
18
18
  getAssetPath,
19
19
  lastPageMode
20
- } from "./cli-58fsk6d7.js";
20
+ } from "./cli-k087gj0p.js";
21
21
  import {
22
22
  countLinkAnnotations,
23
23
  getImageResolutions,
@@ -56,17 +56,11 @@ import {
56
56
  TARGET_IDS,
57
57
  addExtension,
58
58
  applyDefaultPdfStrictChecks,
59
- cssLength,
60
59
  ensureProjectGitignore,
61
- formatPageSize,
62
- isPositivePoints,
63
- judgeRect,
64
60
  loadManifest,
65
61
  loadManifestDoc,
66
62
  loadManifestWithPath,
67
63
  npmRegistryUrl,
68
- pageBoxes,
69
- printAreaMeta,
70
64
  publishTargetFor,
71
65
  resolveConfig,
72
66
  resolveConfigForTarget,
@@ -75,7 +69,7 @@ import {
75
69
  uniqueExtensionId,
76
70
  withFetchTimeout,
77
71
  writeManifestDoc
78
- } from "./cli-6pv84kf3.js";
72
+ } from "./cli-e31jshz6.js";
79
73
  import {
80
74
  EXTENSION_MANIFEST_FILENAME,
81
75
  GUTTERPRESS_CSS,
@@ -96,7 +90,7 @@ import {
96
90
  readExtensionMeta,
97
91
  resolveDeclaredStyles,
98
92
  slugify
99
- } from "./cli-wyg7qstd.js";
93
+ } from "./cli-ttj95aex.js";
100
94
  import {
101
95
  PACKAGE_META,
102
96
  PACKAGE_VERSION,
@@ -106,12 +100,20 @@ import {
106
100
  log,
107
101
  setLogLevel,
108
102
  warn
109
- } from "./cli-eatdqx3z.js";
103
+ } from "./cli-1fy1pm9d.js";
110
104
  import {
111
105
  DEBOUNCE,
112
106
  UsageError,
113
107
  resolvePort
114
108
  } from "./cli-d8xjns66.js";
109
+ import {
110
+ cssLength,
111
+ formatPageSize,
112
+ isPositivePoints,
113
+ judgeRect,
114
+ pageBoxes,
115
+ printAreaMeta
116
+ } from "./cli-65gy8ask.js";
115
117
  import {
116
118
  BuildError
117
119
  } from "./cli-46ycxe6r.js";
@@ -1596,7 +1598,7 @@ function checkCssRaw(css, from) {
1596
1598
  warnings.push({
1597
1599
  rule: rulePageSizeInStylesheet,
1598
1600
  severity: "warning",
1599
- message: `"size" in a stylesheet's @page rule is ignored: the page size comes from the manifest (preset:, or page: width and height — Setup > Book in the app), which is written after every stylesheet. Remove this declaration; to print at another size, change the manifest.`,
1601
+ message: `"size" in a stylesheet's @page rule is ignored: the page size comes from the manifest (preset:, or page: width and height — set them in Book setup), which is written after every stylesheet. Remove this declaration; to print at another size, change the manifest.`,
1600
1602
  ...nodeLoc(decl)
1601
1603
  });
1602
1604
  return;
@@ -3097,7 +3099,7 @@ registerCheck(check27);
3097
3099
  import { readFile as readFile13 } from "node:fs/promises";
3098
3100
 
3099
3101
  // src/checks/source/character-table.ts
3100
- var CHARACTER_PATTERN = new RegExp("[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u00A0\\u00AD\\u00B4\\u02BC" + "\\u061C\\u2000-\\u2015\\u2018\\u201B-\\u201D\\u201F\\u2022\\u2026" + "\\u2028-\\u202F\\u2032\\u2033\\u205F\\u2060\\u2066-\\u2069\\u2190\\u2192" + "\\u2194\\u21D2\\u2212\\u25AA\\u25E6\\u2605\\u2713\\u2714\\u2717\\u3000" + "\\uE000-\\uF8FF\\uFEFF\\uFF02\\uFF07\\uFFFD]" + "|\\p{Emoji_Presentation}|\\p{Extended_Pictographic}(?=\\uFE0F)" + "|(?<=[\\p{L}\\p{N}])\\u2019|\\u2019(?![\\p{L}\\p{N}])", "gu");
3102
+ var CHARACTER_PATTERN = new RegExp("[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u00A0\\u00AD\\u00B4\\u02BC" + "\\u061C\\u2000-\\u2015\\u2018\\u201B-\\u201D\\u201F\\u2022\\u2026" + "\\u2028-\\u202F\\u2032\\u2033\\u205F\\u2060\\u2066-\\u2069\\u2190\\u2192" + "\\u2194\\u21D2\\u25AA\\u25E6\\u2605\\u2713\\u2714\\u2717\\u3000" + "\\uE000-\\uF8FF\\uFEFF\\uFF02\\uFF07\\uFFFD]" + "|\\p{Emoji_Presentation}|\\p{Extended_Pictographic}(?=\\uFE0F)" + "|(?<=[\\p{L}\\p{N}])\\u2019|\\u2019(?![\\p{L}\\p{N}])", "gu");
3101
3103
  var NAMED = {
3102
3104
  8212: ["Em dash", "typographer", "---"],
3103
3105
  8211: ["En dash", "typographer", "--"],
@@ -3118,7 +3120,6 @@ var NAMED = {
3118
3120
  8209: ["Non-breaking hyphen", "lookalike", "-"],
3119
3121
  8210: ["Figure dash", "lookalike", "-"],
3120
3122
  8213: ["Horizontal bar", "lookalike", "---"],
3121
- 8722: ["Minus sign", "lookalike", "-"],
3122
3123
  8226: ["Bullet", "symbol", "&bull;"],
3123
3124
  9702: ["White bullet", "symbol", "&#x25E6;"],
3124
3125
  9642: ["Small square", "symbol", "&squf;"],
@@ -3858,6 +3859,7 @@ function canonicalChapterId(p) {
3858
3859
  }
3859
3860
 
3860
3861
  // src/lib/markdown/assemble.ts
3862
+ import postcss5 from "postcss";
3861
3863
  function pageGeometryCss(page) {
3862
3864
  if (!page || !isPositivePoints(page.width) || !isPositivePoints(page.height))
3863
3865
  return null;
@@ -3878,6 +3880,65 @@ function defaultPageMarginCss(page) {
3878
3880
  @page { margin: ${cssLength(DEFAULT_PAGE_MARGIN_PT)}; }
3879
3881
  }`;
3880
3882
  }
3883
+ function liftPageRules(css) {
3884
+ if (!/@page\b/i.test(css))
3885
+ return { layered: css, pages: "" };
3886
+ let root;
3887
+ try {
3888
+ root = postcss5.parse(css);
3889
+ } catch {
3890
+ return { layered: css, pages: "" };
3891
+ }
3892
+ const found = [];
3893
+ root.walkAtRules(/^page$/i, (rule) => {
3894
+ found.push(rule);
3895
+ });
3896
+ const pages = found.map((rule) => {
3897
+ let lifted = rule.clone();
3898
+ for (let p = rule.parent;p instanceof postcss5.AtRule || p instanceof postcss5.Rule; p = p.parent) {
3899
+ if (p instanceof postcss5.AtRule && p.name.toLowerCase() === "layer")
3900
+ continue;
3901
+ lifted = p.clone({ nodes: [] }).append(lifted);
3902
+ }
3903
+ rule.remove();
3904
+ return lifted.toString();
3905
+ });
3906
+ return { layered: root.toString(), pages: pages.join(`
3907
+ `) };
3908
+ }
3909
+ var CORE_MARKERS = liftPageRules(`${MARKER_CSS.trim()}
3910
+ ${TWO_VERSIONS_CSS.trim()}`);
3911
+ var CORE_VOCAB = liftPageRules(GUTTERPRESS_CSS.trim());
3912
+ function assembleCssChunks(opts) {
3913
+ const core = (css) => css ? [{ origin: { kind: "core" }, css }] : [];
3914
+ const headed = (title, chunks = []) => chunks.map((chunk2, i) => i === 0 ? { ...chunk2, css: `/* ${title} */
3915
+ ${chunk2.css.trim()}` } : chunk2);
3916
+ const pageGeometry = pageGeometryCss(opts.printArea);
3917
+ const defaultMargin = defaultPageMarginCss(opts.printArea);
3918
+ return [
3919
+ ...core("@layer gp.marker, gp.vocab;"),
3920
+ ...core(`/* gutterpress markers */
3921
+ @layer gp.marker {
3922
+ ${CORE_MARKERS.layered.trim()}
3923
+ }`),
3924
+ ...core(CORE_MARKERS.pages && `/* gutterpress markers: page rules */
3925
+ ${CORE_MARKERS.pages}`),
3926
+ ...core(`/* gutterpress */
3927
+ @layer gp.vocab {
3928
+ ${CORE_VOCAB.layered.trim()}
3929
+ }`),
3930
+ ...core(CORE_VOCAB.pages && `/* gutterpress: page rules */
3931
+ ${CORE_VOCAB.pages}`),
3932
+ ...core(defaultMargin && `/* page margins (default) */
3933
+ ${defaultMargin}`),
3934
+ ...headed("extension css", opts.extensions),
3935
+ ...core(`/* column alignment */
3936
+ ${TABLE_ALIGN_CSS.trim()}`),
3937
+ ...headed("project css", opts.book),
3938
+ ...core(pageGeometry && `/* page geometry (manifest) */
3939
+ ${pageGeometry}`)
3940
+ ];
3941
+ }
3881
3942
  async function assembleBookHtml(opts) {
3882
3943
  const title = opts.title ?? "Document";
3883
3944
  const projectCss = opts.projectCss ?? "";
@@ -3916,30 +3977,11 @@ async function assembleBookHtml(opts) {
3916
3977
  imageRefs.add(ref);
3917
3978
  opts.onImageRefs([...imageRefs]);
3918
3979
  }
3919
- const pageGeometry = pageGeometryCss(opts.printArea);
3920
- const defaultMargin = defaultPageMarginCss(opts.printArea);
3921
- const inlineCss = [
3922
- "@layer gp.marker, gp.vocab;",
3923
- `/* gutterpress markers */
3924
- @layer gp.marker {
3925
- ${MARKER_CSS.trim()}
3926
- ${TWO_VERSIONS_CSS.trim()}
3927
- }`,
3928
- `/* gutterpress */
3929
- @layer gp.vocab {
3930
- ${GUTTERPRESS_CSS.trim()}
3931
- }`,
3932
- defaultMargin ? `/* page margins (default) */
3933
- ${defaultMargin}` : null,
3934
- pluginCss ? `/* extension css */
3935
- ${pluginCss.trim()}` : null,
3936
- `/* column alignment */
3937
- ${TABLE_ALIGN_CSS.trim()}`,
3938
- projectCss ? `/* project css */
3939
- ${projectCss.trim()}` : null,
3940
- pageGeometry ? `/* page geometry (manifest) */
3941
- ${pageGeometry}` : null
3942
- ].filter(Boolean).join(`
3980
+ const inlineCss = assembleCssChunks({
3981
+ printArea: opts.printArea,
3982
+ extensions: pluginCss ? [{ origin: { kind: "extension" }, css: pluginCss }] : [],
3983
+ book: projectCss ? [{ origin: { kind: "book" }, css: projectCss }] : []
3984
+ }).map((chunk2) => chunk2.css).join(`
3943
3985
 
3944
3986
  `);
3945
3987
  const preloadTags = (opts.preloadImages ?? []).map((href) => `
@@ -3972,6 +4014,7 @@ async function resolveActiveMarkdownFiles(inputDir, configuredFiles) {
3972
4014
  async function renderChapters(inputDir, opts = {}) {
3973
4015
  const styles = await resolveActiveStyles(inputDir, opts.styles);
3974
4016
  const inlined = await inlineStyles(inputDir, styles);
4017
+ const book = liftPageRules(inlined.css);
3975
4018
  const blocks = [];
3976
4019
  const layers = [];
3977
4020
  const styleWarnings = [];
@@ -3986,10 +4029,13 @@ async function renderChapters(inputDir, opts = {}) {
3986
4029
  if (!css)
3987
4030
  continue;
3988
4031
  layers.push(group.layer);
4032
+ const { layered, pages } = liftPageRules(css);
3989
4033
  blocks.push(`/* ${group.name} */
3990
4034
  @layer ${group.layer} {
3991
- ${css.trim()}
3992
- }`);
4035
+ ${layered.trim()}
4036
+ }` + (pages ? `
4037
+ /* ${group.name}: page rules */
4038
+ ${pages}` : ""));
3993
4039
  }
3994
4040
  styleWarnings.push(...inlined.warnings);
3995
4041
  if (styleWarnings.length > 0)
@@ -4007,7 +4053,8 @@ ${css.trim()}
4007
4053
  return assembleBookHtml({
4008
4054
  files,
4009
4055
  readText: (relPath) => readFile16(join(inputDir, canonicalChapterId(relPath)), "utf-8"),
4010
- projectCss: inlined.css,
4056
+ projectCss: book.pages ? `${book.layered.trim()}
4057
+ ${book.pages}` : inlined.css,
4011
4058
  preloadImages: cssAssetCopies.map((c) => c.to),
4012
4059
  title: opts.title,
4013
4060
  language: opts.language,
@@ -5056,7 +5103,7 @@ class PdfOutput {
5056
5103
  const rawPdf = pdfxMode ? path7.join(stage, "raw.pdf") : path7.resolve(pdfFile);
5057
5104
  await fsp2.mkdir(path7.dirname(path7.resolve(pdfFile)), { recursive: true });
5058
5105
  log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
5059
- const { buildNativePdf } = await import("./engine-4rabsgwq.js");
5106
+ const { buildNativePdf } = await import("./engine-jvkcahr1.js");
5060
5107
  const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
5061
5108
  title: config.title,
5062
5109
  author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
@@ -6379,10 +6426,10 @@ function starterOverridesCss() {
6379
6426
  return `/**
6380
6427
  * Your book's own styles. Rules here win over your look's.
6381
6428
  *
6382
- * In the app you rarely need this file: Setup > Look chooses a look and
6383
- * changes its fonts and colors (those changes are saved here as :root
6384
- * custom properties), Setup > Advanced lists every setting, and Setup > Book
6385
- * sets the page size and margins (in the manifest).
6429
+ * In the app you rarely need this file: Book setup chooses a look, changes
6430
+ * its fonts and colors (those changes are saved here as :root custom
6431
+ * properties), lists every setting, and sets the page size and margins (in
6432
+ * the manifest).
6386
6433
  *
6387
6434
  * To change the look from the command line: gutterpress ext remove <old>
6388
6435
  * <book>, then gutterpress ext add <look> <book>.
@@ -7878,13 +7925,13 @@ async function createFolder(fetchImpl, accessToken, name) {
7878
7925
  throw await driveFailure(res, `Couldn't create the Google Drive folder "${name}"`);
7879
7926
  return await res.json();
7880
7927
  }
7881
- async function findFolderByName(fetchImpl, accessToken, name) {
7928
+ async function findFoldersByName(fetchImpl, accessToken, name, limit = 1) {
7882
7929
  const q = `name='${escapeDriveQueryValue(name)}' and mimeType='${FOLDER_MIME}' and trashed=false`;
7883
7930
  const params = new URLSearchParams({
7884
7931
  q,
7885
7932
  fields: "files(id,name)",
7886
7933
  orderBy: "modifiedTime desc",
7887
- pageSize: "1"
7934
+ pageSize: String(limit)
7888
7935
  });
7889
7936
  const res = await driveFetch(fetchImpl, `${DRIVE_API_BASE}/files?${params.toString()}`, {
7890
7937
  method: "GET",
@@ -7893,10 +7940,10 @@ async function findFolderByName(fetchImpl, accessToken, name) {
7893
7940
  if (!res.ok)
7894
7941
  throw await driveFailure(res, `Couldn't look up the Google Drive folder "${name}"`);
7895
7942
  const body = await res.json();
7896
- return body.files?.[0] ?? null;
7943
+ return body.files ?? [];
7897
7944
  }
7898
7945
  async function ensureFolder(fetchImpl, accessToken, name) {
7899
- const found = await findFolderByName(fetchImpl, accessToken, name);
7946
+ const [found] = await findFoldersByName(fetchImpl, accessToken, name);
7900
7947
  if (found)
7901
7948
  return found;
7902
7949
  return createFolder(fetchImpl, accessToken, name);
@@ -8029,8 +8076,80 @@ async function resumableUpload(fetchImpl, accessToken, opts) {
8029
8076
  }
8030
8077
  }
8031
8078
 
8032
- // src/lib/publish/providers/gdrive.ts
8079
+ // src/lib/publish/providers/gdrive-folder.ts
8033
8080
  var DEFAULT_FOLDER_NAME = "Gutterpress";
8081
+ var NO_OWN_FOLDER = { myFolderId: "", myFolder: "", myFolderFor: "", myFolderAccount: "" };
8082
+ function readConfig(req) {
8083
+ const text = (key) => {
8084
+ const value = req.config[key];
8085
+ return (typeof value === "string" ? value.trim() : "") || undefined;
8086
+ };
8087
+ return {
8088
+ folder: text("folder"),
8089
+ folderId: text("folderId"),
8090
+ myFolderId: text("myFolderId"),
8091
+ myFolder: text("myFolder"),
8092
+ myFolderFor: text("myFolderFor"),
8093
+ myFolderAccount: text("myFolderAccount"),
8094
+ account: req.deps.credentialAccount?.trim() ?? ""
8095
+ };
8096
+ }
8097
+ async function resolveFolder(req, accessToken) {
8098
+ const fetchImpl = req.deps.fetch ?? globalThis.fetch;
8099
+ const cfg = readConfig(req);
8100
+ let found = [];
8101
+ if (cfg.folderId) {
8102
+ const folder = await getFolderById(fetchImpl, accessToken, cfg.folderId);
8103
+ if (folder)
8104
+ found = [folder];
8105
+ } else if (cfg.folder) {
8106
+ found = await findFoldersByName(fetchImpl, accessToken, cfg.folder, 2);
8107
+ }
8108
+ const first = found[0];
8109
+ const book = { title: cfg.folder || cfg.folderId || "", reachable: !cfg.folderId || !!first };
8110
+ if (cfg.myFolderId) {
8111
+ const bookKey = cfg.folderId || cfg.folder;
8112
+ if (bookKey && cfg.myFolderFor === bookKey && (cfg.myFolderAccount ?? "") === cfg.account) {
8113
+ const mine = await getFolderById(fetchImpl, accessToken, cfg.myFolderId);
8114
+ return mine ? { state: "mine", destination: toDestination(mine), book } : { state: "unreachable", book, mine: cfg.myFolder || cfg.myFolderId };
8115
+ }
8116
+ if (book.reachable) {
8117
+ await req.deps.selections?.setSettings(req.project.projectDir, "gdrive", NO_OWN_FOLDER);
8118
+ }
8119
+ }
8120
+ if (cfg.folderId) {
8121
+ return first ? { state: "book", destination: toDestination(first) } : { state: "unreachable", book };
8122
+ }
8123
+ if (cfg.folder) {
8124
+ return {
8125
+ state: "by-name",
8126
+ name: cfg.folder,
8127
+ ...first ? { destination: toDestination(first) } : {},
8128
+ several: found.length > 1
8129
+ };
8130
+ }
8131
+ return { state: "unset" };
8132
+ }
8133
+ async function uploadFolder(where, fetchImpl, accessToken) {
8134
+ switch (where.state) {
8135
+ case "unreachable":
8136
+ throw new Error(where.mine ? `Your own Drive folder "${where.mine}" for this book can't be reached any more: it was trashed or deleted, or belongs to another Google account. Choose a folder you can reach on the Google Drive step (it is saved for you only).` : `This book publishes to the Drive folder "${where.book.title}", which this Google account can't reach: it isn't shared with Gutterpress under this account, or it was trashed or deleted. The book keeps its folder for everyone else. To publish to a folder you can reach, choose one for yourself on the Google Drive step in the desktop app (saved for you only), or pass --folder-id <id> or --folder <name> to "gutterpress publish" for one run.`);
8137
+ case "unset":
8138
+ return toDestination(await ensureFolder(fetchImpl, accessToken, DEFAULT_FOLDER_NAME));
8139
+ case "by-name":
8140
+ return where.destination ?? toDestination(await createFolder(fetchImpl, accessToken, where.name));
8141
+ default:
8142
+ return where.destination;
8143
+ }
8144
+ }
8145
+ function folderUrl(id) {
8146
+ return `https://drive.google.com/drive/folders/${id}`;
8147
+ }
8148
+ function toDestination(folder) {
8149
+ return { id: folder.id, title: folder.name, url: folderUrl(folder.id) };
8150
+ }
8151
+
8152
+ // src/lib/publish/providers/gdrive.ts
8034
8153
  var NOT_CONNECTED_MESSAGE = "Google Drive isn't connected. Run `gutterpress publish --provider gdrive --connect` (or set GDRIVE_REFRESH_TOKEN) first.";
8035
8154
  var info5 = {
8036
8155
  id: "gdrive",
@@ -8049,13 +8168,6 @@ var info5 = {
8049
8168
  },
8050
8169
  destinations: { label: "Folder", canCreate: true }
8051
8170
  };
8052
- function readConfig(req) {
8053
- const cfg = req.config;
8054
- return {
8055
- folder: cfg.folder?.trim() || undefined,
8056
- folderId: cfg.folderId?.trim() || undefined
8057
- };
8058
- }
8059
8171
  function formatBytes(bytes) {
8060
8172
  if (bytes < 1024 * 1024)
8061
8173
  return `${(bytes / 1024).toFixed(0)} KB`;
@@ -8077,22 +8189,6 @@ async function getAccessToken(req) {
8077
8189
  const accessToken = await mintAccessToken(resolved, req.deps.fetch ?? globalThis.fetch);
8078
8190
  return { accessToken, source: resolved.source };
8079
8191
  }
8080
- async function resolveFolder(fetchImpl, accessToken, cfg) {
8081
- if (cfg.folderId) {
8082
- const folder = await getFolderById(fetchImpl, accessToken, cfg.folderId);
8083
- if (!folder) {
8084
- throw new Error(`The Drive folder recorded in publish.gdrive.folderId ("${cfg.folderId}") can't be found — it may have been trashed or the id copied wrong. Pick the folder again.`);
8085
- }
8086
- return folder;
8087
- }
8088
- return ensureFolder(fetchImpl, accessToken, cfg.folder || DEFAULT_FOLDER_NAME);
8089
- }
8090
- function folderUrl(id) {
8091
- return `https://drive.google.com/drive/folders/${id}`;
8092
- }
8093
- function toDestination(folder) {
8094
- return { id: folder.id, title: folder.name, url: folderUrl(folder.id) };
8095
- }
8096
8192
  var ZIP_CHUNK_BYTES = 64 * 1024;
8097
8193
  function yieldToEventLoop() {
8098
8194
  return new Promise((resolve5) => setImmediate(resolve5));
@@ -8219,9 +8315,12 @@ var gdriveProvider = {
8219
8315
  const folder = await createFolder(req.deps.fetch ?? globalThis.fetch, accessToken, name);
8220
8316
  return toDestination(folder);
8221
8317
  },
8318
+ async destinationStatus(req) {
8319
+ const { accessToken } = await getAccessToken(req);
8320
+ return resolveFolder(req, accessToken);
8321
+ },
8222
8322
  async upload(req) {
8223
8323
  const fetchImpl = req.deps.fetch ?? globalThis.fetch;
8224
- const cfg = readConfig(req);
8225
8324
  const { accessToken } = await getAccessToken(req);
8226
8325
  const isHtml = req.artifact.format === "html";
8227
8326
  const source = isHtml ? await zipHtmlExport(req.artifact.path, req.project.title, (msg) => req.deps.onProgress?.(msg)) : {
@@ -8239,10 +8338,11 @@ var gdriveProvider = {
8239
8338
  }
8240
8339
  }
8241
8340
  req.deps.onProgress?.("Resolving the Drive folder…");
8242
- const folder = await resolveFolder(fetchImpl, accessToken, cfg);
8341
+ const where = await resolveFolder(req, accessToken);
8342
+ const folder = await uploadFolder(where, fetchImpl, accessToken);
8243
8343
  const fileName = source.fileName;
8244
8344
  const existing = await findFileInFolder(fetchImpl, accessToken, folder.id, fileName);
8245
- req.deps.onProgress?.(existing ? `Updating "${fileName}" in "${folder.name}"…` : `Uploading "${fileName}" to "${folder.name}"…`);
8345
+ req.deps.onProgress?.(existing ? `Updating "${fileName}" in "${folder.title}"…` : `Uploading "${fileName}" to "${folder.title}"…`);
8246
8346
  let lastReported = -1;
8247
8347
  const file = await resumableUpload(fetchImpl, accessToken, {
8248
8348
  ...existing ? { fileId: existing.id } : {},
@@ -8262,11 +8362,11 @@ var gdriveProvider = {
8262
8362
  return {
8263
8363
  kind: "published",
8264
8364
  url: file.webViewLink,
8265
- detail: `Uploaded "${fileName}" to the "${folder.name}" folder in your Google Drive${existing ? " (updated the existing file)" : ""}.`,
8365
+ detail: `Uploaded "${fileName}" to the "${folder.title}" folder in your Google Drive${existing ? " (updated the existing file)" : ""}.` + (where.state === "by-name" && where.several ? ` Several of your folders are named "${where.name}"; this is the most recently changed one: ${folder.url}` : ""),
8266
8366
  followUp: [
8267
8367
  "To share it, open it in Drive and use the Share button — Gutterpress never changes who can see your files.",
8268
8368
  isHtml ? "Google Drive stores files, not live websites — download and unzip it to view the export, or publish it as a real site with the Azure Static Web Apps provider." : undefined,
8269
- cfg.folderId ? undefined : `Tip (CLI): record the folder id in the manifest (publish.gdrive.folderId: "${folder.id}") so renaming the folder in Drive can never break publishing.`
8369
+ where.state === "unset" ? `Tip (CLI): record the folder id in the manifest (publish.gdrive.folderId: "${folder.id}") so renaming the folder in Drive can never break publishing.` : undefined
8270
8370
  ].filter((s) => !!s)
8271
8371
  };
8272
8372
  } finally {
@@ -8878,7 +8978,7 @@ async function resolvePublishProvider(id, book) {
8878
8978
  const ext = await bookExtensions(book);
8879
8979
  const npmEntry = ext.extensions.some((c) => !c.path && c.name === id);
8880
8980
  if (npmEntry) {
8881
- const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-e7je32pr.js");
8981
+ const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-as0g38p1.js");
8882
8982
  await restoreForCommand2(book.projectDir, { manifestPath: book.manifestPath, failFast: true });
8883
8983
  }
8884
8984
  const publishers = await discover(ext);
@@ -8916,7 +9016,7 @@ function infoFor(p) {
8916
9016
  }
8917
9017
  function pluginProvider(p) {
8918
9018
  let loading;
8919
- const load = () => loading ??= import("./plugins-sprxsz5d.js").then((m) => m.loadPublishFunction(p));
9019
+ const load = () => loading ??= import("./plugins-zsm6wv79.js").then((m) => m.loadPublishFunction(p));
8920
9020
  return {
8921
9021
  info: infoFor(p),
8922
9022
  async authenticate() {
@@ -8987,7 +9087,8 @@ async function requestFor(provider, options, deps) {
8987
9087
  const publishSettings = manifest.publish ?? {};
8988
9088
  const providerConfig = {
8989
9089
  ...publishSettings[provider.info.id] ?? {},
8990
- ...(await deps.selections?.settings(options.projectDir))?.[provider.info.id] ?? {}
9090
+ ...(await deps.selections?.settings(options.projectDir))?.[provider.info.id] ?? {},
9091
+ ...options.settings
8991
9092
  };
8992
9093
  const bookAccount = typeof providerConfig.credential === "string" ? providerConfig.credential.trim() : "";
8993
9094
  const effectiveAccount = bookAccount || deps.credentialAccount;
@@ -9205,7 +9306,7 @@ async function disconnectPublishCredential(key, deps, options = {}) {
9205
9306
  import { isSeq as isSeq2, isMap as isMap3, YAMLSeq, Scalar } from "yaml";
9206
9307
  // src/lib/version-changes.ts
9207
9308
  import git3 from "isomorphic-git";
9208
- import postcss5 from "postcss";
9309
+ import postcss6 from "postcss";
9209
9310
 
9210
9311
  // src/lib/remote-auth/transport.ts
9211
9312
  import git2 from "isomorphic-git";
@@ -9435,4 +9536,6 @@ function normalizeProjectKey(projectDir) {
9435
9536
  }
9436
9537
  // src/lib/publish/manifest-publish.ts
9437
9538
  import { isMap as isMap4 } from "yaml";
9539
+ // src/lib/root-tokens.ts
9540
+ import postcss7 from "postcss";
9438
9541
  export { reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, scaffoldProject, EXTENSION_KINDS, scaffoldExtension, BUILT_IN_TEMPLATE_IDS, importExtensionFromFile, importExtensionFromUrl, searchNpmExtensions, publishCredentialKey, publishConnectionStatus, listPublishAccounts, connectGoogleDrive, listBookPublishProviders, resolvePublishProvider, resolvePublishFormat, runPublish, connectPublishProvider, disconnectPublishCredential };
@@ -11,13 +11,16 @@ import {
11
11
  prettify,
12
12
  readExtensionMeta,
13
13
  slugify
14
- } from "./cli-wyg7qstd.js";
14
+ } from "./cli-ttj95aex.js";
15
15
  import {
16
16
  log
17
- } from "./cli-eatdqx3z.js";
17
+ } from "./cli-1fy1pm9d.js";
18
18
  import {
19
19
  UsageError
20
20
  } from "./cli-d8xjns66.js";
21
+ import {
22
+ isNonNegativePoints
23
+ } from "./cli-65gy8ask.js";
21
24
  import {
22
25
  BuildError,
23
26
  EXIT_CODES
@@ -94,87 +97,6 @@ import { existsSync, statSync } from "node:fs";
94
97
  import { resolve, dirname } from "node:path";
95
98
  import { parse as parseYaml, YAMLParseError } from "yaml";
96
99
 
97
- // src/engine/shared/print-area.ts
98
- function isPositivePoints(value) {
99
- return typeof value === "number" && Number.isFinite(value) && value > 0;
100
- }
101
- function isNonNegativePoints(value) {
102
- return typeof value === "number" && Number.isFinite(value) && value >= 0;
103
- }
104
- var round3 = (n) => Math.round(n * 1000) / 1000;
105
- function cssLength(pt) {
106
- const inches = round3(pt / 72);
107
- return Math.abs(inches * 72 - pt) < 0.000001 ? `${inches}in` : `${round3(pt)}pt`;
108
- }
109
- function formatPageSize(page) {
110
- return `${cssLength(page.width)} ${cssLength(page.height)}`;
111
- }
112
- function printAreaMeta(page) {
113
- const area = JSON.stringify({
114
- width: page.width,
115
- height: page.height,
116
- bleed: page.bleed ?? 0,
117
- safe: page.safe ?? 0,
118
- artSafe: page.artSafe,
119
- gutter: page.gutter,
120
- preset: page.preset,
121
- spec: page.spec
122
- });
123
- return `<meta name="gp-print-area" content='${area.replace(/&/g, "&amp;").replace(/'/g, "&#39;")}'>`;
124
- }
125
- function pageBoxes(area, side, scale = 1) {
126
- const w = area.width * scale;
127
- const h = area.height * scale;
128
- const b = Math.max(0, area.bleed) * scale;
129
- const s = Math.max(0, area.safe) * scale;
130
- const a = Math.max(0, area.artSafe) * scale;
131
- const g = Math.max(0, area.gutter) * scale;
132
- const [textLeft, textRight] = side === "verso" ? [s, g] : [g, s];
133
- const [artLeft, artRight] = side === "verso" ? [a, g] : [g, a];
134
- const trim = { x: side === "verso" ? b : 0, y: b, w: Math.max(0, w - b), h: Math.max(0, h - 2 * b) };
135
- return {
136
- sheet: { x: 0, y: 0, w, h },
137
- trim,
138
- safe: {
139
- x: trim.x + textLeft,
140
- y: trim.y + s,
141
- w: Math.max(0, trim.w - textLeft - textRight),
142
- h: Math.max(0, trim.h - 2 * s)
143
- },
144
- safeArt: {
145
- x: trim.x + artLeft,
146
- y: trim.y + a,
147
- w: Math.max(0, trim.w - artLeft - artRight),
148
- h: Math.max(0, trim.h - 2 * a)
149
- },
150
- bleed: b
151
- };
152
- }
153
- var SEVERITY = { inside: 0, unsafe: 1, bleed: 2, "off-page": 3 };
154
- function judgeRect(r, boxes, rule, tol = 0) {
155
- const { sheet, trim } = boxes;
156
- const safe = rule === "art" ? boxes.safeArt : boxes.safe;
157
- const sides = [
158
- ["left", -r.x, -safe.x, -trim.x, -sheet.x],
159
- ["right", r.x + r.w, safe.x + safe.w, trim.x + trim.w, sheet.x + sheet.w],
160
- ["top", -r.y, -safe.y, -trim.y, -sheet.y],
161
- ["bottom", r.y + r.h, safe.y + safe.h, trim.y + trim.h, sheet.y + sheet.h]
162
- ];
163
- let worst = { kind: "inside", gap: 0, need: 0, edge: null };
164
- let beyond = 0;
165
- for (const [edge, ink, limit, trimEdge, sheetEdge] of sides) {
166
- const past = ink - limit;
167
- if (past <= tol || rule === "art" && ink >= sheetEdge - tol)
168
- continue;
169
- const kind = ink > sheetEdge + tol ? "off-page" : ink > trimEdge + tol ? "bleed" : "unsafe";
170
- if (SEVERITY[kind] > SEVERITY[worst.kind] || kind === worst.kind && past > beyond) {
171
- worst = { kind, gap: trimEdge - ink, need: trimEdge - limit, edge };
172
- beyond = past;
173
- }
174
- }
175
- return worst;
176
- }
177
-
178
100
  // src/lib/presets.ts
179
101
  var PRESET_IDS = ["dtrpg", "book", "custom"];
180
102
  var DTRPG_PRESET = {
@@ -2195,4 +2117,4 @@ async function restoreForCommand(dir, options) {
2195
2117
  log.warn(problem);
2196
2118
  }
2197
2119
 
2198
- export { isPositivePoints, cssLength, formatPageSize, printAreaMeta, pageBoxes, judgeRect, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, resolveTargets, applyDefaultPdfStrictChecks, MANIFEST_FILENAMES, loadManifest, loadManifestWithPath, resolveConfig, resolveConfigForTarget, FriendlyHttpError, withFetchTimeout, loadManifestDoc, writeManifestDoc, npmRegistryUrl, ensureProjectGitignore, listProjectExtensions, setExtensionEnabled, removeExtension, addExtension, checkExtensionUpdates, updateExtensions, uniqueExtensionId, restorePinnedExtensions, restoreFailureMessage, restoreOfflineMessage, restoreForCommand };
2120
+ export { PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, resolveTargets, applyDefaultPdfStrictChecks, MANIFEST_FILENAMES, loadManifest, loadManifestWithPath, resolveConfig, resolveConfigForTarget, FriendlyHttpError, withFetchTimeout, loadManifestDoc, writeManifestDoc, npmRegistryUrl, ensureProjectGitignore, listProjectExtensions, setExtensionEnabled, removeExtension, addExtension, checkExtensionUpdates, updateExtensions, uniqueExtensionId, restorePinnedExtensions, restoreFailureMessage, restoreOfflineMessage, restoreForCommand };