image-sprout 1.0.2 → 1.2.0
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 +111 -3
- package/dist/app/assets/index-BX6u0Mtk.js +3 -0
- package/dist/app/assets/index-Cs3-aG7c.css +1 -0
- package/dist/app/index.html +2 -2
- package/dist/cli/index.js +122 -55
- package/package.json +5 -1
- package/dist/app/assets/index-C7ombVWN.css +0 -1
- package/dist/app/assets/index-Yj_7qKUG.js +0 -3
package/dist/cli/index.js
CHANGED
|
@@ -5,11 +5,14 @@ import path from "node:path";
|
|
|
5
5
|
import { createServer } from "node:http";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
const OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions";
|
|
8
|
-
const
|
|
8
|
+
const DEFAULT_ANALYSIS_MODEL = "google/gemini-3.1-flash-image-preview";
|
|
9
9
|
const REFERER = "https://image-sprout.app";
|
|
10
10
|
const OPENAI_SIZE_MAP = {
|
|
11
11
|
"1:1": "1024x1024",
|
|
12
|
+
"4:3": "1536x1024",
|
|
13
|
+
"3:2": "1536x1024",
|
|
12
14
|
"16:9": "1536x1024",
|
|
15
|
+
"4:5": "1024x1536",
|
|
13
16
|
"9:16": "1024x1536"
|
|
14
17
|
};
|
|
15
18
|
const MAX_REFERENCE_IMAGES = 14;
|
|
@@ -168,7 +171,7 @@ async function generateImages(request, apiKey, signal) {
|
|
|
168
171
|
};
|
|
169
172
|
});
|
|
170
173
|
}
|
|
171
|
-
async function analyzeReferenceImages(imageDataUrls, apiKey, signal) {
|
|
174
|
+
async function analyzeReferenceImages(imageDataUrls, apiKey, signal, analysisModel) {
|
|
172
175
|
validateApiKey(apiKey);
|
|
173
176
|
const cappedImageUrls = capImages(imageDataUrls);
|
|
174
177
|
const imageParts = buildImageParts(cappedImageUrls);
|
|
@@ -187,18 +190,21 @@ visualStyle: Describe ONLY the visual rendering style. Cover color palette, line
|
|
|
187
190
|
subjectGuide: Describe ONLY the recurring subject(s) that appear across images — their physical design, proportions, distinguishing features, and general personality or tone. Focus on who or what should stay consistent across generations. Exclude rendering style, medium, palette, composition, lighting, and other art-direction details, even if they are prominent in the images. Do NOT list specific scenes, actions, settings, or scenarios from the images. These will be provided separately by the user for each new image.`
|
|
188
191
|
};
|
|
189
192
|
const content = [...imageParts, textPart];
|
|
190
|
-
const responseData = await sendRequest(content, ["text"], apiKey,
|
|
193
|
+
const responseData = await sendRequest(content, ["text"], apiKey, analysisModel ?? DEFAULT_ANALYSIS_MODEL, void 0);
|
|
191
194
|
const text = extractTextFromResponse(responseData);
|
|
192
195
|
if (!text) {
|
|
193
196
|
throw new Error("No text response received from reference image analysis");
|
|
194
197
|
}
|
|
198
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
199
|
+
const jsonCandidate = jsonMatch ? jsonMatch[0] : text;
|
|
195
200
|
try {
|
|
196
|
-
const parsed = JSON.parse(
|
|
201
|
+
const parsed = JSON.parse(jsonCandidate);
|
|
197
202
|
return {
|
|
198
203
|
visualStyle: sanitizeGuideText(parsed.visualStyle ?? parsed.styleDescription ?? ""),
|
|
199
204
|
subjectGuide: sanitizeGuideText(parsed.subjectGuide ?? parsed.coreInstruction ?? "")
|
|
200
205
|
};
|
|
201
206
|
} catch {
|
|
207
|
+
console.warn("Warning: analysis model returned non-JSON response; using raw text as visualStyle");
|
|
202
208
|
return { visualStyle: sanitizeGuideText(text), subjectGuide: "" };
|
|
203
209
|
}
|
|
204
210
|
}
|
|
@@ -220,7 +226,8 @@ const BUILTIN_IMAGE_MODELS = [
|
|
|
220
226
|
}
|
|
221
227
|
];
|
|
222
228
|
const DEFAULT_MODEL = BUILTIN_IMAGE_MODELS[0].id;
|
|
223
|
-
const
|
|
229
|
+
const SIZE_PRESETS = ["1:1", "4:3", "3:2", "16:9", "4:5", "9:16"];
|
|
230
|
+
const SUPPORTED_IMAGE_COUNTS = [1, 2, 4, 6];
|
|
224
231
|
const EXIT_CODES = {
|
|
225
232
|
SUCCESS: 0,
|
|
226
233
|
NOT_FOUND: 1,
|
|
@@ -385,14 +392,18 @@ function parseBoolean(input, key) {
|
|
|
385
392
|
const QUICK_HELP = `image-sprout <command> [options]
|
|
386
393
|
|
|
387
394
|
Commands:
|
|
388
|
-
project
|
|
389
|
-
ref
|
|
390
|
-
session
|
|
391
|
-
run
|
|
392
|
-
model
|
|
393
|
-
config
|
|
394
|
-
web
|
|
395
|
-
help
|
|
395
|
+
project Manage projects and generation workflow
|
|
396
|
+
ref Manage project reference images
|
|
397
|
+
session Inspect project sessions
|
|
398
|
+
run Inspect saved runs
|
|
399
|
+
model Manage available image models
|
|
400
|
+
config Manage local configuration
|
|
401
|
+
web Launch the interactive local web app
|
|
402
|
+
help Show command help
|
|
403
|
+
|
|
404
|
+
Aliases:
|
|
405
|
+
generate Shorthand for: project generate
|
|
406
|
+
analyze Shorthand for: project derive
|
|
396
407
|
|
|
397
408
|
Options:
|
|
398
409
|
--json Output JSON
|
|
@@ -438,16 +449,18 @@ Alias for:
|
|
|
438
449
|
const GENERATE_HELP = `image-sprout generate [--project <name-or-id>] --prompt <text>|--prompt-file <path> [options]
|
|
439
450
|
|
|
440
451
|
Alias for:
|
|
441
|
-
image-sprout project generate <project> --prompt <text>|--prompt-file <path> [--session <id>] [--feedback <text>] [--model <id>] [--size <16:9|
|
|
452
|
+
image-sprout project generate <project> --prompt <text>|--prompt-file <path> [--session <id>] [--feedback <text>] [--model <id>] [--size <1:1|4:3|3:2|16:9|4:5|9:16>] [--count <1|2|4|6>] [--force]`;
|
|
442
453
|
const SESSION_HELP = `image-sprout session <subcommand> [options]
|
|
443
454
|
|
|
444
455
|
Subcommands:
|
|
445
456
|
list List sessions for a project
|
|
446
457
|
show Show one session
|
|
458
|
+
delete Delete a session and its runs
|
|
447
459
|
|
|
448
460
|
Examples:
|
|
449
461
|
image-sprout session list --project comic-hero
|
|
450
|
-
image-sprout session show --project comic-hero <session-id
|
|
462
|
+
image-sprout session show --project comic-hero <session-id>
|
|
463
|
+
image-sprout session delete --project comic-hero <session-id>`;
|
|
451
464
|
const RUN_HELP = `image-sprout run <subcommand> [options]
|
|
452
465
|
|
|
453
466
|
Subcommands:
|
|
@@ -459,9 +472,13 @@ Examples:
|
|
|
459
472
|
image-sprout run list --project comic-hero --limit 5
|
|
460
473
|
image-sprout run latest --project comic-hero
|
|
461
474
|
image-sprout run show --project comic-hero <run-id>`;
|
|
462
|
-
const WEB_HELP = `image-sprout web [--port <number>]
|
|
475
|
+
const WEB_HELP = `image-sprout web [--port <number>] [--open]
|
|
476
|
+
|
|
477
|
+
Launch the interactive local web app against the shared on-disk project store.
|
|
463
478
|
|
|
464
|
-
|
|
479
|
+
Options:
|
|
480
|
+
--port <number> Port to listen on (default: 4310)
|
|
481
|
+
--open Open the app in the default browser`;
|
|
465
482
|
const MODELS_HELP = `image-sprout model <subcommand> [options]
|
|
466
483
|
|
|
467
484
|
Subcommands:
|
|
@@ -784,7 +801,7 @@ function restoreDefaultModels() {
|
|
|
784
801
|
writeRegistry(registry);
|
|
785
802
|
return registry;
|
|
786
803
|
}
|
|
787
|
-
const CONFIG_KEYS = /* @__PURE__ */ new Set(["apiKey", "model", "sizePreset", "imageCount"]);
|
|
804
|
+
const CONFIG_KEYS = /* @__PURE__ */ new Set(["apiKey", "model", "sizePreset", "imageCount", "analysisModel"]);
|
|
788
805
|
function defaultConfig() {
|
|
789
806
|
return {
|
|
790
807
|
apiKey: "",
|
|
@@ -825,9 +842,9 @@ function readConfig() {
|
|
|
825
842
|
writeConfig(healed);
|
|
826
843
|
return healed;
|
|
827
844
|
}
|
|
828
|
-
if (!
|
|
845
|
+
if (!SIZE_PRESETS.includes(config.sizePreset)) {
|
|
829
846
|
throw new CliError("CONFIG_ERROR", `Invalid configured sizePreset: ${String(config.sizePreset)}`, [
|
|
830
|
-
|
|
847
|
+
`Use one of: ${SIZE_PRESETS.join(", ")}`
|
|
831
848
|
]);
|
|
832
849
|
}
|
|
833
850
|
if (!SUPPORTED_IMAGE_COUNTS.includes(config.imageCount)) {
|
|
@@ -856,7 +873,7 @@ function normalizeKey(input) {
|
|
|
856
873
|
return mapped;
|
|
857
874
|
}
|
|
858
875
|
throw new CliError("INVALID_ARGS", `Unknown config key: ${input}`, [
|
|
859
|
-
"Valid keys: apiKey, model, sizePreset, imageCount"
|
|
876
|
+
"Valid keys: apiKey, model, sizePreset, imageCount, analysisModel"
|
|
860
877
|
]);
|
|
861
878
|
}
|
|
862
879
|
function validateSetValue(key, rawValue) {
|
|
@@ -872,9 +889,9 @@ function validateSetValue(key, rawValue) {
|
|
|
872
889
|
return rawValue;
|
|
873
890
|
}
|
|
874
891
|
case "sizePreset": {
|
|
875
|
-
if (rawValue
|
|
892
|
+
if (!SIZE_PRESETS.includes(rawValue)) {
|
|
876
893
|
throw new CliError("INVALID_ARGS", `Invalid size preset: ${rawValue}`, [
|
|
877
|
-
|
|
894
|
+
`Use one of: ${SIZE_PRESETS.join(", ")}`
|
|
878
895
|
]);
|
|
879
896
|
}
|
|
880
897
|
return rawValue;
|
|
@@ -882,12 +899,19 @@ function validateSetValue(key, rawValue) {
|
|
|
882
899
|
case "imageCount": {
|
|
883
900
|
const value = Number(rawValue);
|
|
884
901
|
if (!SUPPORTED_IMAGE_COUNTS.includes(value)) {
|
|
885
|
-
throw new CliError("INVALID_ARGS",
|
|
902
|
+
throw new CliError("INVALID_ARGS", `imageCount must be one of: ${SUPPORTED_IMAGE_COUNTS.join(", ")}`, [
|
|
886
903
|
"Example: image-sprout config set imageCount 4"
|
|
887
904
|
]);
|
|
888
905
|
}
|
|
889
906
|
return value;
|
|
890
907
|
}
|
|
908
|
+
case "analysisModel": {
|
|
909
|
+
const trimmed = rawValue.trim();
|
|
910
|
+
if (!trimmed) {
|
|
911
|
+
throw new CliError("INVALID_ARGS", "analysisModel must be a non-empty model ID");
|
|
912
|
+
}
|
|
913
|
+
return trimmed;
|
|
914
|
+
}
|
|
891
915
|
default:
|
|
892
916
|
throw new CliError("INVALID_ARGS", `Unsupported config key: ${String(key)}`);
|
|
893
917
|
}
|
|
@@ -903,7 +927,8 @@ function publicConfig(config) {
|
|
|
903
927
|
apiKeyConfigured: config.apiKey.trim().length > 0,
|
|
904
928
|
model: config.model,
|
|
905
929
|
sizePreset: config.sizePreset,
|
|
906
|
-
imageCount: config.imageCount
|
|
930
|
+
imageCount: config.imageCount,
|
|
931
|
+
...config.analysisModel ? { analysisModel: config.analysisModel } : {}
|
|
907
932
|
};
|
|
908
933
|
}
|
|
909
934
|
function updateConfig(changes) {
|
|
@@ -1666,10 +1691,18 @@ function sendJson(res, status, payload) {
|
|
|
1666
1691
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
1667
1692
|
res.end(JSON.stringify(payload));
|
|
1668
1693
|
}
|
|
1694
|
+
const CLI_ERROR_TO_HTTP = {
|
|
1695
|
+
NOT_FOUND: 404,
|
|
1696
|
+
AUTH_REQUIRED: 401,
|
|
1697
|
+
API_ERROR: 502,
|
|
1698
|
+
IO_ERROR: 500,
|
|
1699
|
+
INTERNAL_ERROR: 500,
|
|
1700
|
+
PROJECT_NOT_READY: 409
|
|
1701
|
+
};
|
|
1669
1702
|
function toApiErrorResponse(error) {
|
|
1670
1703
|
if (error instanceof CliError) {
|
|
1671
1704
|
return {
|
|
1672
|
-
status: error.
|
|
1705
|
+
status: CLI_ERROR_TO_HTTP[error.code] ?? 400,
|
|
1673
1706
|
payload: {
|
|
1674
1707
|
error: {
|
|
1675
1708
|
code: error.code,
|
|
@@ -1961,6 +1994,7 @@ async function routeApiRequest(method, urlInput, body = {}) {
|
|
|
1961
1994
|
const requestBody = body;
|
|
1962
1995
|
const target = parseTarget(requestBody.target);
|
|
1963
1996
|
const persist = requestBody.persist !== false;
|
|
1997
|
+
const analysisModel = config.analysisModel;
|
|
1964
1998
|
const styleRefs = target === "subject" ? [] : getProjectReferenceDataUrlsByTarget(projectId, "style");
|
|
1965
1999
|
const subjectRefs = target === "style" ? [] : getProjectReferenceDataUrlsByTarget(projectId, "subject");
|
|
1966
2000
|
if (target === "both" && (styleRefs.length === 0 || subjectRefs.length === 0)) {
|
|
@@ -1975,18 +2009,22 @@ async function routeApiRequest(method, urlInput, body = {}) {
|
|
|
1975
2009
|
let visualStyle;
|
|
1976
2010
|
let subjectGuide;
|
|
1977
2011
|
if (target === "both" && JSON.stringify(styleRefs) === JSON.stringify(subjectRefs)) {
|
|
1978
|
-
const result = await analyzeReferenceImages(styleRefs, apiKey);
|
|
2012
|
+
const result = await analyzeReferenceImages(styleRefs, apiKey, void 0, analysisModel);
|
|
1979
2013
|
visualStyle = result.visualStyle;
|
|
1980
2014
|
subjectGuide = result.subjectGuide;
|
|
2015
|
+
} else if (target === "both") {
|
|
2016
|
+
const [styleResult, subjectResult] = await Promise.all([
|
|
2017
|
+
analyzeReferenceImages(styleRefs, apiKey, void 0, analysisModel),
|
|
2018
|
+
analyzeReferenceImages(subjectRefs, apiKey, void 0, analysisModel)
|
|
2019
|
+
]);
|
|
2020
|
+
visualStyle = styleResult.visualStyle;
|
|
2021
|
+
subjectGuide = subjectResult.subjectGuide;
|
|
2022
|
+
} else if (target === "style") {
|
|
2023
|
+
const result = await analyzeReferenceImages(styleRefs, apiKey, void 0, analysisModel);
|
|
2024
|
+
visualStyle = result.visualStyle;
|
|
1981
2025
|
} else {
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
visualStyle = result.visualStyle;
|
|
1985
|
-
}
|
|
1986
|
-
if (target === "subject" || target === "both") {
|
|
1987
|
-
const result = await analyzeReferenceImages(subjectRefs, apiKey);
|
|
1988
|
-
subjectGuide = result.subjectGuide;
|
|
1989
|
-
}
|
|
2026
|
+
const result = await analyzeReferenceImages(subjectRefs, apiKey, void 0, analysisModel);
|
|
2027
|
+
subjectGuide = result.subjectGuide;
|
|
1990
2028
|
}
|
|
1991
2029
|
if (persist) {
|
|
1992
2030
|
updateProjectGuides(projectId, {
|
|
@@ -1994,7 +2032,7 @@ async function routeApiRequest(method, urlInput, body = {}) {
|
|
|
1994
2032
|
...subjectGuide !== void 0 ? { subjectGuide } : {}
|
|
1995
2033
|
});
|
|
1996
2034
|
}
|
|
1997
|
-
const status =
|
|
2035
|
+
const status = getProjectStatus(projectId);
|
|
1998
2036
|
const project = showProject(projectId);
|
|
1999
2037
|
return {
|
|
2000
2038
|
status: 200,
|
|
@@ -2121,8 +2159,8 @@ async function startWebServer(options = {}) {
|
|
|
2121
2159
|
}
|
|
2122
2160
|
const GLOBAL_OPTIONS = /* @__PURE__ */ new Set(["json", "text", "help", "version", "value", "ids", "limit"]);
|
|
2123
2161
|
function resolveVersion() {
|
|
2124
|
-
if ("1.0
|
|
2125
|
-
return "1.0
|
|
2162
|
+
if ("1.2.0".length > 0) {
|
|
2163
|
+
return "1.2.0";
|
|
2126
2164
|
}
|
|
2127
2165
|
return process.env.npm_package_version ?? "0.0.0";
|
|
2128
2166
|
}
|
|
@@ -2304,10 +2342,10 @@ function parseSize(input, fallback) {
|
|
|
2304
2342
|
if (!input) {
|
|
2305
2343
|
return fallback;
|
|
2306
2344
|
}
|
|
2307
|
-
if (input
|
|
2345
|
+
if (SIZE_PRESETS.includes(input)) {
|
|
2308
2346
|
return input;
|
|
2309
2347
|
}
|
|
2310
|
-
throw new CliError("INVALID_ARGS", `Invalid size: ${input}`, [
|
|
2348
|
+
throw new CliError("INVALID_ARGS", `Invalid size: ${input}`, [`Use one of: ${SIZE_PRESETS.join(", ")}`]);
|
|
2311
2349
|
}
|
|
2312
2350
|
function parseCount(input, fallback) {
|
|
2313
2351
|
if (!input) {
|
|
@@ -2315,7 +2353,7 @@ function parseCount(input, fallback) {
|
|
|
2315
2353
|
}
|
|
2316
2354
|
const value = Number(input);
|
|
2317
2355
|
if (!SUPPORTED_IMAGE_COUNTS.includes(value)) {
|
|
2318
|
-
throw new CliError("INVALID_ARGS",
|
|
2356
|
+
throw new CliError("INVALID_ARGS", `count must be one of: ${SUPPORTED_IMAGE_COUNTS.join(", ")}`, [
|
|
2319
2357
|
"Example: image-sprout generate --count 4 ..."
|
|
2320
2358
|
]);
|
|
2321
2359
|
}
|
|
@@ -2608,10 +2646,12 @@ function runRef(ctx, args, positionals) {
|
|
|
2608
2646
|
}
|
|
2609
2647
|
}
|
|
2610
2648
|
async function runProjectDerive(ctx, args, projectInput) {
|
|
2611
|
-
assertOnlyOptions(args, /* @__PURE__ */ new Set(["project", "api-key", "target"]));
|
|
2649
|
+
assertOnlyOptions(args, /* @__PURE__ */ new Set(["project", "api-key", "target", "analysis-model"]));
|
|
2650
|
+
const config = readConfig();
|
|
2612
2651
|
const projectId = resolveProjectId(projectInput ?? projectFlag(args));
|
|
2613
2652
|
const apiKey = requireApiKey(getStringOption(args, "api-key"));
|
|
2614
2653
|
const target = parseDeriveTarget(getStringOption(args, "target"));
|
|
2654
|
+
const analysisModel = getStringOption(args, "analysis-model") ?? config.analysisModel;
|
|
2615
2655
|
const styleRefDataUrls = target === "subject" ? [] : getProjectReferenceDataUrlsByTarget(projectId, "style");
|
|
2616
2656
|
const subjectRefDataUrls = target === "style" ? [] : getProjectReferenceDataUrlsByTarget(projectId, "subject");
|
|
2617
2657
|
if (target === "style" && styleRefDataUrls.length === 0) {
|
|
@@ -2636,18 +2676,22 @@ async function runProjectDerive(ctx, args, projectInput) {
|
|
|
2636
2676
|
let nextStyle;
|
|
2637
2677
|
let nextSubject;
|
|
2638
2678
|
if (target === "both" && JSON.stringify(styleRefDataUrls) === JSON.stringify(subjectRefDataUrls)) {
|
|
2639
|
-
const analysis = await analyzeReferenceImages(styleRefDataUrls, apiKey);
|
|
2679
|
+
const analysis = await analyzeReferenceImages(styleRefDataUrls, apiKey, void 0, analysisModel);
|
|
2640
2680
|
nextStyle = analysis.visualStyle;
|
|
2641
2681
|
nextSubject = analysis.subjectGuide;
|
|
2682
|
+
} else if (target === "both") {
|
|
2683
|
+
const [styleAnalysis, subjectAnalysis] = await Promise.all([
|
|
2684
|
+
analyzeReferenceImages(styleRefDataUrls, apiKey, void 0, analysisModel),
|
|
2685
|
+
analyzeReferenceImages(subjectRefDataUrls, apiKey, void 0, analysisModel)
|
|
2686
|
+
]);
|
|
2687
|
+
nextStyle = styleAnalysis.visualStyle;
|
|
2688
|
+
nextSubject = subjectAnalysis.subjectGuide;
|
|
2689
|
+
} else if (target === "style") {
|
|
2690
|
+
const analysis = await analyzeReferenceImages(styleRefDataUrls, apiKey, void 0, analysisModel);
|
|
2691
|
+
nextStyle = analysis.visualStyle;
|
|
2642
2692
|
} else {
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
nextStyle = analysis.visualStyle;
|
|
2646
|
-
}
|
|
2647
|
-
if (target === "subject" || target === "both") {
|
|
2648
|
-
const analysis = await analyzeReferenceImages(subjectRefDataUrls, apiKey);
|
|
2649
|
-
nextSubject = analysis.subjectGuide;
|
|
2650
|
-
}
|
|
2693
|
+
const analysis = await analyzeReferenceImages(subjectRefDataUrls, apiKey, void 0, analysisModel);
|
|
2694
|
+
nextSubject = analysis.subjectGuide;
|
|
2651
2695
|
}
|
|
2652
2696
|
updateProjectGuides(projectId, {
|
|
2653
2697
|
...nextStyle !== void 0 ? { visualStyle: nextStyle } : {},
|
|
@@ -2758,7 +2802,7 @@ async function runProjectGenerate(ctx, args, projectInput) {
|
|
|
2758
2802
|
function runSessions(ctx, args, positionals) {
|
|
2759
2803
|
assertOnlyOptions(args, /* @__PURE__ */ new Set(["project"]));
|
|
2760
2804
|
const projectId = resolveProjectId(projectFlag(args));
|
|
2761
|
-
const sub = requireSubcommand("session", positionals[1] ?? "list", ["list", "show"]);
|
|
2805
|
+
const sub = requireSubcommand("session", positionals[1] ?? "list", ["list", "show", "delete"]);
|
|
2762
2806
|
switch (sub) {
|
|
2763
2807
|
case "list": {
|
|
2764
2808
|
const data = limitItems(listSessions(projectId), ctx);
|
|
@@ -2787,6 +2831,15 @@ function runSessions(ctx, args, positionals) {
|
|
|
2787
2831
|
});
|
|
2788
2832
|
return;
|
|
2789
2833
|
}
|
|
2834
|
+
case "delete": {
|
|
2835
|
+
const sessionId = positionals[2];
|
|
2836
|
+
if (!sessionId) {
|
|
2837
|
+
throw new CliError("INVALID_ARGS", "session delete requires <session-id>");
|
|
2838
|
+
}
|
|
2839
|
+
deleteSession(projectId, sessionId);
|
|
2840
|
+
emitSuccess(ctx, { projectId, sessionId }, (payload) => `ok project=${payload.projectId} deleted=${payload.sessionId}`);
|
|
2841
|
+
return;
|
|
2842
|
+
}
|
|
2790
2843
|
}
|
|
2791
2844
|
}
|
|
2792
2845
|
function runRuns(ctx, args, positionals) {
|
|
@@ -2944,13 +2997,17 @@ function runConfig(ctx, args, positionals) {
|
|
|
2944
2997
|
config: data
|
|
2945
2998
|
},
|
|
2946
2999
|
(payload) => {
|
|
2947
|
-
|
|
3000
|
+
const lines = [
|
|
2948
3001
|
`ok path=${payload.path}`,
|
|
2949
3002
|
`apiKey=${payload.config.apiKeyConfigured ? "[set]" : "[empty]"}`,
|
|
2950
3003
|
`model=${payload.config.model}`,
|
|
2951
3004
|
`sizePreset=${payload.config.sizePreset}`,
|
|
2952
3005
|
`imageCount=${payload.config.imageCount}`
|
|
2953
|
-
]
|
|
3006
|
+
];
|
|
3007
|
+
if (payload.config.analysisModel) {
|
|
3008
|
+
lines.push(`analysisModel=${payload.config.analysisModel}`);
|
|
3009
|
+
}
|
|
3010
|
+
return lines.join("\n");
|
|
2954
3011
|
}
|
|
2955
3012
|
);
|
|
2956
3013
|
return;
|
|
@@ -3021,14 +3078,24 @@ function runHelp(ctx, target) {
|
|
|
3021
3078
|
function runVersion(ctx) {
|
|
3022
3079
|
emitSuccess(ctx, { version: ctx.version }, (payload) => payload.version);
|
|
3023
3080
|
}
|
|
3081
|
+
async function openInBrowser(url) {
|
|
3082
|
+
const { platform } = process;
|
|
3083
|
+
const { exec } = await import("node:child_process");
|
|
3084
|
+
const cmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
|
|
3085
|
+
exec(`${cmd} ${url}`);
|
|
3086
|
+
}
|
|
3024
3087
|
async function runWeb(ctx, args) {
|
|
3025
|
-
assertOnlyOptions(args, /* @__PURE__ */ new Set(["port"]));
|
|
3088
|
+
assertOnlyOptions(args, /* @__PURE__ */ new Set(["port", "open"]));
|
|
3026
3089
|
const rawPort = getStringOption(args, "port");
|
|
3027
3090
|
const parsedPort = rawPort ? Number(rawPort) : void 0;
|
|
3028
3091
|
if (rawPort && (parsedPort === void 0 || !Number.isInteger(parsedPort) || parsedPort < 1 || parsedPort > 65535)) {
|
|
3029
3092
|
throw new CliError("INVALID_ARGS", `Invalid port: ${rawPort}`);
|
|
3030
3093
|
}
|
|
3094
|
+
const shouldOpen = getBooleanOption(args, "open") === true;
|
|
3031
3095
|
const server = await startWebServer({ port: parsedPort });
|
|
3096
|
+
if (shouldOpen) {
|
|
3097
|
+
await openInBrowser(server.url);
|
|
3098
|
+
}
|
|
3032
3099
|
emitSuccess(ctx, server, (payload) => `ok url=${payload.url}`);
|
|
3033
3100
|
}
|
|
3034
3101
|
async function dispatch(args, ctx) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "image-sprout",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"image-sprout": "dist/cli/index.js"
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
"files": [
|
|
9
9
|
"dist/"
|
|
10
10
|
],
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/tmchow/image-sprout"
|
|
14
|
+
},
|
|
11
15
|
"publishConfig": {
|
|
12
16
|
"access": "public"
|
|
13
17
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:"Inter", ui-sans-serif, system-ui, sans-serif;--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-green-300:oklch(87.1% .15 154.449);--color-emerald-700:oklch(50.8% .118 165.612);--color-sky-50:oklch(97.7% .013 236.62);--color-sky-400:oklch(74.6% .16 232.661);--color-indigo-50:oklch(96.2% .018 272.314);--color-indigo-100:oklch(93% .034 272.788);--color-indigo-200:oklch(87% .065 274.039);--color-indigo-500:oklch(58.5% .233 277.117);--color-indigo-600:oklch(51.1% .262 276.966);--color-indigo-700:oklch(45.7% .24 277.023);--color-slate-50:oklch(98.4% .003 247.858);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-600:oklch(44.6% .043 257.281);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-800:oklch(27.9% .041 260.031);--color-slate-900:oklch(20.8% .042 265.755);--color-slate-950:oklch(12.9% .042 264.695);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--container-6xl:72rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-accent-50:#eef2ff;--color-accent-500:#6366f1;--color-accent-600:#4f46e5;--color-accent-700:#4338ca}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.inset-0{inset:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.end{inset-inline-end:var(--spacing)}.top-1\.5{top:calc(var(--spacing) * 1.5)}.top-2{top:calc(var(--spacing) * 2)}.top-3{top:calc(var(--spacing) * 3)}.right-0{right:calc(var(--spacing) * 0)}.right-1\.5{right:calc(var(--spacing) * 1.5)}.right-2{right:calc(var(--spacing) * 2)}.right-3{right:calc(var(--spacing) * 3)}.bottom-0{bottom:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.left-2{left:calc(var(--spacing) * 2)}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.z-\[60\]{z-index:60}.z-\[70\]{z-index:70}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.mx-4{margin-inline:calc(var(--spacing) * 4)}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mr-auto{margin-right:auto}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-auto{margin-left:auto}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.aspect-square{aspect-ratio:1}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-\[44px\]{height:44px}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-\[80vh\]{max-height:80vh}.max-h-\[85vh\]{max-height:85vh}.max-h-\[88vh\]{max-height:88vh}.max-h-\[90vh\]{max-height:90vh}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-10{min-height:calc(var(--spacing) * 10)}.min-h-28{min-height:calc(var(--spacing) * 28)}.min-h-44{min-height:calc(var(--spacing) * 44)}.min-h-screen{min-height:100vh}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-10{width:calc(var(--spacing) * 10)}.w-32{width:calc(var(--spacing) * 32)}.w-44{width:calc(var(--spacing) * 44)}.w-auto{width:auto}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-7xl{max-width:var(--container-7xl)}.max-w-64{max-width:calc(var(--spacing) * 64)}.max-w-full{max-width:100%}.max-w-md{max-width:var(--container-md)}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.rotate-180{rotate:180deg}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.resize-y{resize:vertical}.appearance-none{appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.self-start{align-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\[4px\]{border-radius:4px}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-b-xl{border-bottom-right-radius:var(--radius-xl);border-bottom-left-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-r-2{border-right-style:var(--tw-border-style);border-right-width:2px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-accent-500{border-color:var(--color-accent-500)}.border-amber-200{border-color:var(--color-amber-200)}.border-amber-400{border-color:var(--color-amber-400)}.border-indigo-200{border-color:var(--color-indigo-200)}.border-indigo-500{border-color:var(--color-indigo-500)}.border-red-200{border-color:var(--color-red-200)}.border-sky-400{border-color:var(--color-sky-400)}.border-slate-100{border-color:var(--color-slate-100)}.border-slate-200{border-color:var(--color-slate-200)}.border-slate-300{border-color:var(--color-slate-300)}.border-t-indigo-600{border-top-color:var(--color-indigo-600)}.bg-accent-50{background-color:var(--color-accent-50)}.bg-accent-600{background-color:var(--color-accent-600)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-50\/70{background-color:#fffbebb3}@supports (color:color-mix(in lab,red,red)){.bg-amber-50\/70{background-color:color-mix(in oklab,var(--color-amber-50) 70%,transparent)}}.bg-amber-600{background-color:var(--color-amber-600)}.bg-black\/40{background-color:#0006}@supports (color:color-mix(in lab,red,red)){.bg-black\/40{background-color:color-mix(in oklab,var(--color-black) 40%,transparent)}}.bg-black\/45{background-color:#00000073}@supports (color:color-mix(in lab,red,red)){.bg-black\/45{background-color:color-mix(in oklab,var(--color-black) 45%,transparent)}}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black) 60%,transparent)}}.bg-black\/70{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.bg-black\/70{background-color:color-mix(in oklab,var(--color-black) 70%,transparent)}}.bg-indigo-50{background-color:var(--color-indigo-50)}.bg-indigo-100{background-color:var(--color-indigo-100)}.bg-indigo-600{background-color:var(--color-indigo-600)}.bg-red-100{background-color:var(--color-red-100)}.bg-sky-50\/80{background-color:#f0f9ffcc}@supports (color:color-mix(in lab,red,red)){.bg-sky-50\/80{background-color:color-mix(in oklab,var(--color-sky-50) 80%,transparent)}}.bg-slate-50{background-color:var(--color-slate-50)}.bg-slate-100{background-color:var(--color-slate-100)}.bg-slate-200{background-color:var(--color-slate-200)}.bg-slate-900{background-color:var(--color-slate-900)}.bg-slate-950{background-color:var(--color-slate-950)}.bg-slate-950\/75{background-color:#020618bf}@supports (color:color-mix(in lab,red,red)){.bg-slate-950\/75{background-color:color-mix(in oklab,var(--color-slate-950) 75%,transparent)}}.bg-white{background-color:var(--color-white)}.bg-white\/20{background-color:#fff3}@supports (color:color-mix(in lab,red,red)){.bg-white\/20{background-color:color-mix(in oklab,var(--color-white) 20%,transparent)}}.bg-white\/80{background-color:#fffc}@supports (color:color-mix(in lab,red,red)){.bg-white\/80{background-color:color-mix(in oklab,var(--color-white) 80%,transparent)}}.bg-white\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab,red,red)){.bg-white\/90{background-color:color-mix(in oklab,var(--color-white) 90%,transparent)}}.bg-white\/96{background-color:#fffffff5}@supports (color:color-mix(in lab,red,red)){.bg-white\/96{background-color:color-mix(in oklab,var(--color-white) 96%,transparent)}}.bg-gradient-to-br{--tw-gradient-position:to bottom right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-\[linear-gradient\(180deg\,\#fffdf7_0\%\,\#ffffff_18\%\,\#f8fafc_100\%\)\]{background-image:linear-gradient(#fffdf7,#fff 18%,#f8fafc)}.bg-\[url\(\'data\:image\/svg\+xml\;charset\=utf-8\,\%3Csvg\%20xmlns\%3D\%22http\%3A\%2F\%2Fwww\.w3\.org\%2F2000\%2Fsvg\%22\%20width\%3D\%2212\%22\%20height\%3D\%2212\%22\%20viewBox\%3D\%220\%200\%2024\%2024\%22\%20fill\%3D\%22none\%22\%20stroke\%3D\%22\%2364748b\%22\%20stroke-width\%3D\%222\%22\%3E\%3Cpath\%20d\%3D\%22M6\%209l6\%206\%206-6\%22\%2F\%3E\%3C\%2Fsvg\%3E\'\)\]{background-image:url(data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%2212%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%2364748b%22%20stroke-width%3D%222%22%3E%3Cpath%20d%3D%22M6%209l6%206%206-6%22%2F%3E%3C%2Fsvg%3E)}.from-slate-200{--tw-gradient-from:var(--color-slate-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.via-slate-100{--tw-gradient-via:var(--color-slate-100);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.to-slate-200{--tw-gradient-to:var(--color-slate-200);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.bg-\[length\:12px\]{background-size:12px}.bg-\[right_8px_center\]{background-position:right 8px center}.bg-no-repeat{background-repeat:no-repeat}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-1{padding:calc(var(--spacing) * 1)}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.pt-0\.5{padding-top:calc(var(--spacing) * .5)}.pr-6{padding-right:calc(var(--spacing) * 6)}.pb-1{padding-bottom:calc(var(--spacing) * 1)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[0\.14em\]{--tw-tracking:.14em;letter-spacing:.14em}.tracking-\[0\.16em\]{--tw-tracking:.16em;letter-spacing:.16em}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent-600{color:var(--color-accent-600)}.text-amber-600{color:var(--color-amber-600)}.text-amber-700{color:var(--color-amber-700)}.text-amber-800{color:var(--color-amber-800)}.text-emerald-700{color:var(--color-emerald-700)}.text-green-300{color:var(--color-green-300)}.text-indigo-600{color:var(--color-indigo-600)}.text-indigo-700{color:var(--color-indigo-700)}.text-red-300{color:var(--color-red-300)}.text-red-600{color:var(--color-red-600)}.text-slate-300{color:var(--color-slate-300)}.text-slate-400{color:var(--color-slate-400)}.text-slate-500{color:var(--color-slate-500)}.text-slate-600{color:var(--color-slate-600)}.text-slate-700{color:var(--color-slate-700)}.text-slate-800{color:var(--color-slate-800)}.text-slate-900{color:var(--color-slate-900)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.underline{text-decoration-line:underline}.decoration-amber-300{-webkit-text-decoration-color:var(--color-amber-300);text-decoration-color:var(--color-amber-300)}.underline-offset-2{text-underline-offset:2px}.placeholder-slate-400::placeholder{color:var(--color-slate-400)}.opacity-0{opacity:0}.opacity-45{opacity:.45}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring,.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-indigo-600{--tw-ring-color:var(--color-indigo-600)}.ring-slate-200{--tw-ring-color:var(--color-slate-200)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.select-none{-webkit-user-select:none;user-select:none}.group-focus-within\:opacity-100:is(:where(.group):focus-within *){opacity:1}@media(hover:hover){.group-hover\:text-slate-700:is(:where(.group):hover *){color:var(--color-slate-700)}.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\:text-slate-400::placeholder{color:var(--color-slate-400)}@media(hover:hover){.hover\:border-amber-400:hover{border-color:var(--color-amber-400)}.hover\:bg-accent-700:hover{background-color:var(--color-accent-700)}.hover\:bg-amber-700:hover{background-color:var(--color-amber-700)}.hover\:bg-black\/70:hover{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.hover\:bg-black\/70:hover{background-color:color-mix(in oklab,var(--color-black) 70%,transparent)}}.hover\:bg-indigo-50:hover{background-color:var(--color-indigo-50)}.hover\:bg-indigo-700:hover{background-color:var(--color-indigo-700)}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-slate-50:hover{background-color:var(--color-slate-50)}.hover\:bg-slate-100:hover{background-color:var(--color-slate-100)}.hover\:bg-slate-300:hover{background-color:var(--color-slate-300)}.hover\:bg-slate-800:hover{background-color:var(--color-slate-800)}.hover\:bg-white:hover{background-color:var(--color-white)}.hover\:bg-white\/40:hover{background-color:#fff6}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/40:hover{background-color:color-mix(in oklab,var(--color-white) 40%,transparent)}}.hover\:text-accent-600:hover{color:var(--color-accent-600)}.hover\:text-accent-700:hover{color:var(--color-accent-700)}.hover\:text-amber-600:hover{color:var(--color-amber-600)}.hover\:text-amber-900:hover{color:var(--color-amber-900)}.hover\:text-red-500:hover{color:var(--color-red-500)}.hover\:text-red-600:hover{color:var(--color-red-600)}.hover\:text-red-700:hover{color:var(--color-red-700)}.hover\:text-slate-600:hover{color:var(--color-slate-600)}.hover\:text-slate-700:hover{color:var(--color-slate-700)}.hover\:text-slate-800:hover{color:var(--color-slate-800)}}.focus\:border-amber-500:focus{border-color:var(--color-amber-500)}.focus\:border-indigo-500:focus{border-color:var(--color-indigo-500)}.focus\:border-transparent:focus{border-color:#0000}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-accent-500:focus{--tw-ring-color:var(--color-accent-500)}.focus\:ring-amber-500:focus{--tw-ring-color:var(--color-amber-500)}.focus\:ring-indigo-500:focus{--tw-ring-color:var(--color-indigo-500)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:40rem){.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-\[1\.8fr_1fr_auto\]{grid-template-columns:1.8fr 1fr auto}}@media(min-width:64rem){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:80rem){.xl\:col-span-2{grid-column:span 2/span 2}.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}}body{font-family:var(--font-sans);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin:0}#app{width:100%;min-height:100vh}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"<length-percentage>";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"<length-percentage>";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"<length-percentage>";inherits:false;initial-value:100%}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}
|