stego-cli 0.1.3 → 0.1.4

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 (2) hide show
  1. package/dist/stego-cli.js +106 -6
  2. package/package.json +1 -1
package/dist/stego-cli.js CHANGED
@@ -4,6 +4,7 @@ import os from "node:os";
4
4
  import path from "node:path";
5
5
  import process from "node:process";
6
6
  import { spawnSync } from "node:child_process";
7
+ import { createInterface } from "node:readline/promises";
7
8
  import { fileURLToPath } from "node:url";
8
9
  import { markdownExporter } from "./exporters/markdown-exporter.js";
9
10
  import { createPandocExporter } from "./exporters/pandoc-exporter.js";
@@ -16,6 +17,7 @@ const STATUS_RANK = {
16
17
  };
17
18
  const RESERVED_COMMENT_PREFIX = "CMT";
18
19
  const ROOT_CONFIG_FILENAME = "stego.config.json";
20
+ const PROSE_FONT_PROMPT = "Switch workspace to proportional (prose-style) font? (recommended)";
19
21
  const SCAFFOLD_GITIGNORE_CONTENT = `node_modules/
20
22
  /dist/
21
23
  .DS_Store
@@ -75,6 +77,17 @@ This keeps your editor context focused and applies the project's recommended ext
75
77
  stego new-project --project my-book --title "My Book"
76
78
  \`\`\`
77
79
  `;
80
+ const PROSE_MARKDOWN_EDITOR_SETTINGS = {
81
+ "[markdown]": {
82
+ "editor.fontFamily": "Inter, Helvetica Neue, Helvetica, Arial, sans-serif",
83
+ "editor.fontSize": 17,
84
+ "editor.lineHeight": 28,
85
+ "editor.wordWrap": "wordWrapColumn",
86
+ "editor.wordWrapColumn": 72,
87
+ "editor.lineNumbers": "off"
88
+ },
89
+ "markdown.preview.fontFamily": "Inter, Helvetica Neue, Helvetica, Arial, sans-serif"
90
+ };
78
91
  const PROJECT_EXTENSION_RECOMMENDATIONS = [
79
92
  "matt-gold.stego-extension",
80
93
  "matt-gold.saurus-extension"
@@ -83,8 +96,8 @@ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
83
96
  const packageRoot = path.resolve(scriptDir, "..");
84
97
  let repoRoot = "";
85
98
  let config;
86
- main();
87
- function main() {
99
+ void main();
100
+ async function main() {
88
101
  const { command, options } = parseArgs(process.argv.slice(2));
89
102
  if (!command || command === "help" || command === "--help" || command === "-h") {
90
103
  printUsage();
@@ -93,7 +106,7 @@ function main() {
93
106
  try {
94
107
  switch (command) {
95
108
  case "init":
96
- initWorkspace({ force: readBooleanOption(options, "force") });
109
+ await initWorkspace({ force: readBooleanOption(options, "force") });
97
110
  return;
98
111
  case "list-projects":
99
112
  activateWorkspace(options);
@@ -101,7 +114,7 @@ function main() {
101
114
  return;
102
115
  case "new-project":
103
116
  activateWorkspace(options);
104
- createProject(readStringOption(options, "project"), readStringOption(options, "title"));
117
+ await createProject(readStringOption(options, "project"), readStringOption(options, "title"));
105
118
  return;
106
119
  case "validate": {
107
120
  activateWorkspace(options);
@@ -438,7 +451,7 @@ function findNearestFileUpward(startPath, filename) {
438
451
  current = parent;
439
452
  }
440
453
  }
441
- function initWorkspace(options) {
454
+ async function initWorkspace(options) {
442
455
  const targetRoot = process.cwd();
443
456
  const entries = fs
444
457
  .readdirSync(targetRoot, { withFileTypes: true })
@@ -457,6 +470,10 @@ function initWorkspace(options) {
457
470
  copyTemplateAsset(path.join(".vscode", "tasks.json"), targetRoot, copiedPaths);
458
471
  copyTemplateAsset(path.join(".vscode", "extensions.json"), targetRoot, copiedPaths, { optional: true });
459
472
  rewriteTemplateProjectPackageScripts(targetRoot);
473
+ const enableProseFont = await promptYesNo(PROSE_FONT_PROMPT, true);
474
+ if (enableProseFont) {
475
+ writeProjectProseEditorSettings(targetRoot, copiedPaths);
476
+ }
460
477
  writeInitRootPackageJson(targetRoot);
461
478
  logLine(`Initialized Stego workspace in ${targetRoot}`);
462
479
  for (const relativePath of copiedPaths) {
@@ -469,6 +486,34 @@ function initWorkspace(options) {
469
486
  logLine(" npm run list-projects");
470
487
  logLine(" npm run validate -- --project plague-demo");
471
488
  }
489
+ async function promptYesNo(question, defaultYes) {
490
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
491
+ return defaultYes;
492
+ }
493
+ const rl = createInterface({
494
+ input: process.stdin,
495
+ output: process.stdout
496
+ });
497
+ const suffix = defaultYes ? " [Y/n] " : " [y/N] ";
498
+ try {
499
+ while (true) {
500
+ const answer = (await rl.question(`${question}${suffix}`)).trim().toLowerCase();
501
+ if (!answer) {
502
+ return defaultYes;
503
+ }
504
+ if (answer === "y" || answer === "yes") {
505
+ return true;
506
+ }
507
+ if (answer === "n" || answer === "no") {
508
+ return false;
509
+ }
510
+ console.log("Please answer y or n.");
511
+ }
512
+ }
513
+ finally {
514
+ rl.close();
515
+ }
516
+ }
472
517
  function copyTemplateAsset(sourceRelativePath, targetRoot, copiedPaths, options) {
473
518
  const sourcePath = path.join(packageRoot, sourceRelativePath);
474
519
  if (!fs.existsSync(sourcePath)) {
@@ -580,6 +625,53 @@ function ensureProjectExtensionsRecommendations(projectRoot) {
580
625
  };
581
626
  fs.writeFileSync(extensionsPath, `${JSON.stringify(extensionsConfig, null, 2)}\n`, "utf8");
582
627
  }
628
+ function writeProjectProseEditorSettings(targetRoot, copiedPaths) {
629
+ const projectsRoot = path.join(targetRoot, "projects");
630
+ if (!fs.existsSync(projectsRoot)) {
631
+ return;
632
+ }
633
+ for (const entry of fs.readdirSync(projectsRoot, { withFileTypes: true })) {
634
+ if (!entry.isDirectory()) {
635
+ continue;
636
+ }
637
+ const projectRoot = path.join(projectsRoot, entry.name);
638
+ const settingsPath = writeProseEditorSettingsForProject(projectRoot);
639
+ copiedPaths.push(path.relative(targetRoot, settingsPath));
640
+ }
641
+ }
642
+ function writeProseEditorSettingsForProject(projectRoot) {
643
+ const vscodeDir = path.join(projectRoot, ".vscode");
644
+ const settingsPath = path.join(vscodeDir, "settings.json");
645
+ fs.mkdirSync(vscodeDir, { recursive: true });
646
+ let existingSettings = {};
647
+ if (fs.existsSync(settingsPath)) {
648
+ try {
649
+ const parsed = readJson(settingsPath);
650
+ if (isPlainObject(parsed)) {
651
+ existingSettings = parsed;
652
+ }
653
+ }
654
+ catch {
655
+ existingSettings = {};
656
+ }
657
+ }
658
+ const proseMarkdownSettings = isPlainObject(PROSE_MARKDOWN_EDITOR_SETTINGS["[markdown]"])
659
+ ? PROSE_MARKDOWN_EDITOR_SETTINGS["[markdown]"]
660
+ : {};
661
+ const existingMarkdownSettings = isPlainObject(existingSettings["[markdown]"])
662
+ ? existingSettings["[markdown]"]
663
+ : {};
664
+ const nextSettings = {
665
+ ...existingSettings,
666
+ "[markdown]": {
667
+ ...existingMarkdownSettings,
668
+ ...proseMarkdownSettings
669
+ },
670
+ "markdown.preview.fontFamily": PROSE_MARKDOWN_EDITOR_SETTINGS["markdown.preview.fontFamily"]
671
+ };
672
+ fs.writeFileSync(settingsPath, `${JSON.stringify(nextSettings, null, 2)}\n`, "utf8");
673
+ return settingsPath;
674
+ }
583
675
  function writeInitRootPackageJson(targetRoot) {
584
676
  const cliPackage = readJson(path.join(packageRoot, "package.json"));
585
677
  const cliVersion = typeof cliPackage.version === "string" ? cliPackage.version : "0.1.0";
@@ -621,7 +713,7 @@ function listProjects() {
621
713
  console.log(`- ${id}`);
622
714
  }
623
715
  }
624
- function createProject(projectIdOption, titleOption) {
716
+ async function createProject(projectIdOption, titleOption) {
625
717
  const projectId = (projectIdOption || "").trim();
626
718
  if (!projectId) {
627
719
  throw new Error("Project id is required. Use --project <project-id>.");
@@ -681,11 +773,19 @@ function createProject(projectIdOption, titleOption) {
681
773
  fs.writeFileSync(charactersNotesPath, "# Characters\n\n", "utf8");
682
774
  const projectExtensionsPath = path.join(projectRoot, ".vscode", "extensions.json");
683
775
  ensureProjectExtensionsRecommendations(projectRoot);
776
+ let projectSettingsPath = null;
777
+ const enableProseFont = await promptYesNo(PROSE_FONT_PROMPT, true);
778
+ if (enableProseFont) {
779
+ projectSettingsPath = writeProseEditorSettingsForProject(projectRoot);
780
+ }
684
781
  logLine(`Created project: ${path.relative(repoRoot, projectRoot)}`);
685
782
  logLine(`- ${path.relative(repoRoot, projectJsonPath)}`);
686
783
  logLine(`- ${path.relative(repoRoot, projectPackagePath)}`);
687
784
  logLine(`- ${path.relative(repoRoot, charactersNotesPath)}`);
688
785
  logLine(`- ${path.relative(repoRoot, projectExtensionsPath)}`);
786
+ if (projectSettingsPath) {
787
+ logLine(`- ${path.relative(repoRoot, projectSettingsPath)}`);
788
+ }
689
789
  }
690
790
  function getProjectIds() {
691
791
  const projectsDir = path.join(repoRoot, config.projectsDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stego-cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "Installable CLI for the Stego writing monorepo workflow.",
6
6
  "bin": {