rewritable 0.7.0 → 0.8.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.
- package/README.md +21 -1
- package/bin/rwa.mjs +47 -3
- package/package.json +1 -1
- package/seeds/rewritable.html +868 -44
- package/src/identity.mjs +1 -0
- package/src/seed.mjs +84 -1
- package/src/workspace.mjs +262 -0
package/src/identity.mjs
CHANGED
|
@@ -35,6 +35,7 @@ export const KIND_PROVIDERS = {
|
|
|
35
35
|
workflow: [],
|
|
36
36
|
// skill-host: no first-party affordances; installed skills (provenance:'installed')
|
|
37
37
|
// come from parseSkillZone (§8), not this table. Explicit [] mirrors the oracle.
|
|
38
|
+
workspace: [],
|
|
38
39
|
'skill-host': [],
|
|
39
40
|
};
|
|
40
41
|
|
package/src/seed.mjs
CHANGED
|
@@ -1316,6 +1316,82 @@ const KIND_PRESENTATION_BODY = `<article>
|
|
|
1316
1316
|
<p>The view is a pure re-presentation at render time; the bytes on disk never change shape.</p>
|
|
1317
1317
|
</article>`;
|
|
1318
1318
|
|
|
1319
|
+
// ── workspace (directory index / control center) ─────────────────────────────
|
|
1320
|
+
const KIND_WORKSPACE_LENS = 'Describe a change to this workspace index.';
|
|
1321
|
+
const KIND_WORKSPACE_PAL = 'edit this workspace index...';
|
|
1322
|
+
|
|
1323
|
+
const KIND_WORKSPACE_HEADER = `// === PRODUCT HEADER ===
|
|
1324
|
+
// Product: workspace (directory control center).
|
|
1325
|
+
//
|
|
1326
|
+
// A workspace index is a normal re-writeable file named rwa-index.html that
|
|
1327
|
+
// summarizes sibling rewritables in the same directory. The editable article is
|
|
1328
|
+
// a dashboard; the frozen #rwa-workspace JSON manifest is regenerated by the CLI
|
|
1329
|
+
// (\`rwa workspace create|sync\`) from the files on disk. The index coordinates a
|
|
1330
|
+
// folder; it does not merge documents, expand the skill-host runtime, or persist
|
|
1331
|
+
// runtime chrome into any child document.
|
|
1332
|
+
// === END PRODUCT HEADER ===`;
|
|
1333
|
+
|
|
1334
|
+
const KIND_WORKSPACE_BODY = `<!-- rwa:frozen:begin workspace-style -->
|
|
1335
|
+
<style>
|
|
1336
|
+
.rwa-workspace{max-width:1040px;margin:0 auto;padding:32px 24px 72px;}
|
|
1337
|
+
.rwa-workspace header{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;margin-bottom:24px;border-bottom:1px solid var(--gray-200,#e5e7eb);padding-bottom:18px;}
|
|
1338
|
+
.rwa-workspace h1{margin:0;font-size:2rem;line-height:1.1;}
|
|
1339
|
+
.rwa-workspace .rwa-ws-meta{margin:0;color:var(--gray-500,#6b7280);font-size:13px;}
|
|
1340
|
+
.rwa-ws-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;}
|
|
1341
|
+
.rwa-ws-card{display:flex;flex-direction:column;gap:7px;padding:14px 16px;border:1px solid var(--gray-200,#e5e7eb);border-radius:8px;text-decoration:none;color:inherit;background:var(--gray-50,#f9fafb);}
|
|
1342
|
+
.rwa-ws-card:hover{border-color:var(--gray-400,#9ca3af);background:#fff;}
|
|
1343
|
+
.rwa-ws-card strong{font-size:16px;line-height:1.25;}
|
|
1344
|
+
.rwa-ws-card span{font-size:13px;color:var(--gray-600,#4b5563);overflow-wrap:anywhere;}
|
|
1345
|
+
.rwa-ws-card small{font-size:12px;color:var(--gray-500,#6b7280);}
|
|
1346
|
+
.rwa-ws-kind{align-self:flex-start;text-transform:uppercase;letter-spacing:.04em;font-size:10px!important;color:#fff!important;background:var(--gray-800,#1f2937);border-radius:4px;padding:2px 6px;}
|
|
1347
|
+
.rwa-ws-empty{color:var(--gray-500,#6b7280);line-height:1.5;}
|
|
1348
|
+
.rwa-ws-context{display:grid;grid-template-columns:minmax(0,1fr);gap:10px;margin:20px 0 28px;}
|
|
1349
|
+
.rwa-ws-context h2{margin:18px 0 0;font-size:1.1rem;}
|
|
1350
|
+
.rwa-ws-context h2:first-child{margin-top:0;}
|
|
1351
|
+
.rwa-ws-context p,.rwa-ws-context ul,.rwa-ws-context ol{margin:0;line-height:1.55;}
|
|
1352
|
+
.rwa-ws-live{margin-top:28px;padding-top:18px;border-top:1px solid var(--gray-200,#e5e7eb);}
|
|
1353
|
+
.rwa-ws-live h2{margin:0 0 12px;font-size:1rem;}
|
|
1354
|
+
.rwa-ws-live-card{background:#fff;border-color:var(--blue,#2563eb);}
|
|
1355
|
+
</style>
|
|
1356
|
+
<!-- rwa:frozen:end workspace-style -->
|
|
1357
|
+
<article class="rwa-workspace">
|
|
1358
|
+
<header>
|
|
1359
|
+
<div>
|
|
1360
|
+
<h1>Workspace</h1>
|
|
1361
|
+
<p class="rwa-ws-meta">0 documents · run <code>rwa workspace sync</code> in this folder</p>
|
|
1362
|
+
</div>
|
|
1363
|
+
</header>
|
|
1364
|
+
<section class="rwa-ws-context" data-rwa-workspace-context>
|
|
1365
|
+
<h2>Workspace memory</h2>
|
|
1366
|
+
<p>Use this space for durable notes that every document in this workspace should be able to rely on.</p>
|
|
1367
|
+
|
|
1368
|
+
<h2>Guidelines</h2>
|
|
1369
|
+
<ul>
|
|
1370
|
+
<li>Describe the shared tone, standards, constraints, and recurring decisions for this workspace.</li>
|
|
1371
|
+
<li>For a writing workspace, add voice, structure, audience, and publishing rules here.</li>
|
|
1372
|
+
</ul>
|
|
1373
|
+
|
|
1374
|
+
<h2>Examples</h2>
|
|
1375
|
+
<p>Add canonical examples that new documents can imitate, such as a representative blog post, proposal, report, or brief.</p>
|
|
1376
|
+
|
|
1377
|
+
<h2>Open questions</h2>
|
|
1378
|
+
<ul>
|
|
1379
|
+
<li>Track unresolved decisions that should shape future documents.</li>
|
|
1380
|
+
</ul>
|
|
1381
|
+
</section>
|
|
1382
|
+
<h2>Workspace documents</h2>
|
|
1383
|
+
<section class="rwa-ws-grid" aria-label="Workspace documents">
|
|
1384
|
+
<p class="rwa-ws-empty">No sibling rewritables yet. Add documents to this folder, then run <code>rwa workspace sync</code>.</p>
|
|
1385
|
+
</section>
|
|
1386
|
+
<section class="rwa-ws-live" data-rwa-workspace-live hidden>
|
|
1387
|
+
<h2>Open now</h2>
|
|
1388
|
+
<div class="rwa-ws-grid" data-rwa-workspace-live-grid></div>
|
|
1389
|
+
</section>
|
|
1390
|
+
</article>
|
|
1391
|
+
<!-- rwa:frozen:begin workspace-manifest -->
|
|
1392
|
+
<script type="application/rwa-workspace+json" id="rwa-workspace" data-rwa-frozen>{"version":"rwa-workspace/1","name":"Workspace","documents":[]}</script>
|
|
1393
|
+
<!-- rwa:frozen:end workspace-manifest -->`;
|
|
1394
|
+
|
|
1319
1395
|
// ── skill-host (v0.8 actions spec §2) ──────────────────────────────────────
|
|
1320
1396
|
const KIND_SKILLHOST_LENS = 'Describe a change to this skill host.';
|
|
1321
1397
|
const KIND_SKILLHOST_PAL = 'edit this skill host...';
|
|
@@ -1368,6 +1444,13 @@ const KIND_TABLE = {
|
|
|
1368
1444
|
// (spec §5.10); this kind only sets PRODUCT_KIND + starter/framing/lens.
|
|
1369
1445
|
lensClickToAnchor: false,
|
|
1370
1446
|
},
|
|
1447
|
+
workspace: {
|
|
1448
|
+
body: KIND_WORKSPACE_BODY,
|
|
1449
|
+
lensPlaceholder: KIND_WORKSPACE_LENS,
|
|
1450
|
+
palPlaceholder: KIND_WORKSPACE_PAL,
|
|
1451
|
+
productHeader: KIND_WORKSPACE_HEADER,
|
|
1452
|
+
lensClickToAnchor: false,
|
|
1453
|
+
},
|
|
1371
1454
|
'skill-host': {
|
|
1372
1455
|
body: KIND_SKILLHOST_BODY,
|
|
1373
1456
|
lensPlaceholder: KIND_SKILLHOST_LENS,
|
|
@@ -1377,7 +1460,7 @@ const KIND_TABLE = {
|
|
|
1377
1460
|
// in the runtime-owned frozen zone, not authored by clicking a paragraph.
|
|
1378
1461
|
lensClickToAnchor: false,
|
|
1379
1462
|
},
|
|
1380
|
-
// app
|
|
1463
|
+
// app: reserved — wire when the template lands. The CLI rejects
|
|
1381
1464
|
// unknown kinds explicitly rather than silently emitting a document.
|
|
1382
1465
|
};
|
|
1383
1466
|
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
|
|
5
|
+
import { loadSeed, applySeedSubs, replaceInlineDoc, extractInlineDoc, kindOverrides } from './seed.mjs';
|
|
6
|
+
import { inspectDoc } from './doc.mjs';
|
|
7
|
+
|
|
8
|
+
export const WORKSPACE_INDEX = 'rwa-index.html';
|
|
9
|
+
const UUID_RE = /const DOC_UUID = '([0-9a-f-]{36})';/;
|
|
10
|
+
|
|
11
|
+
function titleFromDir(dir) {
|
|
12
|
+
const base = path.basename(path.resolve(dir)) || 'Workspace';
|
|
13
|
+
return base
|
|
14
|
+
.replace(/[-_]+/g, ' ')
|
|
15
|
+
.split(' ')
|
|
16
|
+
.filter(Boolean)
|
|
17
|
+
.map(w => w[0].toUpperCase() + w.slice(1))
|
|
18
|
+
.join(' ') || 'Workspace';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function titleFromFile(file) {
|
|
22
|
+
return path.basename(file, path.extname(file))
|
|
23
|
+
.replace(/[-_]+/g, ' ')
|
|
24
|
+
.split(' ')
|
|
25
|
+
.filter(Boolean)
|
|
26
|
+
.map(w => w[0].toUpperCase() + w.slice(1))
|
|
27
|
+
.join(' ') || file;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function escapeHtml(s) {
|
|
31
|
+
return String(s == null ? '' : s)
|
|
32
|
+
.replace(/&/g, '&')
|
|
33
|
+
.replace(/</g, '<')
|
|
34
|
+
.replace(/>/g, '>')
|
|
35
|
+
.replace(/"/g, '"');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function escapeAttr(s) {
|
|
39
|
+
return escapeHtml(s).replace(/'/g, ''');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function safeScriptJson(obj) {
|
|
43
|
+
return JSON.stringify(obj, null, 2).replace(/<\/script/gi, '<\\/script');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function defaultWorkspaceContext(name) {
|
|
47
|
+
return `<section class="rwa-ws-context" data-rwa-workspace-context>
|
|
48
|
+
<h2>Workspace memory</h2>
|
|
49
|
+
<p>Use this space for durable notes that every document in this workspace should be able to rely on.</p>
|
|
50
|
+
|
|
51
|
+
<h2>Guidelines</h2>
|
|
52
|
+
<ul>
|
|
53
|
+
<li>Describe the shared tone, standards, constraints, and recurring decisions for this workspace.</li>
|
|
54
|
+
<li>For a writing workspace, add voice, structure, audience, and publishing rules here.</li>
|
|
55
|
+
</ul>
|
|
56
|
+
|
|
57
|
+
<h2>Examples</h2>
|
|
58
|
+
<p>Add canonical examples that new documents can imitate, such as a representative blog post, proposal, report, or brief.</p>
|
|
59
|
+
|
|
60
|
+
<h2>Open questions</h2>
|
|
61
|
+
<ul>
|
|
62
|
+
<li>Track unresolved decisions that should shape future documents.</li>
|
|
63
|
+
</ul>
|
|
64
|
+
</section>`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function extractWorkspaceContext(doc, name) {
|
|
68
|
+
const m = String(doc || '').match(/<section\b[^>]*\bdata-rwa-workspace-context\b[^>]*>[\s\S]*?<\/section>/i);
|
|
69
|
+
return m ? m[0] : defaultWorkspaceContext(name);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function indexUuid(indexPath) {
|
|
73
|
+
try {
|
|
74
|
+
const text = await fs.readFile(indexPath, 'utf8');
|
|
75
|
+
return (text.match(UUID_RE) || [])[1] || crypto.randomUUID();
|
|
76
|
+
} catch (e) {
|
|
77
|
+
if (e && e.code === 'ENOENT') return crypto.randomUUID();
|
|
78
|
+
throw e;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function ensureCreateTarget(dir, indexPath, force) {
|
|
83
|
+
try {
|
|
84
|
+
const st = await fs.stat(dir);
|
|
85
|
+
if (!st.isDirectory()) {
|
|
86
|
+
const e = new Error(`workspace target is not a directory: ${dir}`);
|
|
87
|
+
e.exitCode = 2;
|
|
88
|
+
throw e;
|
|
89
|
+
}
|
|
90
|
+
} catch (e) {
|
|
91
|
+
if (e && e.code === 'ENOENT') {
|
|
92
|
+
await fs.mkdir(dir, { recursive: true });
|
|
93
|
+
} else {
|
|
94
|
+
throw e;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
await fs.stat(indexPath);
|
|
100
|
+
if (!force) {
|
|
101
|
+
const e = new Error(`workspace index exists: ${indexPath} (use --force to overwrite)`);
|
|
102
|
+
e.exitCode = 2;
|
|
103
|
+
throw e;
|
|
104
|
+
}
|
|
105
|
+
} catch (e) {
|
|
106
|
+
if (e && e.code === 'ENOENT') return;
|
|
107
|
+
throw e;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function ensureSyncTarget(dir) {
|
|
112
|
+
const st = await fs.stat(dir).catch(e => {
|
|
113
|
+
if (e && e.code === 'ENOENT') {
|
|
114
|
+
const err = new Error(`workspace directory not found: ${dir}`);
|
|
115
|
+
err.exitCode = 2;
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
throw e;
|
|
119
|
+
});
|
|
120
|
+
if (!st.isDirectory()) {
|
|
121
|
+
const e = new Error(`workspace target is not a directory: ${dir}`);
|
|
122
|
+
e.exitCode = 2;
|
|
123
|
+
throw e;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function scanWorkspace(dir) {
|
|
128
|
+
const names = (await fs.readdir(dir))
|
|
129
|
+
.filter(n => /\.html?$/i.test(n))
|
|
130
|
+
.filter(n => n !== WORKSPACE_INDEX)
|
|
131
|
+
.sort((a, b) => a.localeCompare(b));
|
|
132
|
+
const docs = [];
|
|
133
|
+
for (const name of names) {
|
|
134
|
+
const filePath = path.join(dir, name);
|
|
135
|
+
try {
|
|
136
|
+
const info = await inspectDoc(filePath);
|
|
137
|
+
docs.push({
|
|
138
|
+
file: name,
|
|
139
|
+
title: info.self.title || titleFromFile(name),
|
|
140
|
+
kind: info.self.kind || info.kind || 'document',
|
|
141
|
+
uuid: info.uuid,
|
|
142
|
+
blocks: info.self.blocks || 0,
|
|
143
|
+
affordances: Array.isArray(info.self.affordances)
|
|
144
|
+
? info.self.affordances.map(a => ({ kind: a.kind, name: a.name, label: a.label, provenance: a.provenance }))
|
|
145
|
+
: [],
|
|
146
|
+
});
|
|
147
|
+
} catch (e) {
|
|
148
|
+
if (e && e.subcode === 'not_a_rewritable') continue;
|
|
149
|
+
throw e;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return docs;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function buildWorkspaceBody({ name, docs, generatedAt = new Date().toISOString(), contextHtml }) {
|
|
156
|
+
const manifest = {
|
|
157
|
+
version: 'rwa-workspace/1',
|
|
158
|
+
name,
|
|
159
|
+
generatedAt,
|
|
160
|
+
index: WORKSPACE_INDEX,
|
|
161
|
+
documents: docs,
|
|
162
|
+
};
|
|
163
|
+
const cards = docs.length
|
|
164
|
+
? docs.map(doc => {
|
|
165
|
+
const kinds = doc.affordances && doc.affordances.length
|
|
166
|
+
? doc.affordances.map(a => a.kind).join(', ')
|
|
167
|
+
: 'baseline';
|
|
168
|
+
const href = './' + encodeURI(doc.file).replace(/"/g, '%22');
|
|
169
|
+
return `<a class="rwa-ws-card" href="${escapeAttr(href)}">
|
|
170
|
+
<span class="rwa-ws-kind">${escapeHtml(doc.kind)}</span>
|
|
171
|
+
<strong>${escapeHtml(doc.title)}</strong>
|
|
172
|
+
<span>${escapeHtml(doc.file)}</span>
|
|
173
|
+
<small>${escapeHtml(String(doc.blocks))} blocks · ${escapeHtml(kinds)}</small>
|
|
174
|
+
</a>`;
|
|
175
|
+
}).join('\n')
|
|
176
|
+
: '<p class="rwa-ws-empty">No sibling rewritables yet. Add documents to this folder, then run <code>rwa workspace sync</code>.</p>';
|
|
177
|
+
|
|
178
|
+
return `<!-- rwa:frozen:begin workspace-style -->
|
|
179
|
+
<style>
|
|
180
|
+
.rwa-workspace{max-width:1040px;margin:0 auto;padding:32px 24px 72px;}
|
|
181
|
+
.rwa-workspace header{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;margin-bottom:24px;border-bottom:1px solid var(--gray-200,#e5e7eb);padding-bottom:18px;}
|
|
182
|
+
.rwa-workspace h1{margin:0;font-size:2rem;line-height:1.1;}
|
|
183
|
+
.rwa-workspace .rwa-ws-meta{margin:0;color:var(--gray-500,#6b7280);font-size:13px;}
|
|
184
|
+
.rwa-ws-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;}
|
|
185
|
+
.rwa-ws-card{display:flex;flex-direction:column;gap:7px;padding:14px 16px;border:1px solid var(--gray-200,#e5e7eb);border-radius:8px;text-decoration:none;color:inherit;background:var(--gray-50,#f9fafb);}
|
|
186
|
+
.rwa-ws-card:hover{border-color:var(--gray-400,#9ca3af);background:#fff;}
|
|
187
|
+
.rwa-ws-card strong{font-size:16px;line-height:1.25;}
|
|
188
|
+
.rwa-ws-card span{font-size:13px;color:var(--gray-600,#4b5563);overflow-wrap:anywhere;}
|
|
189
|
+
.rwa-ws-card small{font-size:12px;color:var(--gray-500,#6b7280);}
|
|
190
|
+
.rwa-ws-kind{align-self:flex-start;text-transform:uppercase;letter-spacing:.04em;font-size:10px!important;color:#fff!important;background:var(--gray-800,#1f2937);border-radius:4px;padding:2px 6px;}
|
|
191
|
+
.rwa-ws-empty{color:var(--gray-500,#6b7280);line-height:1.5;}
|
|
192
|
+
.rwa-ws-context{display:grid;grid-template-columns:minmax(0,1fr);gap:10px;margin:20px 0 28px;}
|
|
193
|
+
.rwa-ws-context h2{margin:18px 0 0;font-size:1.1rem;}
|
|
194
|
+
.rwa-ws-context h2:first-child{margin-top:0;}
|
|
195
|
+
.rwa-ws-context p,.rwa-ws-context ul,.rwa-ws-context ol{margin:0;line-height:1.55;}
|
|
196
|
+
.rwa-ws-live{margin-top:28px;padding-top:18px;border-top:1px solid var(--gray-200,#e5e7eb);}
|
|
197
|
+
.rwa-ws-live h2{margin:0 0 12px;font-size:1rem;}
|
|
198
|
+
.rwa-ws-live-card{background:#fff;border-color:var(--blue,#2563eb);}
|
|
199
|
+
</style>
|
|
200
|
+
<!-- rwa:frozen:end workspace-style -->
|
|
201
|
+
<article class="rwa-workspace">
|
|
202
|
+
<header>
|
|
203
|
+
<div>
|
|
204
|
+
<h1>${escapeHtml(name)}</h1>
|
|
205
|
+
<p class="rwa-ws-meta">${docs.length} document${docs.length === 1 ? '' : 's'} · synced ${escapeHtml(generatedAt)}</p>
|
|
206
|
+
</div>
|
|
207
|
+
</header>
|
|
208
|
+
${contextHtml || defaultWorkspaceContext(name)}
|
|
209
|
+
<h2>Workspace documents</h2>
|
|
210
|
+
<section class="rwa-ws-grid" aria-label="Workspace documents">
|
|
211
|
+
${cards}
|
|
212
|
+
</section>
|
|
213
|
+
<section class="rwa-ws-live" data-rwa-workspace-live hidden>
|
|
214
|
+
<h2>Open now</h2>
|
|
215
|
+
<div class="rwa-ws-grid" data-rwa-workspace-live-grid></div>
|
|
216
|
+
</section>
|
|
217
|
+
</article>
|
|
218
|
+
<!-- rwa:frozen:begin workspace-manifest -->
|
|
219
|
+
<script type="application/rwa-workspace+json" id="rwa-workspace" data-rwa-frozen>${safeScriptJson(manifest)}</script>
|
|
220
|
+
<!-- rwa:frozen:end workspace-manifest -->`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function writeWorkspaceIndex(dir, { seedCandidates, uuid }) {
|
|
224
|
+
const indexPath = path.join(dir, WORKSPACE_INDEX);
|
|
225
|
+
const docs = await scanWorkspace(dir);
|
|
226
|
+
const name = titleFromDir(dir);
|
|
227
|
+
let contextHtml = defaultWorkspaceContext(name);
|
|
228
|
+
try {
|
|
229
|
+
const existing = await fs.readFile(indexPath, 'utf8');
|
|
230
|
+
contextHtml = extractWorkspaceContext(extractInlineDoc(existing), name);
|
|
231
|
+
} catch (e) {
|
|
232
|
+
if (!(e && e.code === 'ENOENT')) contextHtml = defaultWorkspaceContext(name);
|
|
233
|
+
}
|
|
234
|
+
const overrides = kindOverrides('workspace');
|
|
235
|
+
let html = applySeedSubs(await loadSeed(seedCandidates), {
|
|
236
|
+
uuid,
|
|
237
|
+
title: name,
|
|
238
|
+
fileMeta: WORKSPACE_INDEX,
|
|
239
|
+
lensPlaceholder: overrides.lensPlaceholder,
|
|
240
|
+
palPlaceholder: overrides.palPlaceholder,
|
|
241
|
+
productHeader: overrides.productHeader,
|
|
242
|
+
productKind: 'workspace',
|
|
243
|
+
lensClickToAnchor: overrides.lensClickToAnchor,
|
|
244
|
+
});
|
|
245
|
+
html = replaceInlineDoc(html, buildWorkspaceBody({ name, docs, contextHtml }));
|
|
246
|
+
await fs.writeFile(indexPath, html, 'utf8');
|
|
247
|
+
return { indexPath, docs };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export async function workspaceCreateCmd({ dirPath = '.', force = false, seedCandidates }) {
|
|
251
|
+
const dir = path.resolve(dirPath);
|
|
252
|
+
const indexPath = path.join(dir, WORKSPACE_INDEX);
|
|
253
|
+
await ensureCreateTarget(dir, indexPath, force);
|
|
254
|
+
return writeWorkspaceIndex(dir, { seedCandidates, uuid: await indexUuid(indexPath) });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export async function workspaceSyncCmd({ dirPath = '.', seedCandidates }) {
|
|
258
|
+
const dir = path.resolve(dirPath);
|
|
259
|
+
await ensureSyncTarget(dir);
|
|
260
|
+
const indexPath = path.join(dir, WORKSPACE_INDEX);
|
|
261
|
+
return writeWorkspaceIndex(dir, { seedCandidates, uuid: await indexUuid(indexPath) });
|
|
262
|
+
}
|