@higgsfield/video-editor-mcp 1.0.5 → 1.0.7
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/dist/index.html +158 -144
- package/dist/server.mjs +69 -24
- package/node_modules/@higgsfield/higgsedit/dist/chunk-C6SASQHE.mjs +10 -76
- package/node_modules/@higgsfield/higgsedit/dist/chunk-QLPCYN6K.mjs +22 -5
- package/node_modules/@higgsfield/higgsedit/dist/chunk-VI6IV6JP.mjs +35 -19
- package/node_modules/@higgsfield/higgsedit/dist/higgsedit.mjs +14 -24
- package/node_modules/@higgsfield/higgsedit/dist/history.mjs +295 -0
- package/package.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/plugin.json +1 -1
package/dist/server.mjs
CHANGED
|
@@ -41605,7 +41605,7 @@ function registerHostedProxyTools(server, url2, session = new LocalBrowserSessio
|
|
|
41605
41605
|
const previous = connected?.client;
|
|
41606
41606
|
const next = new Client({
|
|
41607
41607
|
name: "higgsfield-video-editor-local",
|
|
41608
|
-
version: "1.0.
|
|
41608
|
+
version: "1.0.7"
|
|
41609
41609
|
});
|
|
41610
41610
|
const transport = new StreamableHTTPClientTransport(new URL(url2), {
|
|
41611
41611
|
requestInit: { headers: { Authorization: `Bearer ${token}` } }
|
|
@@ -41875,12 +41875,15 @@ function parseCliJson(result) {
|
|
|
41875
41875
|
throw new Error(
|
|
41876
41876
|
(result.stderr || result.stdout).trim().slice(-4e3) || `higgsedit exited ${result.code}`
|
|
41877
41877
|
);
|
|
41878
|
-
|
|
41878
|
+
let value;
|
|
41879
41879
|
try {
|
|
41880
|
-
|
|
41880
|
+
value = JSON.parse(result.stdout);
|
|
41881
41881
|
} catch {
|
|
41882
|
-
|
|
41882
|
+
throw new Error("higgsedit returned invalid or incomplete JSON.");
|
|
41883
41883
|
}
|
|
41884
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
41885
|
+
throw new Error("higgsedit returned an unexpected JSON response; expected an object.");
|
|
41886
|
+
return value;
|
|
41884
41887
|
}
|
|
41885
41888
|
var cliJson = async (args, cwd, timeoutMs) => parseCliJson(await runCli(args, cwd, timeoutMs));
|
|
41886
41889
|
|
|
@@ -41893,8 +41896,8 @@ var directCspOrigins = () => cspOrigins;
|
|
|
41893
41896
|
var CORS = {
|
|
41894
41897
|
"access-control-allow-origin": "*",
|
|
41895
41898
|
"access-control-allow-methods": "GET, HEAD, POST, PUT, DELETE, OPTIONS",
|
|
41896
|
-
"access-control-allow-headers": "range, content-type",
|
|
41897
|
-
"access-control-expose-headers": "content-range, content-length, accept-ranges, content-type",
|
|
41899
|
+
"access-control-allow-headers": "range, content-type, if-none-match, if-modified-since, if-range",
|
|
41900
|
+
"access-control-expose-headers": "content-range, content-length, accept-ranges, content-type, etag, last-modified, cache-control",
|
|
41898
41901
|
// An https page reaching loopback (Private Network Access / Local Network Access preflights).
|
|
41899
41902
|
"access-control-allow-private-network": "true",
|
|
41900
41903
|
"cross-origin-resource-policy": "cross-origin",
|
|
@@ -41906,7 +41909,8 @@ var FORWARDED = [
|
|
|
41906
41909
|
"content-length",
|
|
41907
41910
|
"accept",
|
|
41908
41911
|
"if-none-match",
|
|
41909
|
-
"if-modified-since"
|
|
41912
|
+
"if-modified-since",
|
|
41913
|
+
"if-range"
|
|
41910
41914
|
];
|
|
41911
41915
|
var DirectServer = class {
|
|
41912
41916
|
constructor(origin, ports = DIRECT_PORTS) {
|
|
@@ -42245,7 +42249,7 @@ var restyleStyleSchema = external_exports.object({
|
|
|
42245
42249
|
});
|
|
42246
42250
|
|
|
42247
42251
|
// src/tools/video-editor-local/standalone-resource.ts
|
|
42248
|
-
var packageVersion = "1.0.
|
|
42252
|
+
var packageVersion = "1.0.7";
|
|
42249
42253
|
var VIDEO_EDITOR_RESOURCE_URI = packageVersion ? `ui://higgsfield/video-editor-v3-${encodeURIComponent(packageVersion)}.html` : "ui://higgsfield/video-editor-v3.html";
|
|
42250
42254
|
function panelHtmlPath(env = process.env) {
|
|
42251
42255
|
return env.VIDEO_EDITOR_LOCAL_HTML?.trim() || join5(import.meta.dirname, "index.html");
|
|
@@ -42296,10 +42300,48 @@ import { mkdtemp, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
|
|
|
42296
42300
|
import { tmpdir } from "node:os";
|
|
42297
42301
|
import { join as join6 } from "node:path";
|
|
42298
42302
|
var MAX_CHUNK = 2 << 20;
|
|
42299
|
-
var KEPT_HEADERS = [
|
|
42303
|
+
var KEPT_HEADERS = [
|
|
42304
|
+
"content-type",
|
|
42305
|
+
"content-range",
|
|
42306
|
+
"content-length",
|
|
42307
|
+
"accept-ranges",
|
|
42308
|
+
"etag",
|
|
42309
|
+
"last-modified",
|
|
42310
|
+
"cache-control"
|
|
42311
|
+
];
|
|
42312
|
+
var CONDITIONAL_HEADERS = ["if-none-match", "if-modified-since", "if-range"];
|
|
42313
|
+
async function readReply(res, method) {
|
|
42314
|
+
if (method === "HEAD" || res.status === 304 || res.status === 204) return Buffer.alloc(0);
|
|
42315
|
+
if (Number(res.headers.get("content-length")) > MAX_CHUNK) {
|
|
42316
|
+
await res.body?.cancel();
|
|
42317
|
+
throw new Error("response exceeds the tool chunk limit; ask for a range");
|
|
42318
|
+
}
|
|
42319
|
+
const reader = res.body?.getReader();
|
|
42320
|
+
if (!reader) return Buffer.alloc(0);
|
|
42321
|
+
const chunks = [];
|
|
42322
|
+
let size = 0;
|
|
42323
|
+
try {
|
|
42324
|
+
for (; ; ) {
|
|
42325
|
+
const { done, value } = await reader.read();
|
|
42326
|
+
if (done) return Buffer.concat(chunks, size);
|
|
42327
|
+
size += value.byteLength;
|
|
42328
|
+
if (size > MAX_CHUNK) {
|
|
42329
|
+
await reader.cancel();
|
|
42330
|
+
throw new Error("response exceeds the tool chunk limit; ask for a range");
|
|
42331
|
+
}
|
|
42332
|
+
chunks.push(value);
|
|
42333
|
+
}
|
|
42334
|
+
} finally {
|
|
42335
|
+
reader.releaseLock();
|
|
42336
|
+
}
|
|
42337
|
+
}
|
|
42300
42338
|
async function httpTool(origin, call) {
|
|
42301
42339
|
checkPath(call.path);
|
|
42302
42340
|
const headers = new Headers();
|
|
42341
|
+
for (const name of CONDITIONAL_HEADERS) {
|
|
42342
|
+
const value = call.headers?.[name];
|
|
42343
|
+
if (value !== void 0) headers.set(name, value);
|
|
42344
|
+
}
|
|
42303
42345
|
if (call.rangeStart !== void 0) {
|
|
42304
42346
|
const end = Math.min(call.rangeEnd ?? Infinity, call.rangeStart + MAX_CHUNK - 1);
|
|
42305
42347
|
headers.set("range", `bytes=${call.rangeStart}-${end}`);
|
|
@@ -42322,9 +42364,7 @@ async function send(origin, method, path2, headers, body, signal) {
|
|
|
42322
42364
|
await res.body?.cancel();
|
|
42323
42365
|
throw new Error(STREAMING_IMPORT_REQUIRED);
|
|
42324
42366
|
}
|
|
42325
|
-
const bytes =
|
|
42326
|
-
if (bytes.length > MAX_CHUNK && res.status !== 206)
|
|
42327
|
-
throw new Error(`${path2}: ${bytes.length} bytes without a range; ask for a range`);
|
|
42367
|
+
const bytes = await readReply(res, method);
|
|
42328
42368
|
const kept = {};
|
|
42329
42369
|
for (const name of KEPT_HEADERS) {
|
|
42330
42370
|
const value = res.headers.get(name);
|
|
@@ -42445,7 +42485,6 @@ var reload = (undo) => ({
|
|
|
42445
42485
|
detectedAt: Date.now(),
|
|
42446
42486
|
undo
|
|
42447
42487
|
});
|
|
42448
|
-
var NO_UNDO = { canUndo: false, canRedo: false, undoName: null, redoName: null };
|
|
42449
42488
|
var EventFeed = class {
|
|
42450
42489
|
constructor(origin) {
|
|
42451
42490
|
this.origin = origin;
|
|
@@ -42494,14 +42533,14 @@ var EventFeed = class {
|
|
|
42494
42533
|
holds(after) {
|
|
42495
42534
|
return after >= (this.log[0]?.seq ?? this.seq + 1) - 1 && after <= this.seq;
|
|
42496
42535
|
}
|
|
42497
|
-
/** Events after `after`;
|
|
42536
|
+
/** Events after `after`; a new reader gets one full resync with current undo, not historical edits. */
|
|
42498
42537
|
async next(after, waitMs) {
|
|
42499
|
-
if (after
|
|
42500
|
-
const
|
|
42501
|
-
|
|
42502
|
-
|
|
42503
|
-
);
|
|
42504
|
-
return { seq
|
|
42538
|
+
if (after < 0 || !this.holds(after)) {
|
|
42539
|
+
const seq = this.seq;
|
|
42540
|
+
const response = await fetch(`${this.origin}/api/history`, { signal: this.abort.signal });
|
|
42541
|
+
if (!response.ok) throw new Error(`history: ${response.status}`);
|
|
42542
|
+
const undo = await response.json();
|
|
42543
|
+
return { seq, events: [reload(undo)] };
|
|
42505
42544
|
}
|
|
42506
42545
|
if (after >= 0 && after >= this.seq) {
|
|
42507
42546
|
await new Promise((resolve3) => {
|
|
@@ -43075,7 +43114,7 @@ async function initProject(path2, roots, size = "1080x1920", fps = 30) {
|
|
|
43075
43114
|
if (await hasProjectFile(dir)) return dir;
|
|
43076
43115
|
cliPath();
|
|
43077
43116
|
await mkdir5(dir, { recursive: true });
|
|
43078
|
-
await cliJson(["init", dir, `--size=${size}`, `--fps=${fps}
|
|
43117
|
+
await cliJson(["init", dir, `--size=${size}`, `--fps=${fps}`, "--json"], dir);
|
|
43079
43118
|
return dir;
|
|
43080
43119
|
}
|
|
43081
43120
|
async function newFile(dir, name, place) {
|
|
@@ -43288,7 +43327,7 @@ ${LOCAL_PROJECT_GUIDE}`);
|
|
|
43288
43327
|
annotations: { readOnlyHint: true, openWorldHint: false }
|
|
43289
43328
|
},
|
|
43290
43329
|
async ({ project }) => scoped(project, async (session) => {
|
|
43291
|
-
const outline = await cliJson(["outline", ".", "--json"], session.root);
|
|
43330
|
+
const outline = external_exports.looseObject({ text: external_exports.string().optional() }).parse(await cliJson(["outline", ".", "--json"], session.root));
|
|
43292
43331
|
const { text, ...rest } = outline;
|
|
43293
43332
|
return reply({ ...describe3(session), ...rest }, text ?? JSON.stringify(rest));
|
|
43294
43333
|
})
|
|
@@ -43444,6 +43483,7 @@ ${LOCAL_PROJECT_GUIDE}`);
|
|
|
43444
43483
|
[
|
|
43445
43484
|
"sheet",
|
|
43446
43485
|
".",
|
|
43486
|
+
"--json",
|
|
43447
43487
|
`--times=${times.join(",")}`,
|
|
43448
43488
|
"-o",
|
|
43449
43489
|
out,
|
|
@@ -43792,6 +43832,11 @@ function registerPanelRelay(server, store, stateDirectory, direct) {
|
|
|
43792
43832
|
path: external_exports.string().min(1).max(4096),
|
|
43793
43833
|
rangeStart: external_exports.number().int().nonnegative().optional(),
|
|
43794
43834
|
rangeEnd: external_exports.number().int().nonnegative().optional(),
|
|
43835
|
+
headers: external_exports.object({
|
|
43836
|
+
"if-none-match": external_exports.string().max(8192).optional(),
|
|
43837
|
+
"if-modified-since": external_exports.string().max(128).optional(),
|
|
43838
|
+
"if-range": external_exports.string().max(128).optional()
|
|
43839
|
+
}).optional(),
|
|
43795
43840
|
body: external_exports.string().max(64 * 1024 * 1024).optional(),
|
|
43796
43841
|
project: sessionArg
|
|
43797
43842
|
},
|
|
@@ -44585,7 +44630,7 @@ function createEditorUpdates(options, start = (done) => new Promise((resolve3, r
|
|
|
44585
44630
|
}
|
|
44586
44631
|
}
|
|
44587
44632
|
return {
|
|
44588
|
-
runningVersion: options?.version ?? "1.0.
|
|
44633
|
+
runningVersion: options?.version ?? "1.0.7" ?? "Source checkout",
|
|
44589
44634
|
readyVersion: options && selectedDist(options) !== options.dist ? state.active : null,
|
|
44590
44635
|
channel: options?.channel ?? null,
|
|
44591
44636
|
supported: options !== null,
|
|
@@ -44649,7 +44694,7 @@ var handle = serveStdio(
|
|
|
44649
44694
|
const server = new McpServer(
|
|
44650
44695
|
{
|
|
44651
44696
|
name: "Higgsfield Video Editor (local)",
|
|
44652
|
-
version: "1.0.
|
|
44697
|
+
version: "1.0.7"
|
|
44653
44698
|
},
|
|
44654
44699
|
{ instructions: VIDEO_EDITOR_LOCAL_INSTRUCTIONS }
|
|
44655
44700
|
);
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from "./chunk-QLPCYN6K.mjs";
|
|
18
18
|
import {
|
|
19
19
|
analyze,
|
|
20
|
-
|
|
20
|
+
applyEdits
|
|
21
21
|
} from "./chunk-VI6IV6JP.mjs";
|
|
22
22
|
import {
|
|
23
23
|
inside,
|
|
@@ -94,73 +94,7 @@ var WIDGET_DIST = join(EDITOR, "dist-mcp");
|
|
|
94
94
|
var WIDGET = join(WIDGET_DIST, "mcp-app.js");
|
|
95
95
|
|
|
96
96
|
// src/history.ts
|
|
97
|
-
import {
|
|
98
|
-
import { join as join2 } from "node:path";
|
|
99
|
-
var EditHistory = class {
|
|
100
|
-
constructor(dir, onWrite) {
|
|
101
|
-
this.dir = dir;
|
|
102
|
-
this.onWrite = onWrite;
|
|
103
|
-
}
|
|
104
|
-
dir;
|
|
105
|
-
onWrite;
|
|
106
|
-
undoStack = [];
|
|
107
|
-
redoStack = [];
|
|
108
|
-
begin(name) {
|
|
109
|
-
const entry = { name, files: /* @__PURE__ */ new Map() };
|
|
110
|
-
return {
|
|
111
|
-
onWrite: (path, text) => {
|
|
112
|
-
const prior = entry.files.get(path);
|
|
113
|
-
entry.files.set(path, { before: prior?.before ?? readFileSync2(join2(this.dir, path), "utf8"), after: text });
|
|
114
|
-
this.onWrite?.(path, text);
|
|
115
|
-
},
|
|
116
|
-
commit: () => {
|
|
117
|
-
if (!entry.files.size) return;
|
|
118
|
-
this.undoStack.push(entry);
|
|
119
|
-
this.redoStack.length = 0;
|
|
120
|
-
},
|
|
121
|
-
rollback: () => {
|
|
122
|
-
for (const [path, texts] of entry.files) this.write(path, texts.before);
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
state() {
|
|
127
|
-
return {
|
|
128
|
-
canUndo: this.undoStack.length > 0,
|
|
129
|
-
canRedo: this.redoStack.length > 0,
|
|
130
|
-
undoName: this.undoStack.at(-1)?.name ?? null,
|
|
131
|
-
redoName: this.redoStack.at(-1)?.name ?? null
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
undo() {
|
|
135
|
-
return this.step(this.undoStack, this.redoStack, "after", "before");
|
|
136
|
-
}
|
|
137
|
-
redo() {
|
|
138
|
-
return this.step(this.redoStack, this.undoStack, "before", "after");
|
|
139
|
-
}
|
|
140
|
-
step(from, to, expect, write) {
|
|
141
|
-
const entry = from.at(-1);
|
|
142
|
-
if (!entry) return { ok: false, reason: `nothing to ${write === "before" ? "undo" : "redo"}` };
|
|
143
|
-
for (const [path, texts] of entry.files) {
|
|
144
|
-
let current;
|
|
145
|
-
try {
|
|
146
|
-
current = readFileSync2(join2(this.dir, path), "utf8");
|
|
147
|
-
} catch {
|
|
148
|
-
return { ok: false, reason: `${path} no longer exists` };
|
|
149
|
-
}
|
|
150
|
-
if (current !== texts[expect]) return { ok: false, reason: `${path} changed outside the editor since ${entry.name}` };
|
|
151
|
-
}
|
|
152
|
-
for (const [path, texts] of entry.files) this.write(path, texts[write]);
|
|
153
|
-
from.pop();
|
|
154
|
-
to.push(entry);
|
|
155
|
-
return { ok: true, written: [...entry.files.keys()] };
|
|
156
|
-
}
|
|
157
|
-
write(path, text) {
|
|
158
|
-
const target = join2(this.dir, path);
|
|
159
|
-
this.onWrite?.(path, text);
|
|
160
|
-
writeFileSync(`${target}.higgsedit-${process.pid}.tmp`, text);
|
|
161
|
-
renameSync(`${target}.higgsedit-${process.pid}.tmp`, target);
|
|
162
|
-
}
|
|
163
|
-
};
|
|
97
|
+
import { EditHistory } from "./history.mjs";
|
|
164
98
|
|
|
165
99
|
import { Proxies } from "./proxy.mjs";
|
|
166
100
|
|
|
@@ -300,15 +234,15 @@ async function startDev(dir, port = 0, options = {}) {
|
|
|
300
234
|
if (url.pathname !== "/api/edit" || req.method !== "POST") return false;
|
|
301
235
|
const body = JSON.parse((await readBody(req)).toString());
|
|
302
236
|
const tx = history.begin(body.name ?? "Edit");
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
237
|
+
try {
|
|
238
|
+
const results = applyEdits(dir, body.edits, { onWrite: tx.onWrite, ...body.dryRun ? { dryRun: true } : {} });
|
|
239
|
+
if (results.every((r) => r.ok)) tx.commit();
|
|
240
|
+
else tx.rollback();
|
|
241
|
+
json(res, 200, { ok: results.every((r) => r.ok), results, undo: history.state() });
|
|
242
|
+
} catch (error) {
|
|
243
|
+
tx.rollback();
|
|
244
|
+
throw error;
|
|
308
245
|
}
|
|
309
|
-
if (results.every((r) => r.ok)) tx.commit();
|
|
310
|
-
else tx.rollback();
|
|
311
|
-
json(res, 200, { ok: results.every((r) => r.ok), results, undo: history.state() });
|
|
312
246
|
return true;
|
|
313
247
|
},
|
|
314
248
|
(req, res, url) => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { createRequire as __higgseditRequire } from "node:module";
|
|
3
4
|
import { fileURLToPath as __higgseditPath } from "node:url";
|
|
4
5
|
const require = __higgseditRequire(import.meta.url);
|
|
@@ -131,10 +132,14 @@ var TYPES = {
|
|
|
131
132
|
};
|
|
132
133
|
function sendFile(req, res, file) {
|
|
133
134
|
let size;
|
|
135
|
+
let etag;
|
|
136
|
+
let modified;
|
|
134
137
|
try {
|
|
135
|
-
const st = statSync(file);
|
|
138
|
+
const st = statSync(file, { bigint: true });
|
|
136
139
|
if (!st.isFile()) throw new Error("not a file");
|
|
137
|
-
size = st.size;
|
|
140
|
+
size = Number(st.size);
|
|
141
|
+
etag = `"${createHash("sha256").update(`${st.dev}:${st.ino}:${st.size}:${st.mtimeNs}:${st.ctimeNs}`).digest("hex")}"`;
|
|
142
|
+
modified = Math.floor(Number(st.mtimeNs > st.ctimeNs ? st.mtimeNs : st.ctimeNs) / 1e9) * 1e3;
|
|
138
143
|
} catch {
|
|
139
144
|
res.statusCode = 404;
|
|
140
145
|
res.end("not found");
|
|
@@ -143,11 +148,23 @@ function sendFile(req, res, file) {
|
|
|
143
148
|
res.setHeader("content-type", TYPES[extname(file).toLowerCase()] ?? "application/octet-stream");
|
|
144
149
|
res.setHeader("accept-ranges", "bytes");
|
|
145
150
|
res.setHeader("cache-control", "no-cache");
|
|
146
|
-
|
|
151
|
+
res.setHeader("etag", etag);
|
|
152
|
+
res.setHeader("last-modified", new Date(modified).toUTCString());
|
|
153
|
+
const readable = req.method === "GET" || req.method === "HEAD";
|
|
154
|
+
const noneMatch = req.headers["if-none-match"];
|
|
155
|
+
const unmodified = noneMatch !== void 0 ? noneMatch.split(",").some((value) => value.trim() === "*" || value.trim().replace(/^W\//, "") === etag) : Date.parse(req.headers["if-modified-since"] ?? "") >= modified;
|
|
156
|
+
if (readable && unmodified) {
|
|
157
|
+
res.statusCode = 304;
|
|
158
|
+
res.end();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const ifRange = req.headers["if-range"];
|
|
162
|
+
const sameRange = !ifRange || ifRange === etag || modified <= Date.now() - 6e4 && typeof ifRange === "string" && Date.parse(ifRange) === modified;
|
|
163
|
+
const range = req.method === "GET" && sameRange ? /^bytes=(\d*)-(\d*)$/.exec(req.headers.range ?? "") : null;
|
|
147
164
|
if (range) {
|
|
148
|
-
const start = range[1] ? Number(range[1]) : size - Number(range[2]);
|
|
165
|
+
const start = range[1] ? Number(range[1]) : Math.max(0, size - Number(range[2]));
|
|
149
166
|
const end = range[1] && range[2] ? Math.min(Number(range[2]), size - 1) : size - 1;
|
|
150
|
-
if (start > end || start >= size) {
|
|
167
|
+
if (!range[1] && !Number(range[2]) || !Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start > end || start >= size) {
|
|
151
168
|
res.statusCode = 416;
|
|
152
169
|
res.setHeader("content-range", `bytes */${size}`);
|
|
153
170
|
res.end();
|
|
@@ -242191,7 +242191,7 @@ function literalAt(root, path, at) {
|
|
|
242191
242191
|
}
|
|
242192
242192
|
return node;
|
|
242193
242193
|
}
|
|
242194
|
-
function setProp(project, landed, scope, name, value) {
|
|
242194
|
+
function setProp(project, landed, scope, name, value, validateOnly = false) {
|
|
242195
242195
|
const { tag } = landed;
|
|
242196
242196
|
const at = line(tag);
|
|
242197
242197
|
const [prop, ...path] = name.split(".");
|
|
@@ -242210,18 +242210,18 @@ function setProp(project, landed, scope, name, value) {
|
|
|
242210
242210
|
if (ctx.component && ctx.component.callSites.length > 1) {
|
|
242211
242211
|
refuse("shared-component", `<${tagName(tag)}> is inside <${ctx.component.name}>, used at ${ctx.component.callSites.length} call sites; pass ${prop} as a prop to edit one instance`, tag);
|
|
242212
242212
|
}
|
|
242213
|
-
addAttribute(tag, prop, initializerText(value));
|
|
242213
|
+
if (!validateOnly) addAttribute(tag, prop, initializerText(value));
|
|
242214
242214
|
return at;
|
|
242215
242215
|
}
|
|
242216
242216
|
const init = attr.getInitializer();
|
|
242217
242217
|
if (!init) {
|
|
242218
242218
|
if (path.length) refuse("invalid", `${prop} is a bare boolean`, tag);
|
|
242219
242219
|
if (remove) {
|
|
242220
|
-
attr.remove();
|
|
242220
|
+
if (!validateOnly) attr.remove();
|
|
242221
242221
|
return at;
|
|
242222
242222
|
}
|
|
242223
242223
|
if (value === true) return at;
|
|
242224
|
-
attr.setInitializer(initializerText(value));
|
|
242224
|
+
if (!validateOnly) attr.setInitializer(initializerText(value));
|
|
242225
242225
|
return at;
|
|
242226
242226
|
}
|
|
242227
242227
|
const expr = init.isKind(import_ts_morph3.SyntaxKind.JsxExpression) ? init.getExpression() : init;
|
|
@@ -242230,12 +242230,13 @@ function setProp(project, landed, scope, name, value) {
|
|
|
242230
242230
|
if (ctx.component && ctx.component.callSites.length > 1) {
|
|
242231
242231
|
refuse("shared-component", `${prop} is a literal inside <${ctx.component.name}>, which has ${ctx.component.callSites.length} call sites; pass it as a prop to edit one instance`, attr, attr.getText());
|
|
242232
242232
|
}
|
|
242233
|
-
if (remove)
|
|
242234
|
-
|
|
242233
|
+
if (remove) {
|
|
242234
|
+
if (!validateOnly) attr.remove();
|
|
242235
|
+
} else if (path.length) {
|
|
242235
242236
|
const node = literalAt(expr, path, attr);
|
|
242236
|
-
node.replaceWithText(literalText(value));
|
|
242237
|
+
if (!validateOnly) node.replaceWithText(literalText(value));
|
|
242237
242238
|
} else {
|
|
242238
|
-
attr.setInitializer(initializerText(value));
|
|
242239
|
+
if (!validateOnly) attr.setInitializer(initializerText(value));
|
|
242239
242240
|
}
|
|
242240
242241
|
return at;
|
|
242241
242242
|
}
|
|
@@ -242244,7 +242245,7 @@ function setProp(project, landed, scope, name, value) {
|
|
|
242244
242245
|
const forwarded = forwardedProp(expr, ctx);
|
|
242245
242246
|
if (forwarded) {
|
|
242246
242247
|
const site = callSiteFor(ctx, scope, attr);
|
|
242247
|
-
return setProp(project, site, site.scope, [forwarded, ...path].join("."), value);
|
|
242248
|
+
return setProp(project, site, site.scope, [forwarded, ...path].join("."), value, validateOnly);
|
|
242248
242249
|
}
|
|
242249
242250
|
return refuse("expression", `code-driven: ${prop} is bound to an expression`, attr, attr.getText());
|
|
242250
242251
|
}
|
|
@@ -242262,13 +242263,14 @@ function saidRange(child) {
|
|
|
242262
242263
|
return [child.getPos() + lead, child.getEnd() - trail];
|
|
242263
242264
|
}
|
|
242264
242265
|
var jsxText = (text) => /^[^\s{}<>&][^\n{}<>&]*[^\s{}<>&]$|^[^\s{}<>&]$/.test(text) ? text : `{${JSON.stringify(text)}}`;
|
|
242265
|
-
function setText(project, landed, scope, text) {
|
|
242266
|
+
function setText(project, landed, scope, text, validateOnly = false) {
|
|
242266
242267
|
const { tag } = landed;
|
|
242267
242268
|
const at = line(tag);
|
|
242268
242269
|
const element = elementOf(tag);
|
|
242269
242270
|
const sf = tag.getSourceFile();
|
|
242270
242271
|
if (element.isKind(import_ts_morph3.SyntaxKind.JsxSelfClosingElement)) {
|
|
242271
242272
|
if (tagName(tag) !== "text") refuse("invalid", `<${tagName(tag)}> has no text`, tag);
|
|
242273
|
+
if (validateOnly) return at;
|
|
242272
242274
|
const opening = element.getText().replace(/\s*\/>$/, ">");
|
|
242273
242275
|
sf.replaceText([element.getStart(), element.getEnd()], `${opening}${jsxText(text)}</${tagName(tag)}>`);
|
|
242274
242276
|
return at;
|
|
@@ -242279,7 +242281,7 @@ function setText(project, landed, scope, text) {
|
|
|
242279
242281
|
if (expressions.length) {
|
|
242280
242282
|
const e = expressions[0].asKindOrThrow(import_ts_morph3.SyntaxKind.JsxExpression).getExpressionOrThrow();
|
|
242281
242283
|
if (isLiteral(e) && said.length === 1) {
|
|
242282
|
-
e.replaceWithText(literalText(text));
|
|
242284
|
+
if (!validateOnly) e.replaceWithText(literalText(text));
|
|
242283
242285
|
return at;
|
|
242284
242286
|
}
|
|
242285
242287
|
const loopVar = ctx.inLoop ? mentions(e, ctx.loopNames) : void 0;
|
|
@@ -242287,14 +242289,15 @@ function setText(project, landed, scope, text) {
|
|
|
242287
242289
|
const forwarded = said.length === 1 ? forwardedProp(e, ctx) : void 0;
|
|
242288
242290
|
if (forwarded) {
|
|
242289
242291
|
const site = callSiteFor(ctx, scope, e);
|
|
242290
|
-
if (forwarded === "children") return setText(project, site, site.scope, text);
|
|
242291
|
-
return setProp(project, site, site.scope, forwarded, text);
|
|
242292
|
+
if (forwarded === "children") return setText(project, site, site.scope, text, validateOnly);
|
|
242293
|
+
return setProp(project, site, site.scope, forwarded, text, validateOnly);
|
|
242292
242294
|
}
|
|
242293
242295
|
refuse("expression", `code-driven: the text is computed by {${e.getText()}}`, e, e.getText());
|
|
242294
242296
|
}
|
|
242295
242297
|
if (ctx.component && ctx.component.callSites.length > 1) {
|
|
242296
242298
|
refuse("shared-component", `this text is inside <${ctx.component.name}>, used at ${ctx.component.callSites.length} call sites`, tag);
|
|
242297
242299
|
}
|
|
242300
|
+
if (validateOnly) return at;
|
|
242298
242301
|
if (!said.length) {
|
|
242299
242302
|
sf.insertText(element.getOpeningElement().getEnd(), jsxText(text));
|
|
242300
242303
|
return at;
|
|
@@ -242407,7 +242410,22 @@ function finish(project, target, newId, options) {
|
|
|
242407
242410
|
return { ok: true, written, target: target(), ...newId ? { id: newId } : {} };
|
|
242408
242411
|
}
|
|
242409
242412
|
function applyEdit(dir, key, edit, options = {}) {
|
|
242413
|
+
return applyProjectEdit(new SourceProject(dir), key, edit, options);
|
|
242414
|
+
}
|
|
242415
|
+
function applyEdits(dir, edits, options = {}) {
|
|
242410
242416
|
const project = new SourceProject(dir);
|
|
242417
|
+
const results = [];
|
|
242418
|
+
for (const { key, edit } of edits) {
|
|
242419
|
+
const before = new Map([...project.files].map(([path, file]) => [path, file.sourceFile.getFullText()]));
|
|
242420
|
+
const result = applyProjectEdit(project, key, edit, { dryRun: true });
|
|
242421
|
+
if (result.ok) result.written = [...project.files].filter(([path, file]) => file.sourceFile.getFullText() !== before.get(path)).map(([path]) => path);
|
|
242422
|
+
results.push(result);
|
|
242423
|
+
if (!result.ok) return results;
|
|
242424
|
+
}
|
|
242425
|
+
if (!options.dryRun) project.save(options.onWrite);
|
|
242426
|
+
return results;
|
|
242427
|
+
}
|
|
242428
|
+
function applyProjectEdit(project, key, edit, options) {
|
|
242411
242429
|
try {
|
|
242412
242430
|
if (edit.op === "set-const") {
|
|
242413
242431
|
const landed2 = setConst(project, edit);
|
|
@@ -242521,11 +242539,9 @@ function describeProp(project, found, scope, name) {
|
|
|
242521
242539
|
const init = attr?.getInitializer();
|
|
242522
242540
|
const expr = init?.isKind(import_ts_morph3.SyntaxKind.JsxExpression) ? init.getExpression() : init;
|
|
242523
242541
|
const source = attr?.getText();
|
|
242524
|
-
const probe = new SourceProject(project.dir, [...project.files.keys()]);
|
|
242525
242542
|
try {
|
|
242526
|
-
const
|
|
242527
|
-
const
|
|
242528
|
-
const landed = setProp(probe, target, scope, name, 0);
|
|
242543
|
+
const origin = line(found.tag);
|
|
242544
|
+
const landed = setProp(project, found, scope, name, 0, true);
|
|
242529
242545
|
const followed = landed.file !== origin.file || landed.line !== origin.line;
|
|
242530
242546
|
if (!attr) return { name, kind: "absent", editable: true };
|
|
242531
242547
|
if (followed && landed.id) {
|
|
@@ -242549,12 +242565,11 @@ var forwardedName = (expr) => {
|
|
|
242549
242565
|
return void 0;
|
|
242550
242566
|
};
|
|
242551
242567
|
function describeText(project, found, scope) {
|
|
242552
|
-
const probe = new SourceProject(project.dir, [...project.files.keys()]);
|
|
242553
242568
|
const element = elementOf(found.tag);
|
|
242554
242569
|
const said = element.isKind(import_ts_morph3.SyntaxKind.JsxElement) ? element.getJsxChildren().filter(isSaid) : [];
|
|
242555
242570
|
const source = said.map((c) => c.getText()).join("").trim();
|
|
242556
242571
|
try {
|
|
242557
|
-
setText(
|
|
242572
|
+
setText(project, found, scope, "x", true);
|
|
242558
242573
|
const literal = said.length === 1 && said[0].isKind(import_ts_morph3.SyntaxKind.JsxText);
|
|
242559
242574
|
return { name: "text", kind: literal || !said.length ? "literal" : "call-site", editable: true, ...source ? { source } : {} };
|
|
242560
242575
|
} catch (error) {
|
|
@@ -242573,6 +242588,7 @@ export {
|
|
|
242573
242588
|
stampProject,
|
|
242574
242589
|
parseKey,
|
|
242575
242590
|
applyEdit,
|
|
242591
|
+
applyEdits,
|
|
242576
242592
|
analyze,
|
|
242577
242593
|
sourceFacts
|
|
242578
242594
|
};
|
|
@@ -4,13 +4,6 @@ import { fileURLToPath as __higgseditPath } from "node:url";
|
|
|
4
4
|
const require = __higgseditRequire(import.meta.url);
|
|
5
5
|
const __filename = __higgseditPath(import.meta.url);
|
|
6
6
|
const __dirname = __filename.slice(0, __filename.lastIndexOf("/"));
|
|
7
|
-
import {
|
|
8
|
-
frame,
|
|
9
|
-
render,
|
|
10
|
-
sheet,
|
|
11
|
-
verify
|
|
12
|
-
} from "./chunk-4D6AXU4B.mjs";
|
|
13
|
-
import "./chunk-QLPCYN6K.mjs";
|
|
14
7
|
import {
|
|
15
8
|
flag,
|
|
16
9
|
parseArgs
|
|
@@ -18,21 +11,6 @@ import {
|
|
|
18
11
|
import {
|
|
19
12
|
CLI_MANIFEST
|
|
20
13
|
} from "./chunk-FZ5U36SQ.mjs";
|
|
21
|
-
import {
|
|
22
|
-
check
|
|
23
|
-
} from "./chunk-MK6PL7N4.mjs";
|
|
24
|
-
import {
|
|
25
|
-
stampProject
|
|
26
|
-
} from "./chunk-VI6IV6JP.mjs";
|
|
27
|
-
import {
|
|
28
|
-
assertProject
|
|
29
|
-
} from "./chunk-SIMO6I3D.mjs";
|
|
30
|
-
import "./chunk-6BETMESC.mjs";
|
|
31
|
-
import "./chunk-WZR5PZM7.mjs";
|
|
32
|
-
import "./chunk-F7YUKEI5.mjs";
|
|
33
|
-
import "./chunk-EKFC4S4M.mjs";
|
|
34
|
-
import "./chunk-2VKQHJMM.mjs";
|
|
35
|
-
import "./chunk-75WO2NOF.mjs";
|
|
36
14
|
|
|
37
15
|
// src/main.ts
|
|
38
16
|
import { resolve } from "node:path";
|
|
@@ -114,11 +92,13 @@ async function main(argv) {
|
|
|
114
92
|
print(r, () => [r.written.length ? `created ${target}` : `${target}: nothing to create`, ...r.written.map((f) => ` ${f}`), ...r.skipped.map((f) => ` skipped (exists): ${f}`), `next: ${cd}npm install && npm run dev`].join("\n"));
|
|
115
93
|
return 0;
|
|
116
94
|
}
|
|
95
|
+
const { assertProject } = await import("./chunk-SIMO6I3D.mjs");
|
|
117
96
|
const dir = assertProject(dirArg);
|
|
118
97
|
const depth = flag(args, "depth") === "10" ? 10 : 8;
|
|
119
98
|
const allowFontFallback = args.flags["allow-font-fallback"] === true;
|
|
120
99
|
switch (command) {
|
|
121
100
|
case "check": {
|
|
101
|
+
const { check } = await import("./chunk-MK6PL7N4.mjs");
|
|
122
102
|
const r = await check(dir, { allowFontFallback });
|
|
123
103
|
print(
|
|
124
104
|
r,
|
|
@@ -137,6 +117,7 @@ async function main(argv) {
|
|
|
137
117
|
return r.ok ? 0 : 1;
|
|
138
118
|
}
|
|
139
119
|
case "stamp": {
|
|
120
|
+
const { stampProject } = await import("./chunk-VI6IV6JP.mjs");
|
|
140
121
|
const r = stampProject(dir);
|
|
141
122
|
print(r, () => [`stamped ${r.stamped.length} element(s) in ${r.written.length} file(s)`, ...r.stamped.map((s) => ` ${s.file}: <${s.tag}> id="${s.id}"`), ...r.skipped.map((s) => ` skipped ${s.file}: ${s.reason}`)].join("\n"));
|
|
142
123
|
return r.skipped.length ? 1 : 0;
|
|
@@ -145,6 +126,7 @@ async function main(argv) {
|
|
|
145
126
|
const at = args.positional[1];
|
|
146
127
|
const out = flag(args, "o", "out");
|
|
147
128
|
if (!at || !out) throw new Error("usage: higgsedit frame <dir> <t> -o x.png");
|
|
129
|
+
const { frame } = await import("./chunk-4D6AXU4B.mjs");
|
|
148
130
|
const r = await frame(dir, at, out, depth, allowFontFallback);
|
|
149
131
|
print(r, () => `wrote ${r.out} (frame ${r.frame}, ${r.ms.toFixed(0)} ms)`);
|
|
150
132
|
return 0;
|
|
@@ -153,6 +135,7 @@ async function main(argv) {
|
|
|
153
135
|
const times = flag(args, "times")?.split(",").map((s) => s.trim()).filter(Boolean);
|
|
154
136
|
const out = flag(args, "o", "out");
|
|
155
137
|
if (!times?.length || !out) throw new Error("usage: higgsedit sheet <dir> --times 0,1,2 -o sheet.png");
|
|
138
|
+
const { sheet } = await import("./chunk-4D6AXU4B.mjs");
|
|
156
139
|
const r = await sheet(dir, times, out, Number(flag(args, "columns") ?? Math.min(4, times.length)), Number(flag(args, "width") ?? 480), allowFontFallback);
|
|
157
140
|
print(r, () => `wrote ${r.out} (frames ${r.frames.join(", ")})`);
|
|
158
141
|
return 0;
|
|
@@ -163,6 +146,7 @@ async function main(argv) {
|
|
|
163
146
|
const encoder = flag(args, "encoder");
|
|
164
147
|
if (encoder && !ENCODERS.includes(encoder)) throw new Error(`unknown --encoder ${encoder}; use ${ENCODERS.join(", ")}`);
|
|
165
148
|
const crf = flag(args, "crf");
|
|
149
|
+
const { render } = await import("./chunk-4D6AXU4B.mjs");
|
|
166
150
|
const r = await render(dir, {
|
|
167
151
|
out,
|
|
168
152
|
depth,
|
|
@@ -188,6 +172,7 @@ async function main(argv) {
|
|
|
188
172
|
return 0;
|
|
189
173
|
}
|
|
190
174
|
case "verify": {
|
|
175
|
+
const { verify } = await import("./chunk-4D6AXU4B.mjs");
|
|
191
176
|
const r = await verify(dir, Number(flag(args, "count") ?? 12), depth, allowFontFallback);
|
|
192
177
|
print(r, () => [`${r.identical ? "identical" : "DIFFERENT"}: ${r.frames.length} frames, ${r.depth}-bit, shuffled order ${r.shuffledOrder.join(",")}`, ...r.frames.map((f, i) => ` ${String(f).padStart(6)} ${r.forward[i]} ${r.shuffled[i] === r.forward[i] ? "=" : "\u2260"}`)].join("\n"));
|
|
193
178
|
return r.identical ? 0 : 1;
|
|
@@ -213,13 +198,18 @@ async function main(argv) {
|
|
|
213
198
|
return 1;
|
|
214
199
|
}
|
|
215
200
|
}
|
|
201
|
+
function exitAfterOutput(code) {
|
|
202
|
+
let pending = 2;
|
|
203
|
+
const flushed = () => { if (--pending === 0) process.exit(code); };
|
|
204
|
+
for (const stream of [process.stdout, process.stderr]) stream.write("", flushed);
|
|
205
|
+
}
|
|
216
206
|
patientConnects();
|
|
217
207
|
main(process.argv.slice(2)).then(
|
|
218
|
-
|
|
208
|
+
exitAfterOutput,
|
|
219
209
|
(error) => {
|
|
220
210
|
const hint = error instanceof Error && error.name === "FontLoadError" ? " Pass --allow-font-fallback to render anyway." : "";
|
|
221
211
|
console.error(`higgsedit: ${error instanceof Error ? error.message : String(error)}${hint}`);
|
|
222
|
-
|
|
212
|
+
exitAfterOutput(1);
|
|
223
213
|
}
|
|
224
214
|
);
|
|
225
215
|
var CLI_ROOT = resolve(import.meta.dirname, "..");
|