ima2-gen 1.1.8 → 1.1.9
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 +56 -27
- package/bin/commands/annotate.js +137 -0
- package/bin/commands/annotate.ts +118 -0
- package/bin/commands/cancel.js +37 -33
- package/bin/commands/cancel.ts +45 -0
- package/bin/commands/canvas-versions.js +91 -0
- package/bin/commands/canvas-versions.ts +80 -0
- package/bin/commands/cardnews.js +293 -0
- package/bin/commands/cardnews.ts +248 -0
- package/bin/commands/comfy.js +63 -0
- package/bin/commands/comfy.ts +54 -0
- package/bin/commands/config.js +270 -0
- package/bin/commands/config.ts +265 -0
- package/bin/commands/edit.js +97 -72
- package/bin/commands/edit.ts +116 -0
- package/bin/commands/gen.js +140 -118
- package/bin/commands/gen.ts +176 -0
- package/bin/commands/history.js +164 -0
- package/bin/commands/history.ts +145 -0
- package/bin/commands/ls.js +60 -42
- package/bin/commands/ls.ts +60 -0
- package/bin/commands/metadata.js +45 -0
- package/bin/commands/metadata.ts +36 -0
- package/bin/commands/multimode.js +159 -0
- package/bin/commands/multimode.ts +146 -0
- package/bin/commands/node.js +176 -0
- package/bin/commands/node.ts +157 -0
- package/bin/commands/observability.js +201 -0
- package/bin/commands/observability.ts +176 -0
- package/bin/commands/ping.js +26 -20
- package/bin/commands/ping.ts +29 -0
- package/bin/commands/prompt.js +506 -0
- package/bin/commands/prompt.ts +421 -0
- package/bin/commands/ps.js +78 -71
- package/bin/commands/ps.ts +78 -0
- package/bin/commands/session.js +308 -0
- package/bin/commands/session.ts +265 -0
- package/bin/commands/show.js +75 -40
- package/bin/commands/show.ts +69 -0
- package/bin/ima2.js +324 -310
- package/bin/ima2.ts +444 -0
- package/bin/lib/args.js +75 -66
- package/bin/lib/args.ts +73 -0
- package/bin/lib/browser-id.js +15 -0
- package/bin/lib/browser-id.ts +16 -0
- package/bin/lib/client.js +91 -83
- package/bin/lib/client.ts +109 -0
- package/bin/lib/error-hints.js +14 -17
- package/bin/lib/error-hints.ts +23 -0
- package/bin/lib/files.js +26 -28
- package/bin/lib/files.ts +39 -0
- package/bin/lib/output.js +44 -42
- package/bin/lib/output.ts +58 -0
- package/bin/lib/platform.js +60 -56
- package/bin/lib/platform.ts +97 -0
- package/bin/lib/sse.js +73 -0
- package/bin/lib/sse.ts +73 -0
- package/bin/lib/star-prompt.js +69 -76
- package/bin/lib/star-prompt.ts +97 -0
- package/bin/lib/storage-doctor.js +34 -35
- package/bin/lib/storage-doctor.ts +38 -0
- package/config.js +147 -243
- package/config.ts +331 -0
- package/docs/API.md +48 -8
- package/docs/CLI.md +190 -0
- package/docs/FAQ.ko.md +5 -5
- package/docs/FAQ.md +5 -5
- package/docs/README.ja.md +71 -25
- package/docs/README.ko.md +61 -24
- package/docs/README.zh-CN.md +73 -27
- package/lib/assetLifecycle.js +130 -130
- package/lib/assetLifecycle.ts +142 -0
- package/lib/canvasVersionStore.js +135 -153
- package/lib/canvasVersionStore.ts +181 -0
- package/lib/cardNewsGenerator.js +127 -142
- package/lib/cardNewsGenerator.ts +162 -0
- package/lib/cardNewsJobStore.js +78 -84
- package/lib/cardNewsJobStore.ts +107 -0
- package/lib/cardNewsManifestStore.js +88 -93
- package/lib/cardNewsManifestStore.ts +112 -0
- package/lib/cardNewsPlanner.js +157 -152
- package/lib/cardNewsPlanner.ts +180 -0
- package/lib/cardNewsPlannerClient.js +101 -98
- package/lib/cardNewsPlannerClient.ts +114 -0
- package/lib/cardNewsPlannerPrompt.js +56 -56
- package/lib/cardNewsPlannerPrompt.ts +60 -0
- package/lib/cardNewsPlannerSchema.js +231 -223
- package/lib/cardNewsPlannerSchema.ts +259 -0
- package/lib/cardNewsRoleTemplateStore.js +39 -41
- package/lib/cardNewsRoleTemplateStore.ts +47 -0
- package/lib/cardNewsTemplateStore.js +171 -175
- package/lib/cardNewsTemplateStore.ts +210 -0
- package/lib/codexDetect.js +44 -47
- package/lib/codexDetect.ts +69 -0
- package/lib/comfyBridge.js +164 -184
- package/lib/comfyBridge.ts +214 -0
- package/lib/db.js +41 -51
- package/lib/db.ts +166 -0
- package/lib/errorClassify.js +62 -78
- package/lib/errorClassify.ts +100 -0
- package/lib/generationErrors.js +140 -103
- package/lib/generationErrors.ts +125 -0
- package/lib/historyList.js +149 -147
- package/lib/historyList.ts +164 -0
- package/lib/imageMetadata.js +86 -89
- package/lib/imageMetadata.ts +111 -0
- package/lib/imageMetadataStore.js +46 -51
- package/lib/imageMetadataStore.ts +67 -0
- package/lib/imageModels.js +38 -45
- package/lib/imageModels.ts +52 -0
- package/lib/inflight.js +131 -150
- package/lib/inflight.ts +204 -0
- package/lib/localImportStore.js +87 -93
- package/lib/localImportStore.ts +111 -0
- package/lib/logger.js +105 -112
- package/lib/logger.ts +150 -0
- package/lib/nodeStore.js +65 -64
- package/lib/nodeStore.ts +81 -0
- package/lib/oauthLauncher.js +61 -59
- package/lib/oauthLauncher.ts +64 -0
- package/lib/oauthNormalize.js +15 -19
- package/lib/oauthNormalize.ts +30 -0
- package/lib/oauthProxy.js +834 -832
- package/lib/oauthProxy.ts +995 -0
- package/lib/openDirectory.js +41 -40
- package/lib/openDirectory.ts +45 -0
- package/lib/pngInfo.js +18 -20
- package/lib/pngInfo.ts +26 -0
- package/lib/promptImport/curatedSources.js +125 -129
- package/lib/promptImport/curatedSources.ts +139 -0
- package/lib/promptImport/discoveryRegistry.js +185 -203
- package/lib/promptImport/discoveryRegistry.ts +236 -0
- package/lib/promptImport/errors.js +10 -10
- package/lib/promptImport/errors.ts +18 -0
- package/lib/promptImport/githubDiscovery.js +209 -219
- package/lib/promptImport/githubDiscovery.ts +248 -0
- package/lib/promptImport/githubFolder.js +253 -259
- package/lib/promptImport/githubFolder.ts +308 -0
- package/lib/promptImport/githubSource.js +189 -200
- package/lib/promptImport/githubSource.ts +239 -0
- package/lib/promptImport/gptImageHints.js +49 -56
- package/lib/promptImport/gptImageHints.ts +68 -0
- package/lib/promptImport/parsePromptCandidates.js +108 -123
- package/lib/promptImport/parsePromptCandidates.ts +153 -0
- package/lib/promptImport/promptIndex.js +190 -208
- package/lib/promptImport/promptIndex.ts +248 -0
- package/lib/promptImport/rankPromptCandidates.js +46 -43
- package/lib/promptImport/rankPromptCandidates.ts +49 -0
- package/lib/providerOptions.js +31 -0
- package/lib/providerOptions.ts +41 -0
- package/lib/referenceImageCompress.js +51 -62
- package/lib/referenceImageCompress.ts +75 -0
- package/lib/refs.js +93 -81
- package/lib/refs.ts +117 -0
- package/lib/requestLogger.js +32 -38
- package/lib/requestLogger.ts +48 -0
- package/lib/responsesImageAdapter.js +351 -0
- package/lib/responsesImageAdapter.ts +352 -0
- package/lib/runtimePorts.js +71 -73
- package/lib/runtimePorts.ts +93 -0
- package/lib/sessionStore.js +179 -230
- package/lib/sessionStore.ts +272 -0
- package/lib/storageMigration.js +247 -245
- package/lib/storageMigration.ts +284 -0
- package/lib/styleSheet.js +86 -90
- package/lib/styleSheet.ts +128 -0
- package/lib/systemTrash.js +18 -0
- package/lib/systemTrash.ts +20 -0
- package/package.json +26 -10
- package/routes/annotations.js +76 -79
- package/routes/annotations.ts +95 -0
- package/routes/canvasVersions.js +50 -54
- package/routes/canvasVersions.ts +64 -0
- package/routes/cardNews.js +158 -171
- package/routes/cardNews.ts +183 -0
- package/routes/comfy.js +23 -31
- package/routes/comfy.ts +39 -0
- package/routes/edit.js +183 -214
- package/routes/edit.ts +230 -0
- package/routes/generate.js +269 -291
- package/routes/generate.ts +309 -0
- package/routes/health.js +102 -107
- package/routes/health.ts +114 -0
- package/routes/history.js +136 -144
- package/routes/history.ts +153 -0
- package/routes/imageImport.js +27 -27
- package/routes/imageImport.ts +33 -0
- package/routes/index.js +17 -17
- package/routes/index.ts +35 -0
- package/routes/metadata.js +60 -64
- package/routes/metadata.ts +71 -0
- package/routes/multimode.js +228 -263
- package/routes/multimode.ts +280 -0
- package/routes/nodes.js +378 -424
- package/routes/nodes.ts +455 -0
- package/routes/promptImport.js +284 -324
- package/routes/promptImport.ts +354 -0
- package/routes/prompts.js +333 -360
- package/routes/prompts.ts +379 -0
- package/routes/sessions.js +277 -285
- package/routes/sessions.ts +292 -0
- package/routes/storage.js +29 -31
- package/routes/storage.ts +39 -0
- package/server.js +189 -196
- package/server.ts +235 -0
- package/ui/dist/.vite/manifest.json +101 -0
- package/ui/dist/assets/CardNewsWorkspace-BJOCey7Z.js +2 -0
- package/ui/dist/assets/NodeCanvas-BZV40eAE.css +1 -0
- package/ui/dist/assets/NodeCanvas-C3dzYNsk.js +7 -0
- package/ui/dist/assets/PromptImportDialog-Dqu1VpUh.js +2 -0
- package/ui/dist/assets/PromptImportDiscoverySection-Dg8T9X0L.js +1 -0
- package/ui/dist/assets/PromptImportFolderSection-DBaqsFO4.js +1 -0
- package/ui/dist/assets/PromptLibraryPanel-p5QqR97M.js +2 -0
- package/ui/dist/assets/SettingsWorkspace-B5bSAZ6u.js +1 -0
- package/ui/dist/assets/index-C9cXwiWE.js +25 -0
- package/ui/dist/assets/index-CGMIkZXn.css +1 -0
- package/ui/dist/assets/index-Cvld7dUZ.js +1 -0
- package/ui/dist/index.html +2 -2
- package/assets/phase-a-bg-cleanup-test.png +0 -0
- package/assets/screenshot.png +0 -0
- package/assets/screenshots/classic-generate-light.png +0 -0
- package/assets/screenshots/node-graph-branching.png +0 -0
- package/assets/screenshots/settings-oauth-generation.png +0 -0
- package/assets/screenshots/settings-workspace.png +0 -0
- package/assets/screenshots/style-sheet-editor.png +0 -0
- package/integrations/comfyui/ima2_gen_bridge/__pycache__/__init__.cpython-313.pyc +0 -0
- package/integrations/comfyui/ima2_gen_bridge/__pycache__/nodes.cpython-313.pyc +0 -0
- package/ui/dist/assets/index-BDffwmLs.css +0 -1
- package/ui/dist/assets/index-D0fdHLkJ.js +0 -31
- package/ui/dist/assets/index-D0fdHLkJ.js.map +0 -1
|
@@ -1,68 +1,61 @@
|
|
|
1
1
|
const MODEL_HINTS = [
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
["gpt-image-2", /\bgpt[- ]?image[- ]?2\b/i],
|
|
3
|
+
["gpt-image", /\bgpt[- ]?image\b/i],
|
|
4
|
+
["nano-banana", /\bnano[- ]?banana\b/i],
|
|
5
|
+
["midjourney", /\bmidjourney\b/i],
|
|
6
|
+
["stable-diffusion", /\bstable[- ]?diffusion\b/i],
|
|
7
7
|
];
|
|
8
|
-
|
|
9
8
|
const TASK_HINTS = [
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
["image_generation", /\b(image generation|generate an image|text to image)\b/i],
|
|
10
|
+
["reference-image", /\b(reference image|keep the same|preserve|consistent character|same face)\b/i],
|
|
11
|
+
["mask-edit", /\b(mask|inpaint|outpaint|replace only|change only)\b/i],
|
|
12
|
+
["typography", /\b(typography|headline|readable text|lettering|poster text|title text)\b/i],
|
|
13
|
+
["layout", /\b(layout|grid|composition|poster|diagram|infographic)\b/i],
|
|
14
|
+
["product-shot", /\b(product photo|product shot|packaging|label|reflection)\b/i],
|
|
16
15
|
];
|
|
17
|
-
|
|
18
16
|
const SIZE_HINTS = [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
["2k", /\b2k\b/i],
|
|
18
|
+
["4k", /\b4k\b/i],
|
|
19
|
+
["square", /\bsquare\b/i],
|
|
20
|
+
["portrait", /\bportrait|vertical\b/i],
|
|
21
|
+
["landscape", /\blandscape|horizontal\b/i],
|
|
24
22
|
];
|
|
25
|
-
|
|
26
23
|
const QUALITY_HINTS = [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
["low", /\blow\b/i],
|
|
25
|
+
["medium", /\bmedium\b/i],
|
|
26
|
+
["high", /\bhigh\b/i],
|
|
27
|
+
["auto", /\bauto\b/i],
|
|
28
|
+
["draft", /\bdraft|thumbnail|iteration\b/i],
|
|
29
|
+
["final", /\bfinal|production[- ]ready\b/i],
|
|
33
30
|
];
|
|
34
|
-
|
|
35
31
|
function matches(text, patterns) {
|
|
36
|
-
|
|
32
|
+
return patterns.filter(([, pattern]) => pattern.test(text)).map(([name]) => name);
|
|
37
33
|
}
|
|
38
|
-
|
|
39
34
|
export function extractGptImageHints(text) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
warnings,
|
|
67
|
-
};
|
|
35
|
+
const value = String(text || "");
|
|
36
|
+
const modelHints = matches(value, MODEL_HINTS);
|
|
37
|
+
const taskHints = matches(value, TASK_HINTS);
|
|
38
|
+
const sizeHints = matches(value, SIZE_HINTS);
|
|
39
|
+
const qualityHints = matches(value, QUALITY_HINTS);
|
|
40
|
+
const warnings = [];
|
|
41
|
+
if (/\btransparent|alpha channel|no background|cutout\b/i.test(value)) {
|
|
42
|
+
warnings.push("transparent-unsupported-gpt-image-2");
|
|
43
|
+
}
|
|
44
|
+
if (/\bexact text|small text|dense text|legal copy\b/i.test(value)) {
|
|
45
|
+
warnings.push("text-rendering-sensitive");
|
|
46
|
+
}
|
|
47
|
+
if (/\bcomplex layout|multi[- ]panel|precise diagram\b/i.test(value)) {
|
|
48
|
+
warnings.push("layout-sensitive");
|
|
49
|
+
}
|
|
50
|
+
if (/\b4k|high resolution|ultra[- ]high\b/i.test(value)) {
|
|
51
|
+
warnings.push("high-res-cost-warning");
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
modelHints,
|
|
55
|
+
generationSurfaceHints: taskHints.includes("image_generation") ? ["responses-image-tool"] : [],
|
|
56
|
+
taskHints,
|
|
57
|
+
sizeHints,
|
|
58
|
+
qualityHints,
|
|
59
|
+
warnings,
|
|
60
|
+
};
|
|
68
61
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const MODEL_HINTS = [
|
|
2
|
+
["gpt-image-2", /\bgpt[- ]?image[- ]?2\b/i],
|
|
3
|
+
["gpt-image", /\bgpt[- ]?image\b/i],
|
|
4
|
+
["nano-banana", /\bnano[- ]?banana\b/i],
|
|
5
|
+
["midjourney", /\bmidjourney\b/i],
|
|
6
|
+
["stable-diffusion", /\bstable[- ]?diffusion\b/i],
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
const TASK_HINTS = [
|
|
10
|
+
["image_generation", /\b(image generation|generate an image|text to image)\b/i],
|
|
11
|
+
["reference-image", /\b(reference image|keep the same|preserve|consistent character|same face)\b/i],
|
|
12
|
+
["mask-edit", /\b(mask|inpaint|outpaint|replace only|change only)\b/i],
|
|
13
|
+
["typography", /\b(typography|headline|readable text|lettering|poster text|title text)\b/i],
|
|
14
|
+
["layout", /\b(layout|grid|composition|poster|diagram|infographic)\b/i],
|
|
15
|
+
["product-shot", /\b(product photo|product shot|packaging|label|reflection)\b/i],
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const SIZE_HINTS = [
|
|
19
|
+
["2k", /\b2k\b/i],
|
|
20
|
+
["4k", /\b4k\b/i],
|
|
21
|
+
["square", /\bsquare\b/i],
|
|
22
|
+
["portrait", /\bportrait|vertical\b/i],
|
|
23
|
+
["landscape", /\blandscape|horizontal\b/i],
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const QUALITY_HINTS = [
|
|
27
|
+
["low", /\blow\b/i],
|
|
28
|
+
["medium", /\bmedium\b/i],
|
|
29
|
+
["high", /\bhigh\b/i],
|
|
30
|
+
["auto", /\bauto\b/i],
|
|
31
|
+
["draft", /\bdraft|thumbnail|iteration\b/i],
|
|
32
|
+
["final", /\bfinal|production[- ]ready\b/i],
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
function matches(text, patterns) {
|
|
36
|
+
return patterns.filter(([, pattern]) => pattern.test(text)).map(([name]) => name);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function extractGptImageHints(text) {
|
|
40
|
+
const value = String(text || "");
|
|
41
|
+
const modelHints = matches(value, MODEL_HINTS);
|
|
42
|
+
const taskHints = matches(value, TASK_HINTS);
|
|
43
|
+
const sizeHints = matches(value, SIZE_HINTS);
|
|
44
|
+
const qualityHints = matches(value, QUALITY_HINTS);
|
|
45
|
+
const warnings = [];
|
|
46
|
+
|
|
47
|
+
if (/\btransparent|alpha channel|no background|cutout\b/i.test(value)) {
|
|
48
|
+
warnings.push("transparent-unsupported-gpt-image-2");
|
|
49
|
+
}
|
|
50
|
+
if (/\bexact text|small text|dense text|legal copy\b/i.test(value)) {
|
|
51
|
+
warnings.push("text-rendering-sensitive");
|
|
52
|
+
}
|
|
53
|
+
if (/\bcomplex layout|multi[- ]panel|precise diagram\b/i.test(value)) {
|
|
54
|
+
warnings.push("layout-sensitive");
|
|
55
|
+
}
|
|
56
|
+
if (/\b4k|high resolution|ultra[- ]high\b/i.test(value)) {
|
|
57
|
+
warnings.push("high-res-cost-warning");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
modelHints,
|
|
62
|
+
generationSurfaceHints: taskHints.includes("image_generation") ? ["responses-image-tool"] : [],
|
|
63
|
+
taskHints,
|
|
64
|
+
sizeHints,
|
|
65
|
+
qualityHints,
|
|
66
|
+
warnings,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -1,153 +1,138 @@
|
|
|
1
1
|
import { createHash } from "crypto";
|
|
2
|
-
|
|
3
2
|
function normalizeWhitespace(text) {
|
|
4
|
-
|
|
3
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+\n/g, "\n").trim();
|
|
5
4
|
}
|
|
6
|
-
|
|
7
5
|
function stripFrontmatter(text) {
|
|
8
|
-
|
|
6
|
+
return text.replace(/^---\s*\n[\s\S]*?\n---\s*\n/, "");
|
|
9
7
|
}
|
|
10
|
-
|
|
11
8
|
function isBoilerplate(line) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/^\[.*\]\(.+\)$/.test(trimmed)
|
|
18
|
-
);
|
|
9
|
+
const trimmed = line.trim();
|
|
10
|
+
return (!trimmed ||
|
|
11
|
+
/^\[!\[.*\]\(.+\)\]\(.+\)$/.test(trimmed) ||
|
|
12
|
+
/^!\[.*\]\(.+\)$/.test(trimmed) ||
|
|
13
|
+
/^\[.*\]\(.+\)$/.test(trimmed));
|
|
19
14
|
}
|
|
20
|
-
|
|
21
15
|
function titleFromFilename(filename) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
return (filename || "Imported prompt")
|
|
17
|
+
.replace(/\.(txt|md|markdown)$/i, "")
|
|
18
|
+
.replace(/[-_]+/g, " ")
|
|
19
|
+
.trim() || "Imported prompt";
|
|
26
20
|
}
|
|
27
|
-
|
|
28
21
|
function candidateId(text, ordinal) {
|
|
29
|
-
|
|
22
|
+
return `candidate_${ordinal}_${createHash("sha256").update(text).digest("hex").slice(0, 10)}`;
|
|
30
23
|
}
|
|
31
|
-
|
|
32
24
|
function promptHash(text) {
|
|
33
|
-
|
|
25
|
+
return createHash("sha256").update(normalizeWhitespace(text).toLowerCase()).digest("hex");
|
|
34
26
|
}
|
|
35
|
-
|
|
36
27
|
function headingName(heading, fallback) {
|
|
37
|
-
|
|
28
|
+
return heading?.replace(/^#+\s*/, "").trim() || fallback;
|
|
38
29
|
}
|
|
39
|
-
|
|
40
30
|
function allowedCandidate(text, limits) {
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
const length = text.trim().length;
|
|
32
|
+
return length >= limits.minCandidateChars && length <= limits.maxCandidateChars;
|
|
43
33
|
}
|
|
44
|
-
|
|
45
34
|
function cleanMarkdownBody(text) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
.join("\n"),
|
|
52
|
-
);
|
|
35
|
+
return normalizeWhitespace(text
|
|
36
|
+
.split("\n")
|
|
37
|
+
.filter((line) => !isBoilerplate(line))
|
|
38
|
+
.filter((line) => !/^\|.*\|$/.test(line.trim()))
|
|
39
|
+
.join("\n"));
|
|
53
40
|
}
|
|
54
|
-
|
|
55
41
|
function pushCandidate(candidates, rawText, options) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
42
|
+
const text = normalizeWhitespace(rawText);
|
|
43
|
+
if (!allowedCandidate(text, options.limits))
|
|
44
|
+
return;
|
|
45
|
+
const ordinal = candidates.length + 1;
|
|
46
|
+
const hash = promptHash(text);
|
|
47
|
+
candidates.push({
|
|
48
|
+
id: candidateId(text, ordinal),
|
|
49
|
+
name: options.name,
|
|
50
|
+
text,
|
|
51
|
+
textPreview: text.slice(0, 220),
|
|
52
|
+
tags: [...new Set(options.tags)],
|
|
53
|
+
warnings: options.warnings ?? [],
|
|
54
|
+
source: options.source,
|
|
55
|
+
headingPath: options.headingPath ?? null,
|
|
56
|
+
ordinal,
|
|
57
|
+
promptHash: hash,
|
|
58
|
+
scoreHints: options.scoreHints ?? {},
|
|
59
|
+
});
|
|
73
60
|
}
|
|
74
|
-
|
|
75
61
|
function parseMarkdown(text, options) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
62
|
+
const source = stripFrontmatter(text).slice(0, options.limits.maxSourceCharsScanned);
|
|
63
|
+
const fencePattern = /```([A-Za-z0-9_-]*)\n([\s\S]*?)```/g;
|
|
64
|
+
const acceptedFenceLanguages = new Set(["", "prompt", "text", "markdown", "md"]);
|
|
65
|
+
const ranges = [];
|
|
66
|
+
for (const match of source.matchAll(fencePattern)) {
|
|
67
|
+
const language = (match[1] || "").toLowerCase();
|
|
68
|
+
if (!acceptedFenceLanguages.has(language))
|
|
69
|
+
continue;
|
|
70
|
+
ranges.push([match.index ?? 0, (match.index ?? 0) + match[0].length]);
|
|
71
|
+
pushCandidate(options.candidates, match[2], {
|
|
72
|
+
...options,
|
|
73
|
+
name: `${options.baseName} ${options.candidates.length + 1}`,
|
|
74
|
+
headingPath: options.headingPath ?? null,
|
|
75
|
+
});
|
|
76
|
+
if (options.candidates.length >= options.limits.maxPromptCandidatesPerFile)
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const withoutFences = source
|
|
80
|
+
.split("\n")
|
|
81
|
+
.filter((line, index, lines) => {
|
|
82
|
+
const offset = lines.slice(0, index).join("\n").length + (index > 0 ? 1 : 0);
|
|
83
|
+
return !ranges.some(([start, end]) => offset >= start && offset < end);
|
|
98
84
|
})
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
85
|
+
.join("\n");
|
|
86
|
+
const sections = withoutFences.split(/(?=^#{1,4}\s+)/gm);
|
|
87
|
+
for (const section of sections) {
|
|
88
|
+
const heading = /^#{1,4}\s+(.+)$/m.exec(section)?.[1];
|
|
89
|
+
const body = cleanMarkdownBody(section.replace(/^#{1,4}\s+.+$/m, ""));
|
|
90
|
+
pushCandidate(options.candidates, body, {
|
|
91
|
+
...options,
|
|
92
|
+
name: headingName(heading, `${options.baseName} ${options.candidates.length + 1}`),
|
|
93
|
+
headingPath: headingName(heading, options.baseName),
|
|
94
|
+
});
|
|
95
|
+
if (options.candidates.length >= options.limits.maxPromptCandidatesPerFile)
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
111
98
|
}
|
|
112
|
-
|
|
113
99
|
function splitTextPrompts(text) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
100
|
+
const normalized = normalizeWhitespace(text);
|
|
101
|
+
const separatorBlocks = normalized.split(/\n\s*---+\s*\n/g).filter(Boolean);
|
|
102
|
+
const blocks = separatorBlocks.length > 1
|
|
103
|
+
? separatorBlocks
|
|
104
|
+
: normalized.split(/\n\s*\n+/g).filter(Boolean);
|
|
105
|
+
const numbered = normalized.split(/(?=^\s*\d+[.)]\s+)/gm).filter(Boolean);
|
|
106
|
+
const longLines = normalized.split("\n").map((line) => line.trim()).filter((line) => line.length >= 80);
|
|
107
|
+
return blocks.length > 1 ? blocks : numbered.length > 1 ? numbered : longLines.length > 1 ? longLines : [normalized];
|
|
122
108
|
}
|
|
123
|
-
|
|
124
109
|
function parsePlainText(text, options) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
110
|
+
const chunks = splitTextPrompts(text.slice(0, options.limits.maxSourceCharsScanned));
|
|
111
|
+
for (const chunk of chunks) {
|
|
112
|
+
const clean = chunk.replace(/^\s*\d+[.)]\s+/, "");
|
|
113
|
+
pushCandidate(options.candidates, clean, {
|
|
114
|
+
...options,
|
|
115
|
+
name: `${options.baseName} ${options.candidates.length + 1}`,
|
|
116
|
+
headingPath: `${options.baseName} ${options.candidates.length + 1}`,
|
|
117
|
+
});
|
|
118
|
+
if (options.candidates.length >= options.limits.maxPromptCandidatesPerFile)
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
135
121
|
}
|
|
136
|
-
|
|
137
122
|
export function parsePromptCandidates({ text, filename, source, tags = [], limits }) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
123
|
+
const candidates = [];
|
|
124
|
+
const extension = (filename.split(".").pop() || "").toLowerCase();
|
|
125
|
+
const baseName = titleFromFilename(filename);
|
|
126
|
+
const common = {
|
|
127
|
+
candidates,
|
|
128
|
+
limits,
|
|
129
|
+
baseName,
|
|
130
|
+
tags,
|
|
131
|
+
source,
|
|
132
|
+
};
|
|
133
|
+
if (extension === "txt")
|
|
134
|
+
parsePlainText(text, common);
|
|
135
|
+
else
|
|
136
|
+
parseMarkdown(text, common);
|
|
137
|
+
return candidates.slice(0, limits.maxPromptCandidatesPerFile);
|
|
153
138
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
|
|
3
|
+
function normalizeWhitespace(text) {
|
|
4
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+\n/g, "\n").trim();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function stripFrontmatter(text) {
|
|
8
|
+
return text.replace(/^---\s*\n[\s\S]*?\n---\s*\n/, "");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isBoilerplate(line) {
|
|
12
|
+
const trimmed = line.trim();
|
|
13
|
+
return (
|
|
14
|
+
!trimmed ||
|
|
15
|
+
/^\[!\[.*\]\(.+\)\]\(.+\)$/.test(trimmed) ||
|
|
16
|
+
/^!\[.*\]\(.+\)$/.test(trimmed) ||
|
|
17
|
+
/^\[.*\]\(.+\)$/.test(trimmed)
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function titleFromFilename(filename) {
|
|
22
|
+
return (filename || "Imported prompt")
|
|
23
|
+
.replace(/\.(txt|md|markdown)$/i, "")
|
|
24
|
+
.replace(/[-_]+/g, " ")
|
|
25
|
+
.trim() || "Imported prompt";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function candidateId(text, ordinal) {
|
|
29
|
+
return `candidate_${ordinal}_${createHash("sha256").update(text).digest("hex").slice(0, 10)}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function promptHash(text) {
|
|
33
|
+
return createHash("sha256").update(normalizeWhitespace(text).toLowerCase()).digest("hex");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function headingName(heading, fallback) {
|
|
37
|
+
return heading?.replace(/^#+\s*/, "").trim() || fallback;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function allowedCandidate(text, limits) {
|
|
41
|
+
const length = text.trim().length;
|
|
42
|
+
return length >= limits.minCandidateChars && length <= limits.maxCandidateChars;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function cleanMarkdownBody(text) {
|
|
46
|
+
return normalizeWhitespace(
|
|
47
|
+
text
|
|
48
|
+
.split("\n")
|
|
49
|
+
.filter((line) => !isBoilerplate(line))
|
|
50
|
+
.filter((line) => !/^\|.*\|$/.test(line.trim()))
|
|
51
|
+
.join("\n"),
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function pushCandidate(candidates, rawText, options) {
|
|
56
|
+
const text = normalizeWhitespace(rawText);
|
|
57
|
+
if (!allowedCandidate(text, options.limits)) return;
|
|
58
|
+
const ordinal = candidates.length + 1;
|
|
59
|
+
const hash = promptHash(text);
|
|
60
|
+
candidates.push({
|
|
61
|
+
id: candidateId(text, ordinal),
|
|
62
|
+
name: options.name,
|
|
63
|
+
text,
|
|
64
|
+
textPreview: text.slice(0, 220),
|
|
65
|
+
tags: [...new Set(options.tags)],
|
|
66
|
+
warnings: options.warnings ?? [],
|
|
67
|
+
source: options.source,
|
|
68
|
+
headingPath: options.headingPath ?? null,
|
|
69
|
+
ordinal,
|
|
70
|
+
promptHash: hash,
|
|
71
|
+
scoreHints: options.scoreHints ?? {},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function parseMarkdown(text, options) {
|
|
76
|
+
const source = stripFrontmatter(text).slice(0, options.limits.maxSourceCharsScanned);
|
|
77
|
+
const fencePattern = /```([A-Za-z0-9_-]*)\n([\s\S]*?)```/g;
|
|
78
|
+
const acceptedFenceLanguages = new Set(["", "prompt", "text", "markdown", "md"]);
|
|
79
|
+
const ranges = [];
|
|
80
|
+
|
|
81
|
+
for (const match of source.matchAll(fencePattern)) {
|
|
82
|
+
const language = (match[1] || "").toLowerCase();
|
|
83
|
+
if (!acceptedFenceLanguages.has(language)) continue;
|
|
84
|
+
ranges.push([match.index ?? 0, (match.index ?? 0) + match[0].length]);
|
|
85
|
+
pushCandidate(options.candidates, match[2], {
|
|
86
|
+
...options,
|
|
87
|
+
name: `${options.baseName} ${options.candidates.length + 1}`,
|
|
88
|
+
headingPath: options.headingPath ?? null,
|
|
89
|
+
});
|
|
90
|
+
if (options.candidates.length >= options.limits.maxPromptCandidatesPerFile) return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const withoutFences = source
|
|
94
|
+
.split("\n")
|
|
95
|
+
.filter((line, index, lines) => {
|
|
96
|
+
const offset = lines.slice(0, index).join("\n").length + (index > 0 ? 1 : 0);
|
|
97
|
+
return !ranges.some(([start, end]) => offset >= start && offset < end);
|
|
98
|
+
})
|
|
99
|
+
.join("\n");
|
|
100
|
+
const sections = withoutFences.split(/(?=^#{1,4}\s+)/gm);
|
|
101
|
+
for (const section of sections) {
|
|
102
|
+
const heading = /^#{1,4}\s+(.+)$/m.exec(section)?.[1];
|
|
103
|
+
const body = cleanMarkdownBody(section.replace(/^#{1,4}\s+.+$/m, ""));
|
|
104
|
+
pushCandidate(options.candidates, body, {
|
|
105
|
+
...options,
|
|
106
|
+
name: headingName(heading, `${options.baseName} ${options.candidates.length + 1}`),
|
|
107
|
+
headingPath: headingName(heading, options.baseName),
|
|
108
|
+
});
|
|
109
|
+
if (options.candidates.length >= options.limits.maxPromptCandidatesPerFile) return;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function splitTextPrompts(text) {
|
|
114
|
+
const normalized = normalizeWhitespace(text);
|
|
115
|
+
const separatorBlocks = normalized.split(/\n\s*---+\s*\n/g).filter(Boolean);
|
|
116
|
+
const blocks = separatorBlocks.length > 1
|
|
117
|
+
? separatorBlocks
|
|
118
|
+
: normalized.split(/\n\s*\n+/g).filter(Boolean);
|
|
119
|
+
const numbered = normalized.split(/(?=^\s*\d+[.)]\s+)/gm).filter(Boolean);
|
|
120
|
+
const longLines = normalized.split("\n").map((line) => line.trim()).filter((line) => line.length >= 80);
|
|
121
|
+
return blocks.length > 1 ? blocks : numbered.length > 1 ? numbered : longLines.length > 1 ? longLines : [normalized];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function parsePlainText(text, options) {
|
|
125
|
+
const chunks = splitTextPrompts(text.slice(0, options.limits.maxSourceCharsScanned));
|
|
126
|
+
for (const chunk of chunks) {
|
|
127
|
+
const clean = chunk.replace(/^\s*\d+[.)]\s+/, "");
|
|
128
|
+
pushCandidate(options.candidates, clean, {
|
|
129
|
+
...options,
|
|
130
|
+
name: `${options.baseName} ${options.candidates.length + 1}`,
|
|
131
|
+
headingPath: `${options.baseName} ${options.candidates.length + 1}`,
|
|
132
|
+
});
|
|
133
|
+
if (options.candidates.length >= options.limits.maxPromptCandidatesPerFile) return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function parsePromptCandidates({ text, filename, source, tags = [], limits }) {
|
|
138
|
+
const candidates = [];
|
|
139
|
+
const extension = (filename.split(".").pop() || "").toLowerCase();
|
|
140
|
+
const baseName = titleFromFilename(filename);
|
|
141
|
+
const common = {
|
|
142
|
+
candidates,
|
|
143
|
+
limits,
|
|
144
|
+
baseName,
|
|
145
|
+
tags,
|
|
146
|
+
source,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
if (extension === "txt") parsePlainText(text, common);
|
|
150
|
+
else parseMarkdown(text, common);
|
|
151
|
+
|
|
152
|
+
return candidates.slice(0, limits.maxPromptCandidatesPerFile);
|
|
153
|
+
}
|