plotlink-ows 1.0.5 → 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/server.ts +45 -33
- package/app/web/components/TerminalPanel.tsx +3 -6
- package/app/web/styles.css +24 -12
- package/package.json +1 -1
package/app/server.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import dotenv from "dotenv";
|
|
2
|
+
import os from "os";
|
|
2
3
|
import path from "path";
|
|
3
4
|
import { fileURLToPath } from "url";
|
|
4
5
|
import { ENV_FILE, DATA_DIR, STORIES_DIR, DATABASE_URL } from "./lib/paths";
|
|
@@ -60,45 +61,56 @@ if (fs.existsSync(distPath)) {
|
|
|
60
61
|
});
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
/**
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (!fs.existsSync(dest)) {
|
|
76
|
-
try {
|
|
77
|
-
fs.renameSync(src, dest);
|
|
78
|
-
} catch {
|
|
79
|
-
// EXDEV: cross-device move — fall back to copy
|
|
80
|
-
fs.cpSync(src, dest, { recursive: true });
|
|
81
|
-
}
|
|
82
|
-
console.log(` Migrated story "${entry.name}" → ${STORIES_DIR}`);
|
|
83
|
-
}
|
|
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 });
|
|
84
76
|
}
|
|
77
|
+
console.log(` Migrated story "${entry.name}" from ${label}`);
|
|
85
78
|
}
|
|
79
|
+
}
|
|
86
80
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
console.log(` Migrated database → ${DATA_DIR}`);
|
|
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)}`);
|
|
93
86
|
}
|
|
87
|
+
}
|
|
94
88
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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 */ }
|
|
101
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
|
+
);
|
|
102
114
|
}
|
|
103
115
|
|
|
104
116
|
async function start() {
|
|
@@ -36,7 +36,7 @@ const THEME = {
|
|
|
36
36
|
blue: "#4A6FA5",
|
|
37
37
|
magenta: "#7B4B8A",
|
|
38
38
|
cyan: "#3D7A7A",
|
|
39
|
-
white: "#
|
|
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(() => {
|
package/app/web/styles.css
CHANGED
|
@@ -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
|
+
|