plotlink-ows 1.0.4 → 1.0.8

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.
package/app/lib/paths.ts CHANGED
@@ -5,5 +5,12 @@ import fs from "fs";
5
5
  /** All user state lives in ~/.plotlink-ows/ — survives npx reinstalls */
6
6
  export const CONFIG_DIR = path.join(os.homedir(), ".plotlink-ows");
7
7
  export const ENV_FILE = path.join(CONFIG_DIR, ".env");
8
- // Ensure config dir exists on import
8
+ export const STORIES_DIR = path.join(CONFIG_DIR, "stories");
9
+ export const DATA_DIR = path.join(CONFIG_DIR, "data");
10
+ export const DB_PATH = path.join(DATA_DIR, "local.db");
11
+ export const DATABASE_URL = `file:${DB_PATH}`;
12
+
13
+ // Ensure persistent directories exist on import
9
14
  fs.mkdirSync(CONFIG_DIR, { recursive: true });
15
+ fs.mkdirSync(STORIES_DIR, { recursive: true });
16
+ fs.mkdirSync(DATA_DIR, { recursive: true });
@@ -5,7 +5,7 @@ generator client {
5
5
 
6
6
  datasource db {
7
7
  provider = "sqlite"
8
- url = "file:../../data/local.db"
8
+ url = env("DATABASE_URL")
9
9
  }
10
10
 
11
11
  model Session {
@@ -1,10 +1,7 @@
1
1
  import { Hono } from "hono";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
- import { fileURLToPath } from "url";
5
-
6
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
- const STORIES_DIR = path.join(__dirname, "..", "..", "stories");
4
+ import { STORIES_DIR } from "../lib/paths";
8
5
 
9
6
  const stories = new Hono();
10
7
 
@@ -2,13 +2,11 @@ import { Hono } from "hono";
2
2
  import * as pty from "node-pty";
3
3
  import path from "path";
4
4
  import fs from "fs";
5
- import { fileURLToPath } from "url";
6
5
  import { randomUUID } from "crypto";
6
+ import { STORIES_DIR, DATA_DIR } from "../lib/paths";
7
7
 
8
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
- const STORIES_DIR = path.join(__dirname, "..", "..", "stories");
10
8
  const MAX_SESSIONS = 5;
11
- const SESSION_FILE = path.join(__dirname, "..", "..", "data", "terminal-sessions.json");
9
+ const SESSION_FILE = path.join(DATA_DIR, "terminal-sessions.json");
12
10
 
13
11
  const terminal = new Hono();
14
12
 
package/app/server.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  import dotenv from "dotenv";
2
+ import os from "os";
2
3
  import path from "path";
3
4
  import { fileURLToPath } from "url";
4
- import { ENV_FILE } from "./lib/paths";
5
+ import { ENV_FILE, DATA_DIR, STORIES_DIR, DATABASE_URL } from "./lib/paths";
6
+
7
+ // Set DATABASE_URL before any Prisma imports
8
+ process.env.DATABASE_URL = DATABASE_URL;
5
9
 
6
10
  // Load .env from ~/.plotlink-ows/ before anything else
7
11
  const __dirnamePre = path.dirname(fileURLToPath(import.meta.url));
@@ -57,25 +61,72 @@ if (fs.existsSync(distPath)) {
57
61
  });
58
62
  }
59
63
 
64
+ /** Copy story directories from a source dir into STORIES_DIR, skipping duplicates */
65
+ function migrateStoriesFrom(srcDir: string, label: string) {
66
+ if (!fs.existsSync(srcDir) || srcDir === STORIES_DIR) return;
67
+ const entries = fs.readdirSync(srcDir, { withFileTypes: true })
68
+ .filter((d) => d.isDirectory() && !d.name.startsWith(".") && d.name !== "_example");
69
+ for (const entry of entries) {
70
+ const dest = path.join(STORIES_DIR, entry.name);
71
+ if (fs.existsSync(dest)) continue;
72
+ try {
73
+ fs.renameSync(path.join(srcDir, entry.name), dest);
74
+ } catch {
75
+ fs.cpSync(path.join(srcDir, entry.name), dest, { recursive: true });
76
+ }
77
+ console.log(` Migrated story "${entry.name}" from ${label}`);
78
+ }
79
+ }
80
+
81
+ /** Copy a single file if source exists and destination doesn't */
82
+ function migrateFileFrom(src: string, dest: string, label: string) {
83
+ if (fs.existsSync(src) && !fs.existsSync(dest)) {
84
+ fs.copyFileSync(src, dest);
85
+ console.log(` Migrated ${label} → ${path.dirname(dest)}`);
86
+ }
87
+ }
88
+
89
+ /** Migrate stories/data from old locations to ~/.plotlink-ows/ */
90
+ function migrateOldData() {
91
+ // 1. Scan all previous npx cache directories
92
+ const npxBase = path.join(os.homedir(), ".npm", "_npx");
93
+ if (fs.existsSync(npxBase)) {
94
+ try {
95
+ // Only migrate stories from npx caches — db/sessions are singletons and
96
+ // picking from a random cache entry could restore stale state
97
+ for (const hash of fs.readdirSync(npxBase)) {
98
+ const pkgRoot = path.join(npxBase, hash, "node_modules", "plotlink-ows");
99
+ migrateStoriesFrom(path.join(pkgRoot, "stories"), `npx cache (${hash.slice(0, 8)})`);
100
+ }
101
+ } catch { /* npx cache scan best-effort */ }
102
+ }
103
+
104
+ // 2. Current package-relative path (dev → npx transition)
105
+ const oldStoriesDir = path.join(__dirname, "..", "stories");
106
+ const oldDataDir = path.join(__dirname, "..", "data");
107
+ migrateStoriesFrom(oldStoriesDir, "package directory");
108
+ migrateFileFrom(path.join(oldDataDir, "local.db"), path.join(DATA_DIR, "local.db"), "database");
109
+ migrateFileFrom(
110
+ path.join(oldDataDir, "terminal-sessions.json"),
111
+ path.join(DATA_DIR, "terminal-sessions.json"),
112
+ "terminal sessions",
113
+ );
114
+ }
115
+
60
116
  async function start() {
61
- // Ensure data directory exists
62
- const dataDir = path.join(__dirname, "..", "data");
63
- if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
117
+ // Auto-migrate from old package-relative paths
118
+ migrateOldData();
64
119
 
65
120
  // Run Prisma db push to ensure schema is up to date
66
121
  const schemaPath = path.join(__dirname, "prisma", "schema.prisma");
67
122
  execSync(`npx prisma db push --schema ${schemaPath} --skip-generate`, {
68
123
  stdio: "inherit",
69
- env: { ...process.env, DATABASE_URL: `file:${path.join(dataDir, "local.db")}` },
124
+ env: { ...process.env, DATABASE_URL },
70
125
  });
71
126
 
72
127
  // Initialize database connection
73
128
  await initDb();
74
129
 
75
- // Ensure stories directory exists
76
- const storiesDir = path.join(__dirname, "..", "stories");
77
- if (!fs.existsSync(storiesDir)) fs.mkdirSync(storiesDir, { recursive: true });
78
-
79
130
  const port = Number(process.env.APP_PORT) || 7777;
80
131
  const server = serve({ fetch: app.fetch, port }, (info) => {
81
132
  console.log(`\n PlotLink OWS running at http://localhost:${info.port}\n`);
@@ -36,7 +36,7 @@ const THEME = {
36
36
  blue: "#4A6FA5",
37
37
  magenta: "#7B4B8A",
38
38
  cyan: "#3D7A7A",
39
- white: "#3A2A1E",
39
+ white: "#E6DDD0", // subtle cream tint for input line backgrounds
40
40
  brightBlack: "#8B7355",
41
41
  brightRed: "#B85C5C", // muted red — readable as text, soft as diff bg
42
42
  brightGreen: "#5A8A5A", // muted green — readable as text, soft as diff bg
@@ -184,6 +184,8 @@ export function TerminalPanel({ token, storyName, authFetch, onSelectStory }: Te
184
184
  container.style.width = "100%";
185
185
  container.style.height = "100%";
186
186
  container.style.display = "none";
187
+ container.style.paddingLeft = "10px";
188
+ container.style.boxSizing = "border-box";
187
189
  wrapperRef.current.appendChild(container);
188
190
 
189
191
  const term = new Terminal({
@@ -207,11 +209,6 @@ export function TerminalPanel({ token, storyName, authFetch, onSelectStory }: Te
207
209
  term.loadAddon(serialize);
208
210
  term.open(container);
209
211
 
210
- // Apply padding to term.element so FitAddon measures correctly
211
- if (term.element) {
212
- term.element.style.paddingLeft = "10px";
213
- }
214
-
215
212
  const session: TerminalSession = { term, fit, serialize, ws: null, container, observer: null as unknown as ResizeObserver, connected: false };
216
213
 
217
214
  const observer = new ResizeObserver(() => {
@@ -1,6 +1,18 @@
1
1
  @import "tailwindcss";
2
2
  @plugin "@tailwindcss/typography";
3
3
 
4
+ @theme inline {
5
+ --color-background: #E8DFD0;
6
+ --color-foreground: #2C1810;
7
+ --color-surface: #F0EBE1;
8
+ --color-muted: #8B7355;
9
+ --color-accent: #8B4513;
10
+ --color-accent-dim: #6B3410;
11
+ --color-border: #D4C5B0;
12
+ --color-error: #CC3333;
13
+ --default-font-family: "Inter", system-ui, -apple-system, sans-serif;
14
+ }
15
+
4
16
  :root {
5
17
  --bg: #E8DFD0;
6
18
  --bg-surface: #F0EBE1;
@@ -14,18 +26,6 @@
14
26
  --paper-bg: #F5F0E8;
15
27
  }
16
28
 
17
- @theme inline {
18
- --color-background: var(--bg);
19
- --color-foreground: var(--text);
20
- --color-surface: var(--bg-surface);
21
- --color-muted: var(--text-muted);
22
- --color-accent: var(--accent);
23
- --color-accent-dim: var(--accent-dim);
24
- --color-border: var(--border);
25
- --color-error: var(--error);
26
- --default-font-family: "Inter", system-ui, -apple-system, sans-serif;
27
- }
28
-
29
29
  body {
30
30
  background: var(--bg);
31
31
  color: var(--text);
@@ -67,3 +67,15 @@ code, pre {
67
67
  opacity: 1 !important;
68
68
  color: #8B7355 !important;
69
69
  }
70
+
71
+ /* Remove left vertical line artifact from xterm container */
72
+ .xterm {
73
+ border: none !important;
74
+ outline: none !important;
75
+ }
76
+
77
+ .xterm-viewport {
78
+ border: none !important;
79
+ outline: none !important;
80
+ }
81
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plotlink-ows",
3
- "version": "1.0.4",
3
+ "version": "1.0.8",
4
4
  "bin": {
5
5
  "plotlink-ows": "./bin/plotlink-ows.js"
6
6
  },
@@ -10,10 +10,7 @@
10
10
  */
11
11
  import fs from "fs";
12
12
  import path from "path";
13
- import { fileURLToPath } from "url";
14
-
15
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
- const STORIES_DIR = path.join(__dirname, "..", "stories");
13
+ import { STORIES_DIR } from "../app/lib/paths";
17
14
 
18
15
  interface FileStatus {
19
16
  file: string;