@mevdragon/vidfarm-devcli 0.2.12 → 0.2.14
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/.env.example +37 -1
- package/README.md +27 -183
- package/SKILL.director.md +293 -0
- package/SKILL.platform.md +312 -0
- package/auto-create-hyperframe-templates/AUTO_CREATE_HYPERFRAME_TEMPLATES.md +275 -0
- package/auto-create-hyperframe-templates/extractor-system-prompt.md +128 -0
- package/auto-create-hyperframe-templates/input-manifest.schema.json +56 -0
- package/auto-create-hyperframe-templates/lambda-memory-estimator.md +118 -0
- package/auto-create-hyperframe-templates/production-graph.schema.json +314 -0
- package/auto-create-hyperframe-templates/runbook.md +198 -0
- package/auto-create-hyperframe-templates/scripts/create-hyperframe-template.mjs +368 -0
- package/auto-create-hyperframe-templates/template-plan.schema.json +311 -0
- package/auto-create-hyperframe-templates/template-planner-prompt.md +144 -0
- package/auto-create-templates/AUTO_CREATE_TEMPLATES.md +116 -0
- package/auto-create-templates/extractor-system-prompt.md +6 -1
- package/auto-create-templates/template-plan.schema.json +73 -2
- package/auto-create-templates/template-planner-prompt.md +62 -1
- package/dist/src/account-pages-legacy.js +363 -89
- package/dist/src/account-pages-legacy.js.map +1 -0
- package/dist/src/account-pages.js +3 -2
- package/dist/src/account-pages.js.map +1 -0
- package/dist/src/app.js +7096 -1294
- package/dist/src/app.js.map +1 -0
- package/dist/src/cli.js +115 -29
- package/dist/src/cli.js.map +1 -0
- package/dist/src/composition-runtime.js +581 -0
- package/dist/src/composition-runtime.js.map +1 -0
- package/dist/src/config.js +69 -11
- package/dist/src/config.js.map +1 -0
- package/dist/src/context.js +280 -21
- package/dist/src/context.js.map +1 -0
- package/dist/src/dev-app-legacy.js +18 -17
- package/dist/src/dev-app-legacy.js.map +1 -0
- package/dist/src/dev-app.js +1 -0
- package/dist/src/dev-app.js.map +1 -0
- package/dist/src/dev-serve.js +169 -0
- package/dist/src/dev-serve.js.map +1 -0
- package/dist/src/domain.js +2 -1
- package/dist/src/domain.js.map +1 -0
- package/dist/src/editor-chat-history.js +10 -0
- package/dist/src/editor-chat-history.js.map +1 -0
- package/dist/src/editor-chat.js +135 -25
- package/dist/src/editor-chat.js.map +1 -0
- package/dist/src/editor-dark-theme.js +1128 -0
- package/dist/src/editor-dark-theme.js.map +1 -0
- package/dist/src/frontend/flockposter-cache-store.js +10 -2
- package/dist/src/frontend/flockposter-cache-store.js.map +1 -0
- package/dist/src/frontend/homepage-client.js +6 -3
- package/dist/src/frontend/homepage-client.js.map +1 -0
- package/dist/src/frontend/homepage-shared.js +1 -0
- package/dist/src/frontend/homepage-shared.js.map +1 -0
- package/dist/src/frontend/homepage-store.js +1 -0
- package/dist/src/frontend/homepage-store.js.map +1 -0
- package/dist/src/frontend/homepage-view.js +131 -18
- package/dist/src/frontend/homepage-view.js.map +1 -0
- package/dist/src/frontend/page-runtime-client.js +27 -3
- package/dist/src/frontend/page-runtime-client.js.map +1 -0
- package/dist/src/frontend/page-runtime-store.js +1 -0
- package/dist/src/frontend/page-runtime-store.js.map +1 -0
- package/dist/src/frontend/sentry.js +42 -0
- package/dist/src/frontend/sentry.js.map +1 -0
- package/dist/src/frontend/template-editor-chat.js +1711 -212
- package/dist/src/frontend/template-editor-chat.js.map +1 -0
- package/dist/src/help-page.js +333 -0
- package/dist/src/help-page.js.map +1 -0
- package/dist/src/homepage.js +169 -47
- package/dist/src/homepage.js.map +1 -0
- package/dist/src/hyperframes/composition.js +180 -0
- package/dist/src/hyperframes/composition.js.map +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/instrument.js +30 -0
- package/dist/src/instrument.js.map +1 -0
- package/dist/src/lib/crypto.js +1 -0
- package/dist/src/lib/crypto.js.map +1 -0
- package/dist/src/lib/dev-log.js +54 -0
- package/dist/src/lib/dev-log.js.map +1 -0
- package/dist/src/lib/display-name.js +11 -0
- package/dist/src/lib/display-name.js.map +1 -0
- package/dist/src/lib/ids.js +21 -1
- package/dist/src/lib/ids.js.map +1 -0
- package/dist/src/lib/images.js +1 -0
- package/dist/src/lib/images.js.map +1 -0
- package/dist/src/lib/json.js +1 -0
- package/dist/src/lib/json.js.map +1 -0
- package/dist/src/lib/template-dna.js +1 -0
- package/dist/src/lib/template-dna.js.map +1 -0
- package/dist/src/lib/template-paths.js +1 -0
- package/dist/src/lib/template-paths.js.map +1 -0
- package/dist/src/lib/template-style-options.js +29 -3
- package/dist/src/lib/template-style-options.js.map +1 -0
- package/dist/src/lib/time.js +1 -0
- package/dist/src/lib/time.js.map +1 -0
- package/dist/src/lib/video-quality-harness.js +60 -0
- package/dist/src/lib/video-quality-harness.js.map +1 -0
- package/dist/src/page-runtime.js +1 -0
- package/dist/src/page-runtime.js.map +1 -0
- package/dist/src/page-shell.js +403 -29
- package/dist/src/page-shell.js.map +1 -0
- package/dist/src/primitive-context.js +144 -8
- package/dist/src/primitive-context.js.map +1 -0
- package/dist/src/primitive-registry.js +1919 -78
- package/dist/src/primitive-registry.js.map +1 -0
- package/dist/src/primitive-sdk.js +1 -0
- package/dist/src/primitive-sdk.js.map +1 -0
- package/dist/src/primitives/remotion/html-image.js +3 -1
- package/dist/src/primitives/remotion/html-image.js.map +1 -0
- package/dist/src/primitives/remotion/media-slideshow.js +60 -0
- package/dist/src/primitives/remotion/media-slideshow.js.map +1 -0
- package/dist/src/react-page-shell.js +1 -0
- package/dist/src/react-page-shell.js.map +1 -0
- package/dist/src/ready-post-schedule-component.js +1 -0
- package/dist/src/ready-post-schedule-component.js.map +1 -0
- package/dist/src/registry.js +237 -24
- package/dist/src/registry.js.map +1 -0
- package/dist/src/runtime.js +3 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/services/api-call-history.js +4 -0
- package/dist/src/services/api-call-history.js.map +1 -0
- package/dist/src/services/auth.js +40 -50
- package/dist/src/services/auth.js.map +1 -0
- package/dist/src/services/billing.js +20 -44
- package/dist/src/services/billing.js.map +1 -0
- package/dist/src/services/chat-threads.js +10 -6
- package/dist/src/services/chat-threads.js.map +1 -0
- package/dist/src/services/fork-access.js +93 -0
- package/dist/src/services/fork-access.js.map +1 -0
- package/dist/src/services/fork-manifest.js +43 -0
- package/dist/src/services/fork-manifest.js.map +1 -0
- package/dist/src/services/ghostcut.js +132 -0
- package/dist/src/services/ghostcut.js.map +1 -0
- package/dist/src/services/hyperframes.js +1014 -0
- package/dist/src/services/hyperframes.js.map +1 -0
- package/dist/src/services/job-capacity.js +14 -0
- package/dist/src/services/job-capacity.js.map +1 -0
- package/dist/src/services/job-logs.js +4 -0
- package/dist/src/services/job-logs.js.map +1 -0
- package/dist/src/services/jobs.js +99 -91
- package/dist/src/services/jobs.js.map +1 -0
- package/dist/src/services/media-processing.js +743 -0
- package/dist/src/services/media-processing.js.map +1 -0
- package/dist/src/services/primitive-media-lambda.js +280 -0
- package/dist/src/services/primitive-media-lambda.js.map +1 -0
- package/dist/src/services/providers.js +1560 -178
- package/dist/src/services/providers.js.map +1 -0
- package/dist/src/services/rate-limits.js +3 -2
- package/dist/src/services/rate-limits.js.map +1 -0
- package/dist/src/services/remotion.js +495 -92
- package/dist/src/services/remotion.js.map +1 -0
- package/dist/src/services/serverless-auth.js +374 -0
- package/dist/src/services/serverless-auth.js.map +1 -0
- package/dist/src/services/serverless-jobs.js +1074 -0
- package/dist/src/services/serverless-jobs.js.map +1 -0
- package/dist/src/services/serverless-provider-keys.js +401 -0
- package/dist/src/services/serverless-provider-keys.js.map +1 -0
- package/dist/src/services/serverless-records.js +1088 -0
- package/dist/src/services/serverless-records.js.map +1 -0
- package/dist/src/services/serverless-template-configs.js +67 -0
- package/dist/src/services/serverless-template-configs.js.map +1 -0
- package/dist/src/services/storage.js +171 -35
- package/dist/src/services/storage.js.map +1 -0
- package/dist/src/services/template-certification.js +295 -3
- package/dist/src/services/template-certification.js.map +1 -0
- package/dist/src/services/template-loader.js +45 -1
- package/dist/src/services/template-loader.js.map +1 -0
- package/dist/src/services/template-runtime-bundles.js +217 -0
- package/dist/src/services/template-runtime-bundles.js.map +1 -0
- package/dist/src/services/template-sources.js +452 -87
- package/dist/src/services/template-sources.js.map +1 -0
- package/dist/src/services/video-normalization.js +2 -0
- package/dist/src/services/video-normalization.js.map +1 -0
- package/dist/src/services/webhooks.js +7 -6
- package/dist/src/services/webhooks.js.map +1 -0
- package/dist/src/template-editor-pages.js +2051 -1869
- package/dist/src/template-editor-pages.js.map +1 -0
- package/dist/src/template-editor-shell.js +1376 -181
- package/dist/src/template-editor-shell.js.map +1 -0
- package/dist/src/template-sdk.js +1 -0
- package/dist/src/template-sdk.js.map +1 -0
- package/dist/src/worker.js +11 -226
- package/dist/src/worker.js.map +1 -0
- package/package.json +47 -12
- package/public/assets/homepage-client-app.js +34 -8
- package/public/assets/page-runtime-client-app.js +44 -18
- package/readme.secret.md +89 -0
- package/templates/vidfarm_template_0000/README.md +106 -0
- package/templates/vidfarm_template_0000/SKILL.md +266 -0
- package/templates/vidfarm_template_0000/assets/Abel-Regular.ttf +0 -0
- package/templates/vidfarm_template_0000/assets/DMSerifDisplay-Regular.ttf +0 -0
- package/templates/vidfarm_template_0000/assets/Montserrat[wght].ttf +0 -0
- package/templates/vidfarm_template_0000/assets/SourceCodePro[wght].ttf +0 -0
- package/templates/vidfarm_template_0000/assets/TikTokSans-SemiBold.ttf +0 -0
- package/templates/vidfarm_template_0000/assets/Yesteryear-Regular.ttf +0 -0
- package/templates/vidfarm_template_0000/composition.json +11 -0
- package/templates/vidfarm_template_0000/src/lib/images.js +221 -0
- package/templates/vidfarm_template_0000/src/remotion/Root.js +33 -0
- package/templates/vidfarm_template_0000/src/remotion/index.js +3 -0
- package/templates/vidfarm_template_0000/src/sdk.js +3 -0
- package/templates/vidfarm_template_0000/src/style-options.js +200 -0
- package/templates/vidfarm_template_0000/src/template-dna.js +9 -0
- package/templates/vidfarm_template_0000/src/template.js +1566 -0
- package/templates/vidfarm_template_0000/template.config.json +21 -0
- package/GETTING_STARTED.developers.md +0 -87
- package/SKILL.developer.md +0 -1186
- package/dist/src/db.js +0 -2007
|
@@ -1,1829 +1,2086 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export function renderTemplateEditorDocsPage(input) {
|
|
4
|
-
const backHref = withAccountQuery("/discover", input.account.userId);
|
|
5
|
-
const hasHistoryAccess = input.account.isLoggedIn;
|
|
6
|
-
const preview = input.template.previewUrl
|
|
7
|
-
? renderPreview(input.template.previewUrl, `${input.template.title} preview`)
|
|
8
|
-
: `<div class="editor-preview editor-preview-empty">No preview asset</div>`;
|
|
1
|
+
import { renderBrandLockup, renderPageShell, renderPrimaryNav } from "./page-shell.js";
|
|
2
|
+
export function renderCustomInpaintPage(input) {
|
|
9
3
|
return renderPageShell({
|
|
10
|
-
title:
|
|
4
|
+
title: "Inpaint",
|
|
11
5
|
mainClass: "is-wide",
|
|
12
|
-
runtimeBoot: input.editorChat ? {
|
|
13
|
-
pageKind: "legacy",
|
|
14
|
-
title: `${input.template.title} editor`,
|
|
15
|
-
userId: input.account.userId ?? null,
|
|
16
|
-
editorChat: input.editorChat
|
|
17
|
-
} : undefined,
|
|
18
6
|
style: `
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
.editor-workbench,
|
|
22
|
-
.editor-main,
|
|
23
|
-
.editor-shell {
|
|
24
|
-
padding-bottom: 0;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.editor-shell {
|
|
28
|
-
display: grid;
|
|
29
|
-
gap: 18px;
|
|
7
|
+
.custom-inpaint-frame {
|
|
8
|
+
height: 100%;
|
|
30
9
|
min-height: 0;
|
|
31
|
-
grid-template-rows: auto auto minmax(0, 1fr);
|
|
32
10
|
}
|
|
33
11
|
|
|
34
|
-
.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
12
|
+
.custom-inpaint-shell {
|
|
13
|
+
height: 100%;
|
|
14
|
+
min-height: 0;
|
|
15
|
+
display: grid;
|
|
16
|
+
grid-template-rows: auto minmax(0, 1fr);
|
|
17
|
+
gap: 14px;
|
|
40
18
|
}
|
|
41
19
|
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
20
|
+
.custom-inpaint-workbench {
|
|
21
|
+
height: 100%;
|
|
22
|
+
min-height: 0;
|
|
23
|
+
display: grid;
|
|
24
|
+
grid-template-columns: 280px minmax(0, 1fr) 340px;
|
|
25
|
+
gap: 14px;
|
|
47
26
|
overflow: hidden;
|
|
48
27
|
}
|
|
49
28
|
|
|
50
|
-
.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
list-style: none;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
.editor-panel > summary::-webkit-details-marker {
|
|
61
|
-
display: none;
|
|
29
|
+
.custom-inpaint-panel,
|
|
30
|
+
.custom-inpaint-stage,
|
|
31
|
+
.custom-inpaint-output,
|
|
32
|
+
.editor-inpaint-panel {
|
|
33
|
+
border: 1px solid rgba(191, 164, 109, 0.24);
|
|
34
|
+
border-radius: 18px;
|
|
35
|
+
background: rgba(255, 255, 255, 0.74);
|
|
62
36
|
}
|
|
63
37
|
|
|
64
|
-
.
|
|
38
|
+
.custom-inpaint-panel {
|
|
39
|
+
min-height: 0;
|
|
65
40
|
display: grid;
|
|
66
|
-
|
|
41
|
+
align-content: start;
|
|
42
|
+
gap: 12px;
|
|
43
|
+
padding: 14px;
|
|
44
|
+
overflow: auto;
|
|
67
45
|
}
|
|
68
46
|
|
|
69
|
-
.
|
|
47
|
+
.custom-inpaint-panel h1,
|
|
48
|
+
.custom-inpaint-output h2 {
|
|
70
49
|
margin: 0;
|
|
71
|
-
|
|
72
|
-
|
|
50
|
+
color: #253041;
|
|
51
|
+
font-size: 1.02rem;
|
|
73
52
|
}
|
|
74
53
|
|
|
75
|
-
.
|
|
54
|
+
.custom-inpaint-panel p,
|
|
55
|
+
.custom-inpaint-status,
|
|
56
|
+
.editor-inpaint-label {
|
|
76
57
|
margin: 0;
|
|
77
58
|
color: #617087;
|
|
78
|
-
|
|
59
|
+
font-size: 0.8rem;
|
|
60
|
+
line-height: 1.45;
|
|
79
61
|
}
|
|
80
62
|
|
|
81
|
-
.
|
|
82
|
-
|
|
63
|
+
.custom-inpaint-panel-head,
|
|
64
|
+
.custom-inpaint-tool-head {
|
|
65
|
+
display: flex;
|
|
83
66
|
align-items: center;
|
|
67
|
+
justify-content: space-between;
|
|
84
68
|
gap: 10px;
|
|
85
|
-
color: #445066;
|
|
86
|
-
font-size: 0.82rem;
|
|
87
|
-
font-weight: 700;
|
|
88
|
-
letter-spacing: 0.08em;
|
|
89
|
-
text-transform: uppercase;
|
|
90
|
-
white-space: nowrap;
|
|
91
69
|
}
|
|
92
70
|
|
|
93
|
-
.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
71
|
+
.custom-inpaint-upload {
|
|
72
|
+
min-height: 96px;
|
|
73
|
+
display: grid;
|
|
74
|
+
place-items: center;
|
|
75
|
+
gap: 5px;
|
|
76
|
+
padding: 12px;
|
|
77
|
+
border: 1px dashed rgba(97, 112, 135, 0.36);
|
|
78
|
+
border-radius: 16px;
|
|
79
|
+
background: rgba(255, 251, 244, 0.72);
|
|
80
|
+
color: #253041;
|
|
81
|
+
cursor: pointer;
|
|
101
82
|
}
|
|
102
83
|
|
|
103
|
-
.
|
|
104
|
-
|
|
84
|
+
.custom-inpaint-upload svg {
|
|
85
|
+
width: 22px;
|
|
86
|
+
height: 22px;
|
|
105
87
|
}
|
|
106
88
|
|
|
107
|
-
.
|
|
108
|
-
|
|
89
|
+
.custom-inpaint-upload span {
|
|
90
|
+
max-width: 100%;
|
|
91
|
+
overflow-wrap: anywhere;
|
|
92
|
+
word-break: break-word;
|
|
93
|
+
text-align: center;
|
|
94
|
+
line-height: 1.25;
|
|
109
95
|
}
|
|
110
96
|
|
|
111
|
-
.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
97
|
+
.custom-inpaint-stage {
|
|
98
|
+
position: relative;
|
|
99
|
+
min-height: 0;
|
|
100
|
+
display: grid;
|
|
101
|
+
place-items: center;
|
|
102
|
+
overflow: auto;
|
|
103
|
+
background:
|
|
104
|
+
linear-gradient(45deg, rgba(255,255,255,0.06) 25%, transparent 25%),
|
|
105
|
+
linear-gradient(-45deg, rgba(255,255,255,0.06) 25%, transparent 25%),
|
|
106
|
+
linear-gradient(45deg, transparent 75%, rgba(255,255,255,0.06) 75%),
|
|
107
|
+
linear-gradient(-45deg, transparent 75%, rgba(255,255,255,0.06) 75%),
|
|
108
|
+
#252d3d;
|
|
109
|
+
background-size: 22px 22px;
|
|
110
|
+
background-position: 0 0, 0 11px, 11px -11px, -11px 0;
|
|
118
111
|
}
|
|
119
112
|
|
|
120
|
-
.
|
|
113
|
+
.custom-inpaint-empty {
|
|
114
|
+
position: absolute;
|
|
115
|
+
inset: 0;
|
|
116
|
+
z-index: 1;
|
|
121
117
|
display: grid;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
118
|
+
place-items: center;
|
|
119
|
+
padding: 24px;
|
|
120
|
+
text-align: center;
|
|
121
|
+
color: rgba(255, 251, 244, 0.74);
|
|
122
|
+
font-weight: 700;
|
|
123
|
+
pointer-events: none;
|
|
125
124
|
}
|
|
126
125
|
|
|
127
|
-
.
|
|
128
|
-
|
|
129
|
-
min-height: 320px;
|
|
130
|
-
max-height: 560px;
|
|
131
|
-
object-fit: contain;
|
|
132
|
-
border-radius: 24px;
|
|
133
|
-
border: 1px solid rgba(209, 219, 233, 0.96);
|
|
134
|
-
background: rgba(255, 255, 255, 0.92);
|
|
126
|
+
.custom-inpaint-empty[hidden] {
|
|
127
|
+
display: none;
|
|
135
128
|
}
|
|
136
129
|
|
|
137
|
-
.editor-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
130
|
+
.editor-inpaint-canvas-wrap {
|
|
131
|
+
position: relative;
|
|
132
|
+
display: inline-block;
|
|
133
|
+
max-width: 100%;
|
|
134
|
+
line-height: 0;
|
|
135
|
+
--inpaint-brush-cursor-size: 70px;
|
|
136
|
+
--inpaint-brush-cursor-x: 0px;
|
|
137
|
+
--inpaint-brush-cursor-y: 0px;
|
|
141
138
|
}
|
|
142
139
|
|
|
143
|
-
.editor-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
140
|
+
.editor-inpaint-canvas-wrap img {
|
|
141
|
+
max-width: 100%;
|
|
142
|
+
max-height: calc(100vh - 180px);
|
|
143
|
+
display: block;
|
|
144
|
+
object-fit: contain;
|
|
145
|
+
user-select: none;
|
|
148
146
|
}
|
|
149
147
|
|
|
150
|
-
.editor-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
148
|
+
.editor-inpaint-canvas-wrap canvas {
|
|
149
|
+
position: absolute;
|
|
150
|
+
inset: 0;
|
|
151
|
+
width: 100%;
|
|
152
|
+
height: 100%;
|
|
153
|
+
opacity: 0.86;
|
|
154
|
+
cursor: none;
|
|
155
|
+
touch-action: none;
|
|
155
156
|
}
|
|
156
157
|
|
|
157
|
-
.editor-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
158
|
+
.editor-inpaint-brush-cursor {
|
|
159
|
+
position: absolute;
|
|
160
|
+
z-index: 2;
|
|
161
|
+
left: 0;
|
|
162
|
+
top: 0;
|
|
163
|
+
width: var(--inpaint-brush-cursor-size);
|
|
164
|
+
height: var(--inpaint-brush-cursor-size);
|
|
165
|
+
border: 2px solid rgba(255, 255, 255, 0.92);
|
|
166
|
+
border-radius: 999px;
|
|
167
|
+
box-shadow:
|
|
168
|
+
0 0 0 1px rgba(37, 48, 65, 0.78),
|
|
169
|
+
0 0 18px rgba(37, 48, 65, 0.28);
|
|
170
|
+
opacity: 0;
|
|
171
|
+
pointer-events: none;
|
|
172
|
+
transform: translate(var(--inpaint-brush-cursor-x), var(--inpaint-brush-cursor-y)) translate(-50%, -50%);
|
|
162
173
|
}
|
|
163
174
|
|
|
164
|
-
.editor-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
175
|
+
.editor-inpaint-brush-cursor[data-visible="true"] {
|
|
176
|
+
opacity: 1;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.editor-inpaint-panel {
|
|
180
|
+
height: 100%;
|
|
181
|
+
max-height: calc(100vh - 150px);
|
|
182
|
+
min-height: 0;
|
|
183
|
+
display: grid;
|
|
184
|
+
grid-template-rows: auto;
|
|
185
|
+
align-content: start;
|
|
186
|
+
gap: 12px;
|
|
187
|
+
overflow-x: hidden;
|
|
188
|
+
overflow-y: auto;
|
|
189
|
+
overscroll-behavior: contain;
|
|
190
|
+
padding: 12px;
|
|
169
191
|
}
|
|
170
192
|
|
|
171
|
-
.editor-
|
|
172
|
-
.editor-
|
|
193
|
+
.editor-inpaint-toolbar,
|
|
194
|
+
.editor-inpaint-color-row,
|
|
195
|
+
.custom-inpaint-actions {
|
|
173
196
|
display: flex;
|
|
174
|
-
|
|
197
|
+
align-items: center;
|
|
175
198
|
gap: 10px;
|
|
176
199
|
}
|
|
177
200
|
|
|
178
|
-
.editor-toolbar
|
|
179
|
-
|
|
201
|
+
.editor-inpaint-toolbar,
|
|
202
|
+
.custom-inpaint-actions {
|
|
180
203
|
flex-wrap: wrap;
|
|
181
|
-
gap: 10px;
|
|
182
|
-
margin-top: 14px;
|
|
183
204
|
}
|
|
184
205
|
|
|
185
|
-
.editor-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
color: #2f3c4f;
|
|
190
|
-
border-radius: 999px;
|
|
191
|
-
padding: 10px 14px;
|
|
192
|
-
font: inherit;
|
|
193
|
-
font-size: 0.88rem;
|
|
194
|
-
font-weight: 700;
|
|
195
|
-
cursor: pointer;
|
|
206
|
+
.editor-inpaint-brush {
|
|
207
|
+
display: grid;
|
|
208
|
+
gap: 6px;
|
|
209
|
+
min-width: 150px;
|
|
196
210
|
}
|
|
197
211
|
|
|
198
|
-
.editor-
|
|
199
|
-
|
|
200
|
-
background: #fff;
|
|
212
|
+
.editor-inpaint-brush input[type="range"] {
|
|
213
|
+
width: 100%;
|
|
201
214
|
}
|
|
202
215
|
|
|
203
|
-
.editor-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
border-radius: 999px;
|
|
211
|
-
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
212
|
-
background: rgba(255, 255, 255, 0.9);
|
|
216
|
+
.editor-inpaint-color-picker {
|
|
217
|
+
width: 42px;
|
|
218
|
+
height: 38px;
|
|
219
|
+
padding: 2px;
|
|
220
|
+
border: 1px solid rgba(97, 112, 135, 0.24);
|
|
221
|
+
border-radius: 12px;
|
|
222
|
+
background: #fff;
|
|
213
223
|
}
|
|
214
224
|
|
|
215
|
-
.editor-
|
|
216
|
-
display:
|
|
217
|
-
grid-template-columns: minmax(0, 1fr);
|
|
218
|
-
gap: 18px;
|
|
219
|
-
min-height: 0;
|
|
225
|
+
.editor-inpaint-swatches {
|
|
226
|
+
display: none;
|
|
220
227
|
}
|
|
221
228
|
|
|
222
|
-
.editor-
|
|
229
|
+
.editor-inpaint-prompts {
|
|
223
230
|
display: grid;
|
|
224
231
|
align-content: start;
|
|
225
|
-
gap:
|
|
226
|
-
padding: 22px;
|
|
232
|
+
gap: 10px;
|
|
227
233
|
}
|
|
228
234
|
|
|
229
|
-
.editor-
|
|
230
|
-
|
|
231
|
-
|
|
235
|
+
.editor-inpaint-provider {
|
|
236
|
+
display: none;
|
|
237
|
+
gap: 6px;
|
|
232
238
|
}
|
|
233
239
|
|
|
234
|
-
.editor-
|
|
235
|
-
|
|
236
|
-
letter-spacing: -0.04em;
|
|
240
|
+
.editor-inpaint-provider[data-visible="true"] {
|
|
241
|
+
display: grid;
|
|
237
242
|
}
|
|
238
243
|
|
|
239
|
-
.editor-
|
|
240
|
-
|
|
244
|
+
.editor-inpaint-provider select {
|
|
245
|
+
min-height: 38px;
|
|
246
|
+
padding: 0 34px 0 12px;
|
|
247
|
+
border-radius: 12px;
|
|
248
|
+
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
249
|
+
background: rgba(255, 255, 255, 0.96);
|
|
241
250
|
color: #243041;
|
|
251
|
+
font: inherit;
|
|
252
|
+
font-weight: 800;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.editor-inpaint-color-row {
|
|
256
|
+
position: relative;
|
|
257
|
+
align-items: flex-start;
|
|
258
|
+
padding: 4px;
|
|
259
|
+
border-radius: 16px;
|
|
242
260
|
}
|
|
243
261
|
|
|
244
|
-
.editor-
|
|
262
|
+
.editor-inpaint-color-tools {
|
|
263
|
+
flex: 0 0 auto;
|
|
245
264
|
display: grid;
|
|
265
|
+
justify-items: center;
|
|
246
266
|
gap: 6px;
|
|
247
267
|
}
|
|
248
268
|
|
|
249
|
-
.editor-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
269
|
+
.editor-inpaint-prompt-stack {
|
|
270
|
+
min-width: 0;
|
|
271
|
+
flex: 1 1 auto;
|
|
272
|
+
display: grid;
|
|
273
|
+
align-content: start;
|
|
274
|
+
gap: 8px;
|
|
254
275
|
}
|
|
255
276
|
|
|
256
|
-
.editor-
|
|
277
|
+
.editor-inpaint-global {
|
|
257
278
|
display: grid;
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
border-radius: 20px;
|
|
261
|
-
border: 1px solid rgba(191, 164, 109, 0.22);
|
|
262
|
-
background: rgba(255, 255, 255, 0.72);
|
|
279
|
+
align-content: start;
|
|
280
|
+
gap: 8px;
|
|
263
281
|
}
|
|
264
282
|
|
|
265
|
-
.editor-
|
|
283
|
+
.editor-inpaint-prompt-field {
|
|
284
|
+
position: relative;
|
|
285
|
+
min-width: 0;
|
|
286
|
+
display: grid;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.editor-inpaint-attachment-tools {
|
|
266
290
|
display: flex;
|
|
267
|
-
flex-wrap: wrap;
|
|
268
291
|
align-items: center;
|
|
269
|
-
|
|
292
|
+
flex-wrap: wrap;
|
|
293
|
+
gap: 6px;
|
|
270
294
|
}
|
|
271
295
|
|
|
272
|
-
.editor-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
296
|
+
.editor-inpaint-attach-button {
|
|
297
|
+
position: absolute;
|
|
298
|
+
right: 8px;
|
|
299
|
+
bottom: 8px;
|
|
300
|
+
transform: translateY(-10px);
|
|
301
|
+
width: 30px;
|
|
302
|
+
height: 30px;
|
|
303
|
+
display: inline-grid;
|
|
304
|
+
place-items: center;
|
|
305
|
+
padding: 0;
|
|
306
|
+
border-radius: 999px;
|
|
307
|
+
border: 1px solid transparent;
|
|
308
|
+
background: rgba(255, 255, 255, 0.72);
|
|
309
|
+
color: rgba(37, 48, 65, 0.56);
|
|
310
|
+
font: inherit;
|
|
311
|
+
cursor: pointer;
|
|
277
312
|
}
|
|
278
313
|
|
|
279
|
-
.editor-
|
|
280
|
-
|
|
314
|
+
.editor-inpaint-attach-button:hover,
|
|
315
|
+
.editor-inpaint-attach-button:focus-visible {
|
|
316
|
+
border-color: rgba(191, 164, 109, 0.34);
|
|
317
|
+
background: rgba(255, 251, 244, 0.94);
|
|
318
|
+
color: #253041;
|
|
281
319
|
}
|
|
282
320
|
|
|
283
|
-
.editor-
|
|
284
|
-
|
|
321
|
+
.editor-inpaint-attach-button svg {
|
|
322
|
+
width: 16px;
|
|
323
|
+
height: 16px;
|
|
285
324
|
}
|
|
286
325
|
|
|
287
|
-
.editor-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
326
|
+
.editor-inpaint-attachment-chips {
|
|
327
|
+
display: flex;
|
|
328
|
+
align-items: center;
|
|
329
|
+
flex-wrap: wrap;
|
|
330
|
+
gap: 6px;
|
|
331
|
+
min-width: 0;
|
|
293
332
|
}
|
|
294
333
|
|
|
295
|
-
.editor-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
334
|
+
.editor-inpaint-attachment-chip {
|
|
335
|
+
min-width: 0;
|
|
336
|
+
max-width: 210px;
|
|
337
|
+
display: inline-flex;
|
|
338
|
+
align-items: center;
|
|
339
|
+
gap: 4px;
|
|
340
|
+
padding: 1px 4px 1px 1px;
|
|
341
|
+
border-radius: 999px;
|
|
342
|
+
border: 1px solid rgba(191, 164, 109, 0.3);
|
|
343
|
+
background: rgba(247, 241, 230, 0.82);
|
|
344
|
+
color: #253041;
|
|
345
|
+
font-size: 0.74rem;
|
|
346
|
+
font-weight: 800;
|
|
304
347
|
}
|
|
305
348
|
|
|
306
|
-
.editor-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
349
|
+
.editor-inpaint-attachment-chip img {
|
|
350
|
+
width: 22px;
|
|
351
|
+
height: 22px;
|
|
352
|
+
min-width: 22px;
|
|
353
|
+
max-width: 22px;
|
|
354
|
+
aspect-ratio: 1 / 1;
|
|
355
|
+
flex: 0 0 22px;
|
|
356
|
+
object-fit: cover;
|
|
357
|
+
border-radius: 999px;
|
|
358
|
+
background: rgba(37, 48, 65, 0.08);
|
|
310
359
|
}
|
|
311
360
|
|
|
312
|
-
.editor-
|
|
313
|
-
display: grid;
|
|
314
|
-
gap: 8px;
|
|
361
|
+
.editor-inpaint-attachment-chip span {
|
|
315
362
|
min-width: 0;
|
|
363
|
+
overflow: hidden;
|
|
364
|
+
text-overflow: ellipsis;
|
|
365
|
+
white-space: nowrap;
|
|
316
366
|
}
|
|
317
367
|
|
|
318
|
-
.editor-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
368
|
+
.editor-inpaint-attachment-chip button {
|
|
369
|
+
width: 18px;
|
|
370
|
+
height: 18px;
|
|
371
|
+
min-width: 18px;
|
|
372
|
+
max-width: 18px;
|
|
373
|
+
aspect-ratio: 1 / 1;
|
|
374
|
+
flex: 0 0 18px;
|
|
375
|
+
display: inline-grid;
|
|
376
|
+
place-items: center;
|
|
377
|
+
padding: 0;
|
|
378
|
+
border: 0;
|
|
379
|
+
border-radius: 999px;
|
|
380
|
+
background: rgba(37, 48, 65, 0.1);
|
|
381
|
+
color: #253041;
|
|
382
|
+
font: inherit;
|
|
383
|
+
line-height: 1;
|
|
384
|
+
cursor: pointer;
|
|
324
385
|
}
|
|
325
386
|
|
|
326
|
-
.editor-
|
|
327
|
-
|
|
328
|
-
font-size: 0.82rem;
|
|
329
|
-
font-weight: 800;
|
|
387
|
+
.editor-inpaint-color-row[data-active="true"] {
|
|
388
|
+
background: rgba(219, 193, 122, 0.16);
|
|
330
389
|
}
|
|
331
390
|
|
|
332
|
-
.editor-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
height: 36px;
|
|
341
|
-
min-height: 36px;
|
|
342
|
-
flex: 0 0 36px;
|
|
343
|
-
gap: 0;
|
|
344
|
-
padding: 0;
|
|
391
|
+
.editor-inpaint-color-dot {
|
|
392
|
+
width: 26px;
|
|
393
|
+
height: 26px;
|
|
394
|
+
min-width: 26px;
|
|
395
|
+
max-width: 26px;
|
|
396
|
+
aspect-ratio: 1 / 1;
|
|
397
|
+
flex: 0 0 26px;
|
|
398
|
+
margin-top: 8px;
|
|
345
399
|
border-radius: 999px;
|
|
346
|
-
border:
|
|
347
|
-
background: #
|
|
348
|
-
|
|
400
|
+
border: 2px solid rgba(37, 48, 65, 0.18);
|
|
401
|
+
background: var(--inpaint-color, #ff2d55);
|
|
402
|
+
box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.8);
|
|
349
403
|
cursor: pointer;
|
|
350
|
-
line-height: 1;
|
|
351
|
-
box-shadow:
|
|
352
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.58),
|
|
353
|
-
0 6px 14px rgba(102, 82, 43, 0.12);
|
|
354
|
-
transition:
|
|
355
|
-
transform 120ms ease,
|
|
356
|
-
border-color 120ms ease,
|
|
357
|
-
background 120ms ease,
|
|
358
|
-
color 120ms ease;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
.editor-quickstart-copy:hover {
|
|
362
|
-
border-color: rgba(102, 82, 43, 0.44);
|
|
363
|
-
background: #e4cb84;
|
|
364
|
-
color: #243041;
|
|
365
404
|
}
|
|
366
405
|
|
|
367
|
-
.editor-
|
|
368
|
-
|
|
406
|
+
.editor-inpaint-color-remove {
|
|
407
|
+
width: 22px;
|
|
408
|
+
height: 22px;
|
|
409
|
+
min-width: 22px;
|
|
410
|
+
max-width: 22px;
|
|
411
|
+
aspect-ratio: 1 / 1;
|
|
412
|
+
flex: 0 0 22px;
|
|
413
|
+
display: inline-grid;
|
|
414
|
+
place-items: center;
|
|
415
|
+
padding: 0;
|
|
416
|
+
border: 1px solid rgba(37, 48, 65, 0.12);
|
|
417
|
+
border-radius: 999px;
|
|
418
|
+
background: rgba(255, 255, 255, 0.72);
|
|
419
|
+
color: rgba(37, 48, 65, 0.56);
|
|
420
|
+
cursor: pointer;
|
|
369
421
|
}
|
|
370
422
|
|
|
371
|
-
.editor-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
color:
|
|
375
|
-
font-size: 1rem;
|
|
376
|
-
font-weight: 900;
|
|
377
|
-
line-height: 1;
|
|
423
|
+
.editor-inpaint-color-remove:hover,
|
|
424
|
+
.editor-inpaint-color-remove:focus-visible {
|
|
425
|
+
border-color: rgba(191, 164, 109, 0.36);
|
|
426
|
+
color: #253041;
|
|
378
427
|
}
|
|
379
428
|
|
|
380
|
-
.editor-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
color: #243041;
|
|
384
|
-
transform: scale(0.96);
|
|
429
|
+
.editor-inpaint-color-remove svg {
|
|
430
|
+
width: 13px;
|
|
431
|
+
height: 13px;
|
|
385
432
|
}
|
|
386
433
|
|
|
387
|
-
.editor-
|
|
388
|
-
border-color:
|
|
389
|
-
|
|
390
|
-
color: #1e5c52;
|
|
434
|
+
.editor-inpaint-color-row[data-active="true"] .editor-inpaint-color-dot {
|
|
435
|
+
border-color: #253041;
|
|
436
|
+
box-shadow: 0 0 0 3px rgba(219, 193, 122, 0.45), inset 0 0 0 2px rgba(255, 255, 255, 0.8);
|
|
391
437
|
}
|
|
392
438
|
|
|
393
|
-
.editor-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
439
|
+
.editor-inpaint-prompt {
|
|
440
|
+
width: 100%;
|
|
441
|
+
min-height: 72px;
|
|
442
|
+
display: block;
|
|
443
|
+
resize: vertical;
|
|
444
|
+
padding: 10px 44px 18px 12px;
|
|
445
|
+
border-radius: 14px;
|
|
446
|
+
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
447
|
+
background: rgba(255, 255, 255, 0.96);
|
|
448
|
+
color: #243041;
|
|
449
|
+
font: inherit;
|
|
450
|
+
line-height: 1.45;
|
|
397
451
|
}
|
|
398
452
|
|
|
399
|
-
.editor-
|
|
400
|
-
|
|
401
|
-
display:
|
|
453
|
+
.editor-history-button {
|
|
454
|
+
min-height: 38px;
|
|
455
|
+
display: inline-flex;
|
|
456
|
+
align-items: center;
|
|
457
|
+
justify-content: center;
|
|
458
|
+
gap: 6px;
|
|
459
|
+
padding: 0 12px;
|
|
460
|
+
border-radius: 12px;
|
|
461
|
+
border: 1px solid rgba(191, 164, 109, 0.28);
|
|
462
|
+
background: rgba(255, 255, 255, 0.86);
|
|
463
|
+
color: #253041;
|
|
464
|
+
font-size: 0.82rem;
|
|
465
|
+
font-weight: 800;
|
|
466
|
+
cursor: pointer;
|
|
402
467
|
}
|
|
403
468
|
|
|
404
|
-
.editor-
|
|
405
|
-
|
|
469
|
+
.editor-history-button.is-primary {
|
|
470
|
+
border-color: rgba(37, 48, 65, 0.9);
|
|
471
|
+
background: #253041;
|
|
472
|
+
color: #fffaf0;
|
|
406
473
|
}
|
|
407
474
|
|
|
408
|
-
.editor-
|
|
409
|
-
|
|
410
|
-
|
|
475
|
+
.editor-history-button[disabled] {
|
|
476
|
+
opacity: 0.58;
|
|
477
|
+
cursor: wait;
|
|
411
478
|
}
|
|
412
479
|
|
|
413
|
-
.editor-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
stroke: currentColor;
|
|
480
|
+
.editor-history-button.is-icon {
|
|
481
|
+
width: 38px;
|
|
482
|
+
padding: 0;
|
|
483
|
+
flex: 0 0 auto;
|
|
418
484
|
}
|
|
419
485
|
|
|
420
|
-
.editor-
|
|
486
|
+
.editor-history-button.is-block {
|
|
421
487
|
width: 100%;
|
|
422
|
-
min-height: 190px;
|
|
423
|
-
resize: vertical;
|
|
424
|
-
border: 1px solid rgba(191, 164, 109, 0.24);
|
|
425
|
-
border-radius: 18px;
|
|
426
|
-
background: rgba(255, 255, 255, 0.72);
|
|
427
|
-
color: #324156;
|
|
428
|
-
padding: 14px 15px;
|
|
429
|
-
font: 0.82rem/1.55 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
430
|
-
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.76);
|
|
431
488
|
}
|
|
432
489
|
|
|
433
|
-
.editor-
|
|
434
|
-
|
|
435
|
-
|
|
490
|
+
.editor-history-button svg {
|
|
491
|
+
width: 18px;
|
|
492
|
+
height: 18px;
|
|
436
493
|
}
|
|
437
494
|
|
|
438
|
-
.editor-
|
|
439
|
-
|
|
440
|
-
|
|
495
|
+
.editor-history-button[data-loading="true"]::before {
|
|
496
|
+
content: "";
|
|
497
|
+
width: 14px;
|
|
498
|
+
height: 14px;
|
|
499
|
+
border-radius: 999px;
|
|
500
|
+
border: 2px solid currentColor;
|
|
501
|
+
border-top-color: transparent;
|
|
502
|
+
animation: editor-inpaint-spin 0.75s linear infinite;
|
|
441
503
|
}
|
|
442
504
|
|
|
443
|
-
|
|
444
|
-
|
|
505
|
+
@keyframes editor-inpaint-spin {
|
|
506
|
+
to { transform: rotate(360deg); }
|
|
445
507
|
}
|
|
446
508
|
|
|
447
|
-
.
|
|
448
|
-
|
|
449
|
-
gap: 10px;
|
|
450
|
-
overflow-x: auto;
|
|
451
|
-
overflow-y: hidden;
|
|
452
|
-
padding: 2px 0 8px;
|
|
453
|
-
scrollbar-width: thin;
|
|
509
|
+
.custom-inpaint-settings-wrap {
|
|
510
|
+
position: relative;
|
|
454
511
|
}
|
|
455
512
|
|
|
456
|
-
.
|
|
457
|
-
|
|
513
|
+
.custom-inpaint-settings {
|
|
514
|
+
position: absolute;
|
|
515
|
+
right: 0;
|
|
516
|
+
top: calc(100% + 8px);
|
|
517
|
+
z-index: 20;
|
|
518
|
+
width: 260px;
|
|
519
|
+
display: grid;
|
|
520
|
+
gap: 8px;
|
|
521
|
+
padding: 12px;
|
|
522
|
+
border-radius: 16px;
|
|
523
|
+
border: 1px solid rgba(191, 164, 109, 0.32);
|
|
524
|
+
background: rgba(255, 251, 244, 0.98);
|
|
525
|
+
box-shadow: 0 18px 44px rgba(15, 23, 42, 0.16);
|
|
458
526
|
}
|
|
459
527
|
|
|
460
|
-
.
|
|
461
|
-
|
|
462
|
-
|
|
528
|
+
.custom-inpaint-settings[hidden],
|
|
529
|
+
.custom-inpaint-output[hidden] {
|
|
530
|
+
display: none;
|
|
463
531
|
}
|
|
464
532
|
|
|
465
|
-
.
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
533
|
+
.custom-inpaint-settings input {
|
|
534
|
+
width: 100%;
|
|
535
|
+
min-height: 38px;
|
|
536
|
+
padding: 0 12px;
|
|
537
|
+
border-radius: 12px;
|
|
538
|
+
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
539
|
+
background: rgba(255, 255, 255, 0.96);
|
|
540
|
+
color: #243041;
|
|
541
|
+
font: inherit;
|
|
472
542
|
}
|
|
473
543
|
|
|
474
|
-
.
|
|
475
|
-
|
|
544
|
+
.custom-inpaint-output {
|
|
545
|
+
display: grid;
|
|
546
|
+
gap: 10px;
|
|
547
|
+
padding: 12px;
|
|
476
548
|
}
|
|
477
549
|
|
|
478
|
-
.
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
550
|
+
.custom-inpaint-preview {
|
|
551
|
+
width: 100%;
|
|
552
|
+
display: grid;
|
|
553
|
+
padding: 0;
|
|
554
|
+
border: 0;
|
|
555
|
+
border-radius: 12px;
|
|
556
|
+
background: #252d3d;
|
|
557
|
+
cursor: zoom-in;
|
|
482
558
|
}
|
|
483
559
|
|
|
484
|
-
.
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
560
|
+
.custom-inpaint-output img {
|
|
561
|
+
width: 100%;
|
|
562
|
+
max-height: 320px;
|
|
563
|
+
object-fit: contain;
|
|
564
|
+
border-radius: 12px;
|
|
565
|
+
background: #252d3d;
|
|
490
566
|
}
|
|
491
567
|
|
|
492
|
-
.
|
|
493
|
-
|
|
568
|
+
.custom-inpaint-history {
|
|
569
|
+
display: grid;
|
|
570
|
+
gap: 8px;
|
|
571
|
+
padding: 12px;
|
|
572
|
+
border: 1px solid rgba(191, 164, 109, 0.24);
|
|
573
|
+
border-radius: 18px;
|
|
574
|
+
background: rgba(255, 255, 255, 0.58);
|
|
494
575
|
}
|
|
495
576
|
|
|
496
|
-
.
|
|
497
|
-
|
|
498
|
-
display: inline-flex;
|
|
577
|
+
.custom-inpaint-history-head {
|
|
578
|
+
display: flex;
|
|
499
579
|
align-items: center;
|
|
500
|
-
justify-content:
|
|
501
|
-
|
|
502
|
-
padding: 0 12px;
|
|
503
|
-
border-radius: 999px;
|
|
504
|
-
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
505
|
-
background: rgba(255, 255, 255, 0.92);
|
|
506
|
-
color: #324156;
|
|
507
|
-
font-size: 0.78rem;
|
|
508
|
-
font-weight: 700;
|
|
509
|
-
cursor: pointer;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
.editor-history-remove {
|
|
513
|
-
color: #8d4a3b;
|
|
580
|
+
justify-content: space-between;
|
|
581
|
+
gap: 8px;
|
|
514
582
|
}
|
|
515
583
|
|
|
516
|
-
.
|
|
517
|
-
|
|
518
|
-
color: #
|
|
584
|
+
.custom-inpaint-history-head h2 {
|
|
585
|
+
margin: 0;
|
|
586
|
+
color: #253041;
|
|
587
|
+
font-size: 1rem;
|
|
519
588
|
}
|
|
520
589
|
|
|
521
|
-
.
|
|
590
|
+
.custom-inpaint-history-refresh {
|
|
522
591
|
width: 30px;
|
|
523
|
-
|
|
592
|
+
height: 30px;
|
|
593
|
+
min-width: 30px;
|
|
524
594
|
padding: 0;
|
|
525
|
-
border-radius: 999px;
|
|
526
595
|
}
|
|
527
596
|
|
|
528
|
-
.
|
|
597
|
+
.custom-inpaint-history-refresh svg {
|
|
529
598
|
width: 15px;
|
|
530
599
|
height: 15px;
|
|
531
600
|
}
|
|
532
601
|
|
|
533
|
-
.
|
|
534
|
-
|
|
602
|
+
.custom-inpaint-history-list {
|
|
603
|
+
display: grid;
|
|
604
|
+
gap: 8px;
|
|
605
|
+
overflow: visible;
|
|
606
|
+
padding: 2px;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.custom-inpaint-history-empty {
|
|
610
|
+
color: #617087;
|
|
611
|
+
font-size: 0.8rem;
|
|
612
|
+
line-height: 1.4;
|
|
535
613
|
}
|
|
536
614
|
|
|
537
|
-
.
|
|
615
|
+
.custom-inpaint-history-item {
|
|
538
616
|
position: relative;
|
|
539
|
-
|
|
617
|
+
min-width: 0;
|
|
618
|
+
display: grid;
|
|
619
|
+
grid-template-columns: 54px minmax(0, 1fr);
|
|
620
|
+
align-items: center;
|
|
621
|
+
gap: 8px;
|
|
622
|
+
padding: 6px;
|
|
623
|
+
border: 1px solid rgba(191, 164, 109, 0.2);
|
|
624
|
+
border-radius: 12px;
|
|
625
|
+
background: rgba(255, 251, 244, 0.62);
|
|
540
626
|
}
|
|
541
627
|
|
|
542
|
-
.
|
|
543
|
-
width:
|
|
544
|
-
|
|
628
|
+
.custom-inpaint-history-thumb-button {
|
|
629
|
+
width: 54px;
|
|
630
|
+
height: 54px;
|
|
631
|
+
display: grid;
|
|
545
632
|
padding: 0;
|
|
633
|
+
border: 0;
|
|
634
|
+
border-radius: 10px;
|
|
635
|
+
background: transparent;
|
|
636
|
+
cursor: zoom-in;
|
|
546
637
|
}
|
|
547
638
|
|
|
548
|
-
.
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
z-index: 20;
|
|
553
|
-
display: none;
|
|
554
|
-
min-width: 148px;
|
|
555
|
-
padding: 6px;
|
|
556
|
-
border: 1px solid rgba(191, 164, 109, 0.28);
|
|
639
|
+
.custom-inpaint-history-thumb {
|
|
640
|
+
width: 54px;
|
|
641
|
+
height: 54px;
|
|
642
|
+
border: 1px solid rgba(191, 164, 109, 0.3);
|
|
557
643
|
border-radius: 10px;
|
|
558
|
-
|
|
559
|
-
|
|
644
|
+
object-fit: cover;
|
|
645
|
+
background: #252d3d;
|
|
560
646
|
}
|
|
561
647
|
|
|
562
|
-
.
|
|
648
|
+
.custom-inpaint-history-generating {
|
|
649
|
+
width: 54px;
|
|
650
|
+
height: 54px;
|
|
563
651
|
display: grid;
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
padding: 8px 9px;
|
|
570
|
-
border-radius: 7px;
|
|
571
|
-
color: #324156;
|
|
572
|
-
font-size: 0.78rem;
|
|
573
|
-
font-weight: 700;
|
|
574
|
-
cursor: pointer;
|
|
652
|
+
place-items: center;
|
|
653
|
+
border: 1px solid rgba(191, 164, 109, 0.3);
|
|
654
|
+
border-radius: 10px;
|
|
655
|
+
background: linear-gradient(135deg, rgba(37, 48, 65, 0.92), rgba(122, 75, 22, 0.84));
|
|
656
|
+
color: #fff7e8;
|
|
575
657
|
}
|
|
576
658
|
|
|
577
|
-
.
|
|
578
|
-
|
|
659
|
+
.custom-inpaint-history-generating::before {
|
|
660
|
+
content: "";
|
|
661
|
+
width: 22px;
|
|
662
|
+
height: 22px;
|
|
663
|
+
border-radius: 999px;
|
|
664
|
+
border: 2px solid rgba(255, 247, 232, 0.34);
|
|
665
|
+
border-top-color: #fff7e8;
|
|
666
|
+
animation: editor-inpaint-spin 0.75s linear infinite;
|
|
579
667
|
}
|
|
580
668
|
|
|
581
|
-
.
|
|
582
|
-
|
|
669
|
+
.custom-inpaint-history-meta {
|
|
670
|
+
min-width: 0;
|
|
671
|
+
display: grid;
|
|
672
|
+
gap: 5px;
|
|
583
673
|
}
|
|
584
674
|
|
|
585
|
-
.
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
675
|
+
.custom-inpaint-history-title {
|
|
676
|
+
min-width: 0;
|
|
677
|
+
color: #253041;
|
|
678
|
+
font-size: 0.78rem;
|
|
679
|
+
font-weight: 800;
|
|
590
680
|
overflow: hidden;
|
|
681
|
+
text-overflow: ellipsis;
|
|
682
|
+
white-space: nowrap;
|
|
591
683
|
}
|
|
592
684
|
|
|
593
|
-
.
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
scrollbar-width: thin;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
.editor-history-group-preview {
|
|
602
|
-
position: relative;
|
|
603
|
-
width: 52px;
|
|
604
|
-
height: 52px;
|
|
605
|
-
padding: 0;
|
|
606
|
-
border-radius: 9px;
|
|
607
|
-
border: 1px solid rgba(191, 164, 109, 0.26);
|
|
608
|
-
background: rgba(255, 255, 255, 0.74);
|
|
685
|
+
.custom-inpaint-history-subtitle {
|
|
686
|
+
min-width: 0;
|
|
687
|
+
color: #617087;
|
|
688
|
+
font-size: 0.72rem;
|
|
689
|
+
font-weight: 700;
|
|
609
690
|
overflow: hidden;
|
|
610
|
-
|
|
611
|
-
|
|
691
|
+
text-overflow: ellipsis;
|
|
692
|
+
white-space: nowrap;
|
|
612
693
|
}
|
|
613
694
|
|
|
614
|
-
.
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
display: block;
|
|
695
|
+
.custom-inpaint-history-actions {
|
|
696
|
+
display: flex;
|
|
697
|
+
align-items: center;
|
|
698
|
+
justify-content: flex-start;
|
|
699
|
+
gap: 4px;
|
|
620
700
|
}
|
|
621
701
|
|
|
622
|
-
.
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
702
|
+
.custom-inpaint-history-icon {
|
|
703
|
+
width: 24px;
|
|
704
|
+
height: 24px;
|
|
705
|
+
display: inline-grid;
|
|
706
|
+
place-items: center;
|
|
626
707
|
padding: 0;
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
color: #617087;
|
|
632
|
-
font-size: 0.75rem;
|
|
633
|
-
font-weight: 800;
|
|
634
|
-
flex: 0 0 auto;
|
|
708
|
+
border: 1px solid rgba(191, 164, 109, 0.24);
|
|
709
|
+
border-radius: 999px;
|
|
710
|
+
background: rgba(255, 255, 255, 0.88);
|
|
711
|
+
color: #253041;
|
|
635
712
|
cursor: pointer;
|
|
713
|
+
text-decoration: none;
|
|
636
714
|
}
|
|
637
715
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
716
|
+
.custom-inpaint-history-check {
|
|
717
|
+
min-height: 24px;
|
|
718
|
+
padding: 0 9px;
|
|
719
|
+
border: 1px solid rgba(191, 164, 109, 0.3);
|
|
720
|
+
border-radius: 999px;
|
|
642
721
|
background: rgba(255, 255, 255, 0.9);
|
|
722
|
+
color: #253041;
|
|
723
|
+
font-size: 0.72rem;
|
|
724
|
+
font-weight: 850;
|
|
725
|
+
cursor: pointer;
|
|
643
726
|
}
|
|
644
727
|
|
|
645
|
-
.
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
min-height: 30px;
|
|
649
|
-
padding: 0 10px;
|
|
650
|
-
border-radius: 999px;
|
|
651
|
-
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
652
|
-
background: rgba(255, 255, 255, 0.9);
|
|
653
|
-
color: #3b465a;
|
|
654
|
-
font-size: 0.78rem;
|
|
655
|
-
font-weight: 700;
|
|
728
|
+
.custom-inpaint-history-check[disabled] {
|
|
729
|
+
cursor: wait;
|
|
730
|
+
opacity: 0.64;
|
|
656
731
|
}
|
|
657
732
|
|
|
658
|
-
.
|
|
659
|
-
|
|
660
|
-
|
|
733
|
+
.custom-inpaint-history-icon svg {
|
|
734
|
+
width: 13px;
|
|
735
|
+
height: 13px;
|
|
661
736
|
}
|
|
662
737
|
|
|
663
|
-
.
|
|
738
|
+
.custom-inpaint-history-popconfirm {
|
|
739
|
+
position: absolute;
|
|
740
|
+
right: 6px;
|
|
741
|
+
top: 6px;
|
|
742
|
+
z-index: 6;
|
|
743
|
+
width: 156px;
|
|
664
744
|
display: grid;
|
|
665
|
-
gap:
|
|
745
|
+
gap: 7px;
|
|
746
|
+
padding: 8px;
|
|
747
|
+
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
748
|
+
border-radius: 12px;
|
|
749
|
+
background: rgba(255, 251, 244, 0.98);
|
|
750
|
+
box-shadow: 0 14px 30px rgba(15, 23, 42, 0.16);
|
|
666
751
|
}
|
|
667
752
|
|
|
668
|
-
.
|
|
669
|
-
|
|
670
|
-
border-radius: 18px;
|
|
671
|
-
border: 1px dashed rgba(191, 164, 109, 0.34);
|
|
672
|
-
background: rgba(255, 255, 255, 0.56);
|
|
673
|
-
color: #617087;
|
|
753
|
+
.custom-inpaint-history-popconfirm[hidden] {
|
|
754
|
+
display: none;
|
|
674
755
|
}
|
|
675
756
|
|
|
676
|
-
.
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
757
|
+
.custom-inpaint-history-popconfirm p {
|
|
758
|
+
margin: 0;
|
|
759
|
+
color: #253041;
|
|
760
|
+
font-size: 0.76rem;
|
|
761
|
+
font-weight: 800;
|
|
762
|
+
line-height: 1.25;
|
|
681
763
|
}
|
|
682
764
|
|
|
683
|
-
.
|
|
765
|
+
.custom-inpaint-history-popconfirm-actions {
|
|
684
766
|
display: flex;
|
|
685
|
-
|
|
686
|
-
justify-content: space-between;
|
|
687
|
-
gap: 12px;
|
|
688
|
-
padding: 14px 16px;
|
|
689
|
-
cursor: pointer;
|
|
690
|
-
list-style: none;
|
|
767
|
+
gap: 5px;
|
|
691
768
|
}
|
|
692
769
|
|
|
693
|
-
.
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
770
|
+
.custom-inpaint-history-popconfirm-actions button {
|
|
771
|
+
flex: 1 1 0;
|
|
772
|
+
min-height: 26px;
|
|
773
|
+
padding: 0 7px;
|
|
774
|
+
border-radius: 9px;
|
|
775
|
+
border: 1px solid rgba(191, 164, 109, 0.28);
|
|
776
|
+
background: rgba(255, 255, 255, 0.92);
|
|
777
|
+
color: #253041;
|
|
778
|
+
font: inherit;
|
|
779
|
+
font-size: 0.72rem;
|
|
780
|
+
font-weight: 800;
|
|
781
|
+
cursor: pointer;
|
|
701
782
|
}
|
|
702
783
|
|
|
703
|
-
.
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
color: #243041;
|
|
707
|
-
word-break: break-all;
|
|
784
|
+
.custom-inpaint-history-popconfirm-actions button[data-danger="true"] {
|
|
785
|
+
border-color: rgba(155, 45, 45, 0.3);
|
|
786
|
+
color: #9b2d2d;
|
|
708
787
|
}
|
|
709
788
|
|
|
710
|
-
.
|
|
711
|
-
|
|
712
|
-
color: #74829c;
|
|
713
|
-
font-size: 0.82rem;
|
|
789
|
+
.custom-inpaint-status[data-tone="error"] {
|
|
790
|
+
color: #9b2d2d;
|
|
714
791
|
}
|
|
715
792
|
|
|
716
|
-
.
|
|
717
|
-
color: #
|
|
718
|
-
font-size: 0.8rem;
|
|
719
|
-
font-weight: 700;
|
|
720
|
-
letter-spacing: 0.08em;
|
|
721
|
-
text-transform: uppercase;
|
|
793
|
+
.custom-inpaint-status[data-tone="success"] {
|
|
794
|
+
color: #2f7d4f;
|
|
722
795
|
}
|
|
723
796
|
|
|
724
|
-
.
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
font-weight: 800;
|
|
734
|
-
letter-spacing: 0.08em;
|
|
735
|
-
text-transform: uppercase;
|
|
797
|
+
.custom-inpaint-lightbox {
|
|
798
|
+
position: fixed;
|
|
799
|
+
inset: 0;
|
|
800
|
+
z-index: 80;
|
|
801
|
+
display: none;
|
|
802
|
+
place-items: center;
|
|
803
|
+
padding: 22px;
|
|
804
|
+
background: rgba(15, 23, 42, 0.72);
|
|
805
|
+
backdrop-filter: blur(12px);
|
|
736
806
|
}
|
|
737
807
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
transform: rotate(360deg);
|
|
741
|
-
}
|
|
808
|
+
.custom-inpaint-lightbox[data-open="true"] {
|
|
809
|
+
display: grid;
|
|
742
810
|
}
|
|
743
811
|
|
|
744
|
-
.
|
|
745
|
-
display:
|
|
746
|
-
gap: 10px;
|
|
747
|
-
padding: 0 16px 16px;
|
|
812
|
+
.custom-inpaint-lightbox[hidden] {
|
|
813
|
+
display: none;
|
|
748
814
|
}
|
|
749
815
|
|
|
750
|
-
.
|
|
816
|
+
.custom-inpaint-lightbox-dialog {
|
|
817
|
+
width: min(1100px, 96vw);
|
|
818
|
+
max-height: 94vh;
|
|
751
819
|
display: grid;
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
border: 1px solid rgba(
|
|
756
|
-
|
|
820
|
+
grid-template-rows: auto minmax(0, 1fr);
|
|
821
|
+
gap: 12px;
|
|
822
|
+
padding: 12px;
|
|
823
|
+
border: 1px solid rgba(255, 251, 244, 0.18);
|
|
824
|
+
border-radius: 18px;
|
|
825
|
+
background: rgba(37, 45, 61, 0.96);
|
|
826
|
+
box-shadow: 0 24px 72px rgba(0, 0, 0, 0.38);
|
|
757
827
|
}
|
|
758
828
|
|
|
759
|
-
.
|
|
829
|
+
.custom-inpaint-lightbox-head {
|
|
760
830
|
display: flex;
|
|
761
|
-
|
|
831
|
+
align-items: center;
|
|
762
832
|
justify-content: space-between;
|
|
763
833
|
gap: 10px;
|
|
764
834
|
}
|
|
765
835
|
|
|
766
|
-
.
|
|
767
|
-
display: grid;
|
|
768
|
-
gap: 6px;
|
|
836
|
+
.custom-inpaint-lightbox-title {
|
|
769
837
|
min-width: 0;
|
|
838
|
+
margin: 0;
|
|
839
|
+
color: #fffaf0;
|
|
840
|
+
font-size: 0.9rem;
|
|
841
|
+
font-weight: 800;
|
|
842
|
+
overflow: hidden;
|
|
843
|
+
text-overflow: ellipsis;
|
|
844
|
+
white-space: nowrap;
|
|
770
845
|
}
|
|
771
846
|
|
|
772
|
-
.
|
|
847
|
+
.custom-inpaint-lightbox-actions {
|
|
773
848
|
display: flex;
|
|
774
|
-
flex-wrap: wrap;
|
|
775
|
-
gap: 8px;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
.editor-history-method,
|
|
779
|
-
.editor-history-status {
|
|
780
|
-
display: inline-flex;
|
|
781
849
|
align-items: center;
|
|
782
|
-
|
|
783
|
-
padding: 0 10px;
|
|
784
|
-
border-radius: 999px;
|
|
785
|
-
border: 1px solid rgba(191, 164, 109, 0.3);
|
|
786
|
-
background: rgba(255, 255, 255, 0.92);
|
|
787
|
-
font-size: 0.78rem;
|
|
788
|
-
font-weight: 800;
|
|
850
|
+
gap: 8px;
|
|
789
851
|
}
|
|
790
852
|
|
|
791
|
-
.
|
|
792
|
-
|
|
853
|
+
.custom-inpaint-lightbox-stage {
|
|
854
|
+
min-height: 0;
|
|
855
|
+
display: grid;
|
|
856
|
+
place-items: center;
|
|
857
|
+
overflow: auto;
|
|
793
858
|
}
|
|
794
859
|
|
|
795
|
-
.
|
|
796
|
-
|
|
860
|
+
.custom-inpaint-lightbox-stage img {
|
|
861
|
+
max-width: 100%;
|
|
862
|
+
max-height: calc(94vh - 92px);
|
|
863
|
+
object-fit: contain;
|
|
864
|
+
border-radius: 12px;
|
|
865
|
+
background: #111827;
|
|
797
866
|
}
|
|
798
867
|
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
868
|
+
@media (max-width: 1100px) {
|
|
869
|
+
.custom-inpaint-workbench {
|
|
870
|
+
grid-template-columns: 1fr;
|
|
871
|
+
overflow: auto;
|
|
872
|
+
}
|
|
802
873
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
line-height: 1.55;
|
|
807
|
-
color: #324156;
|
|
808
|
-
word-break: break-all;
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
.editor-history-card-time {
|
|
812
|
-
color: #74829c;
|
|
813
|
-
font-size: 0.8rem;
|
|
814
|
-
white-space: nowrap;
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
.editor-history-card-previews {
|
|
818
|
-
display: flex;
|
|
819
|
-
flex-wrap: wrap;
|
|
820
|
-
gap: 8px;
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
.editor-history-card-preview {
|
|
824
|
-
position: relative;
|
|
825
|
-
width: 112px;
|
|
826
|
-
height: 112px;
|
|
827
|
-
display: inline-flex;
|
|
828
|
-
align-items: center;
|
|
829
|
-
justify-content: center;
|
|
830
|
-
overflow: hidden;
|
|
831
|
-
padding: 0;
|
|
832
|
-
border-radius: 18px;
|
|
833
|
-
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
834
|
-
background: rgba(255, 255, 255, 0.94);
|
|
835
|
-
color: #324156;
|
|
836
|
-
text-decoration: none;
|
|
837
|
-
cursor: pointer;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
.editor-history-video-play {
|
|
841
|
-
position: absolute;
|
|
842
|
-
left: 50%;
|
|
843
|
-
top: 50%;
|
|
844
|
-
width: 34px;
|
|
845
|
-
height: 34px;
|
|
846
|
-
transform: translate(-50%, -50%);
|
|
847
|
-
display: inline-flex;
|
|
848
|
-
align-items: center;
|
|
849
|
-
justify-content: center;
|
|
850
|
-
border-radius: 999px;
|
|
851
|
-
background: rgba(18, 24, 34, 0.72);
|
|
852
|
-
box-shadow: 0 8px 24px rgba(12, 18, 28, 0.22);
|
|
853
|
-
pointer-events: none;
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
.editor-history-group-preview .editor-history-video-play {
|
|
857
|
-
width: 24px;
|
|
858
|
-
height: 24px;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
.editor-history-video-play::before {
|
|
862
|
-
content: "";
|
|
863
|
-
display: block;
|
|
864
|
-
width: 0;
|
|
865
|
-
height: 0;
|
|
866
|
-
margin-left: 3px;
|
|
867
|
-
border-top: 7px solid transparent;
|
|
868
|
-
border-bottom: 7px solid transparent;
|
|
869
|
-
border-left: 11px solid #fff8ee;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
.editor-history-group-preview .editor-history-video-play::before {
|
|
873
|
-
margin-left: 2px;
|
|
874
|
-
border-top-width: 5px;
|
|
875
|
-
border-bottom-width: 5px;
|
|
876
|
-
border-left-width: 8px;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
.editor-history-card-preview img,
|
|
880
|
-
.editor-history-card-preview video {
|
|
881
|
-
width: 100%;
|
|
882
|
-
height: 100%;
|
|
883
|
-
object-fit: cover;
|
|
884
|
-
display: block;
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
.editor-history-card-preview-label {
|
|
888
|
-
position: absolute;
|
|
889
|
-
left: 8px;
|
|
890
|
-
right: 8px;
|
|
891
|
-
bottom: 8px;
|
|
892
|
-
min-height: 22px;
|
|
893
|
-
display: inline-flex;
|
|
894
|
-
align-items: center;
|
|
895
|
-
justify-content: center;
|
|
896
|
-
padding: 0 8px;
|
|
897
|
-
border-radius: 999px;
|
|
898
|
-
background: rgba(24, 30, 42, 0.72);
|
|
899
|
-
color: #fff8ee;
|
|
900
|
-
font-size: 0.72rem;
|
|
901
|
-
font-weight: 700;
|
|
902
|
-
letter-spacing: 0.02em;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
.editor-history-card-preview-fallback {
|
|
906
|
-
padding: 0 10px;
|
|
907
|
-
text-align: center;
|
|
908
|
-
font-size: 0.78rem;
|
|
909
|
-
font-weight: 700;
|
|
910
|
-
line-height: 1.4;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
.editor-history-card-grid {
|
|
914
|
-
display: grid;
|
|
915
|
-
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
916
|
-
gap: 12px;
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
.editor-history-card .vf-editor-chat-http-card {
|
|
920
|
-
background: rgba(248, 244, 235, 0.74);
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
.editor-history-card-foot {
|
|
924
|
-
display: flex;
|
|
925
|
-
justify-content: flex-end;
|
|
926
|
-
color: #7b879b;
|
|
927
|
-
font-size: 0.72rem;
|
|
928
|
-
line-height: 1.3;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
.editor-history-sample {
|
|
932
|
-
display: grid;
|
|
933
|
-
gap: 8px;
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
.editor-history-sample h4 {
|
|
937
|
-
margin: 0;
|
|
938
|
-
font-size: 0.82rem;
|
|
939
|
-
color: #445066;
|
|
940
|
-
text-transform: uppercase;
|
|
941
|
-
letter-spacing: 0.08em;
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
.editor-history-sample pre {
|
|
945
|
-
margin: 0;
|
|
946
|
-
min-height: 90px;
|
|
947
|
-
max-height: 280px;
|
|
948
|
-
overflow: auto;
|
|
949
|
-
padding: 12px;
|
|
950
|
-
border-radius: 14px;
|
|
951
|
-
background: rgba(37, 45, 61, 0.96);
|
|
952
|
-
color: #f7efe1;
|
|
953
|
-
font-size: 0.78rem;
|
|
954
|
-
line-height: 1.5;
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
.editor-history-sample-note {
|
|
958
|
-
color: #74829c;
|
|
959
|
-
font-size: 0.78rem;
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
.editor-modal-backdrop {
|
|
963
|
-
position: fixed;
|
|
964
|
-
inset: 0;
|
|
965
|
-
display: none;
|
|
966
|
-
align-items: center;
|
|
967
|
-
justify-content: center;
|
|
968
|
-
padding: 24px;
|
|
969
|
-
background: rgba(24, 30, 42, 0.52);
|
|
970
|
-
backdrop-filter: blur(10px);
|
|
971
|
-
z-index: 1200;
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
.editor-modal-backdrop[data-open="true"] {
|
|
975
|
-
display: flex;
|
|
874
|
+
.editor-inpaint-panel {
|
|
875
|
+
max-height: none;
|
|
876
|
+
}
|
|
976
877
|
}
|
|
878
|
+
`,
|
|
879
|
+
body: `
|
|
880
|
+
<section class="frame custom-inpaint-frame">
|
|
881
|
+
<header class="topbar">
|
|
882
|
+
${renderBrandLockup({ displayName: input.account.name, email: input.account.email, accountId: input.account.userId })}
|
|
883
|
+
<div class="topbar-actions">
|
|
884
|
+
${renderPrimaryNav({ loggedIn: true, current: "inpaint", accountId: input.account.userId })}
|
|
885
|
+
</div>
|
|
886
|
+
</header>
|
|
887
|
+
<div class="frame-body custom-inpaint-shell">
|
|
888
|
+
<section class="custom-inpaint-workbench">
|
|
889
|
+
<aside class="custom-inpaint-panel">
|
|
890
|
+
<div class="custom-inpaint-panel-head">
|
|
891
|
+
<h1>Inpaint</h1>
|
|
892
|
+
<button type="button" class="editor-history-button" data-custom-inpaint-start-new>Start New</button>
|
|
893
|
+
</div>
|
|
894
|
+
<p>Upload a source image, paint replacement regions, then create a reusable image edit for a timeline layer.</p>
|
|
895
|
+
<button type="button" class="custom-inpaint-upload" data-custom-inpaint-upload>
|
|
896
|
+
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 14V4"></path><path d="m6.5 7.5 3.5-3.5 3.5 3.5"></path><path d="M4 15.8h12"></path></svg>
|
|
897
|
+
<strong>Add Source Image</strong>
|
|
898
|
+
<span data-custom-inpaint-source-name>No image selected</span>
|
|
899
|
+
</button>
|
|
900
|
+
<input data-custom-inpaint-file type="file" accept="image/png,image/jpeg,image/webp,image/gif,image/avif" hidden />
|
|
901
|
+
<p class="custom-inpaint-status" data-custom-inpaint-status aria-live="polite"></p>
|
|
902
|
+
<div class="custom-inpaint-output" data-custom-inpaint-output hidden>
|
|
903
|
+
<h2>Result</h2>
|
|
904
|
+
<button type="button" class="custom-inpaint-preview" data-custom-inpaint-result-open aria-label="View inpaint result">
|
|
905
|
+
<img data-custom-inpaint-result alt="Image edit result" />
|
|
906
|
+
</button>
|
|
907
|
+
<a class="editor-history-button is-primary" data-custom-inpaint-download href="#" target="_blank" rel="noopener noreferrer" download>Download</a>
|
|
908
|
+
</div>
|
|
909
|
+
<section class="custom-inpaint-history" aria-label="Image edit job history">
|
|
910
|
+
<div class="custom-inpaint-history-head">
|
|
911
|
+
<h2>History</h2>
|
|
912
|
+
<button type="button" class="editor-history-button custom-inpaint-history-refresh" data-custom-inpaint-history-refresh aria-label="Refresh image edit history" title="Refresh history">
|
|
913
|
+
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16.5 10a6.5 6.5 0 1 1-1.9-4.6"></path><path d="M16.5 4.2v4.2h-4.2"></path></svg>
|
|
914
|
+
</button>
|
|
915
|
+
</div>
|
|
916
|
+
<div class="custom-inpaint-history-list" data-custom-inpaint-history-list>
|
|
917
|
+
<div class="custom-inpaint-history-empty">Loading history...</div>
|
|
918
|
+
</div>
|
|
919
|
+
</section>
|
|
920
|
+
</aside>
|
|
921
|
+
<main class="custom-inpaint-stage">
|
|
922
|
+
<div class="custom-inpaint-empty" data-custom-inpaint-empty>Upload an image to start painting</div>
|
|
923
|
+
<div class="editor-inpaint-canvas-wrap" data-custom-inpaint-canvas-wrap hidden>
|
|
924
|
+
<img data-editor-inpaint-image alt="" />
|
|
925
|
+
<canvas data-editor-inpaint-canvas></canvas>
|
|
926
|
+
<div class="editor-inpaint-brush-cursor" data-editor-inpaint-brush-cursor aria-hidden="true"></div>
|
|
927
|
+
</div>
|
|
928
|
+
</main>
|
|
929
|
+
<aside class="editor-inpaint-panel" aria-label="Image edit controls">
|
|
930
|
+
<div class="custom-inpaint-tool-head">
|
|
931
|
+
<div class="custom-inpaint-actions" aria-label="Mask history">
|
|
932
|
+
<button type="button" class="editor-history-button is-icon" data-editor-inpaint-undo aria-label="Undo paint stroke" title="Undo">
|
|
933
|
+
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7.2 7H3.8V3.6"></path><path d="M4 7a7 7 0 1 1 1.9 6.6"></path></svg>
|
|
934
|
+
</button>
|
|
935
|
+
<button type="button" class="editor-history-button is-icon" data-editor-inpaint-redo aria-label="Redo paint stroke" title="Redo">
|
|
936
|
+
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12.8 7h3.4V3.6"></path><path d="M16 7a7 7 0 1 0-1.9 6.6"></path></svg>
|
|
937
|
+
</button>
|
|
938
|
+
</div>
|
|
939
|
+
<div class="custom-inpaint-settings-wrap">
|
|
940
|
+
<button type="button" class="editor-history-button is-icon" data-custom-inpaint-settings-button aria-label="Image edit settings" title="Settings">
|
|
941
|
+
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8.8 3.4 9.4 2h1.2l.6 1.4 1.2.5 1.4-.6.9.9-.6 1.4.5 1.2 1.4.6v1.2l-1.4.6-.5 1.2.6 1.4-.9.9-1.4-.6-1.2.5-.6 1.4H9.4l-.6-1.4-1.2-.5-1.4.6-.9-.9.6-1.4-.5-1.2L4 8.6V7.4l1.4-.6.5-1.2-.6-1.4.9-.9 1.4.6 1.2-.5Z"></path><circle cx="10" cy="8" r="2.4"></circle></svg>
|
|
942
|
+
</button>
|
|
943
|
+
<div class="custom-inpaint-settings" data-custom-inpaint-settings hidden>
|
|
944
|
+
<label>
|
|
945
|
+
<span class="editor-inpaint-label">Tracer</span>
|
|
946
|
+
<input data-custom-inpaint-tracer type="text" autocomplete="off" spellcheck="false" placeholder="image-edit" />
|
|
947
|
+
</label>
|
|
948
|
+
</div>
|
|
949
|
+
</div>
|
|
950
|
+
</div>
|
|
951
|
+
<div class="editor-inpaint-toolbar">
|
|
952
|
+
<label class="editor-inpaint-brush">
|
|
953
|
+
<span class="editor-inpaint-label">Brush <span data-editor-inpaint-brush-label>70</span></span>
|
|
954
|
+
<input data-editor-inpaint-brush type="range" min="6" max="180" step="1" value="70" />
|
|
955
|
+
</label>
|
|
956
|
+
<input class="editor-inpaint-color-picker" data-editor-inpaint-color-picker type="color" value="#ff2d55" aria-label="Mask color" />
|
|
957
|
+
<label class="editor-inpaint-provider" data-editor-inpaint-provider-wrap>
|
|
958
|
+
<span class="editor-inpaint-label">Provider</span>
|
|
959
|
+
<select data-editor-inpaint-provider aria-label="AI provider"></select>
|
|
960
|
+
</label>
|
|
961
|
+
</div>
|
|
962
|
+
<div class="editor-inpaint-global">
|
|
963
|
+
<span class="editor-inpaint-label">Whole image prompt</span>
|
|
964
|
+
<div class="editor-inpaint-prompt-field">
|
|
965
|
+
<textarea class="editor-inpaint-prompt" data-editor-inpaint-global-prompt placeholder="Describe the overall edit, style, constraints, or preservation notes."></textarea>
|
|
966
|
+
<button type="button" class="editor-inpaint-attach-button" data-editor-inpaint-attach-scope="global" aria-label="Attach reference image" title="Attach reference image">
|
|
967
|
+
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m8.1 10.7 4.8-4.8a2.5 2.5 0 0 1 3.5 3.5l-6.1 6.1a4 4 0 0 1-5.7-5.7l6.3-6.3"></path><path d="m10 8.8-4.1 4.1a1.6 1.6 0 0 0 2.3 2.3l5.6-5.6"></path></svg>
|
|
968
|
+
</button>
|
|
969
|
+
</div>
|
|
970
|
+
<div class="editor-inpaint-attachment-tools">
|
|
971
|
+
<span class="editor-inpaint-attachment-chips" data-editor-inpaint-attachments="global"></span>
|
|
972
|
+
</div>
|
|
973
|
+
</div>
|
|
974
|
+
<input data-editor-inpaint-attachment-input type="file" accept="image/jpeg,image/png,image/webp" multiple hidden />
|
|
975
|
+
<div class="editor-inpaint-swatches" data-editor-inpaint-swatches aria-label="Mask colors"></div>
|
|
976
|
+
<div class="editor-inpaint-prompts" data-editor-inpaint-prompts></div>
|
|
977
|
+
<button type="button" class="editor-history-button is-block" data-editor-inpaint-add-color>Add color</button>
|
|
978
|
+
<div class="custom-inpaint-actions">
|
|
979
|
+
<button type="button" class="editor-history-button" data-editor-inpaint-clear>Clear</button>
|
|
980
|
+
<button type="button" class="editor-history-button is-primary" data-editor-inpaint-submit><span data-editor-inpaint-submit-label>Create image edit</span></button>
|
|
981
|
+
</div>
|
|
982
|
+
</aside>
|
|
983
|
+
</section>
|
|
984
|
+
</div>
|
|
985
|
+
</section>
|
|
986
|
+
<div class="custom-inpaint-lightbox" data-custom-inpaint-lightbox data-open="false" hidden>
|
|
987
|
+
<div class="custom-inpaint-lightbox-dialog" role="dialog" aria-modal="true" aria-labelledby="custom-inpaint-lightbox-title">
|
|
988
|
+
<div class="custom-inpaint-lightbox-head">
|
|
989
|
+
<p class="custom-inpaint-lightbox-title" id="custom-inpaint-lightbox-title" data-custom-inpaint-lightbox-title>Image edit output</p>
|
|
990
|
+
<div class="custom-inpaint-lightbox-actions">
|
|
991
|
+
<button type="button" class="editor-history-button is-icon" data-custom-inpaint-lightbox-copy aria-label="Copy image URL" title="Copy URL">
|
|
992
|
+
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8.4 11.6a3.2 3.2 0 0 0 4.5 0l2.2-2.2a3.2 3.2 0 0 0-4.5-4.5L9.4 6.1"></path><path d="M11.6 8.4a3.2 3.2 0 0 0-4.5 0L4.9 10.6a3.2 3.2 0 0 0 4.5 4.5l1.2-1.2"></path></svg>
|
|
993
|
+
</button>
|
|
994
|
+
<a class="editor-history-button is-primary" data-custom-inpaint-lightbox-download href="#" target="_blank" rel="noopener noreferrer" download>Download</a>
|
|
995
|
+
<button type="button" class="editor-history-button is-icon" data-custom-inpaint-lightbox-close aria-label="Close preview" title="Close">
|
|
996
|
+
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 5l10 10"></path><path d="M15 5 5 15"></path></svg>
|
|
997
|
+
</button>
|
|
998
|
+
</div>
|
|
999
|
+
</div>
|
|
1000
|
+
<div class="custom-inpaint-lightbox-stage">
|
|
1001
|
+
<img data-custom-inpaint-lightbox-image alt="Image edit output preview" />
|
|
1002
|
+
</div>
|
|
1003
|
+
</div>
|
|
1004
|
+
</div>
|
|
1005
|
+
<script>
|
|
1006
|
+
(() => {
|
|
1007
|
+
const uploadButton = document.querySelector("[data-custom-inpaint-upload]");
|
|
1008
|
+
const startNewButton = document.querySelector("[data-custom-inpaint-start-new]");
|
|
1009
|
+
const fileInput = document.querySelector("[data-custom-inpaint-file]");
|
|
1010
|
+
const sourceName = document.querySelector("[data-custom-inpaint-source-name]");
|
|
1011
|
+
const statusEl = document.querySelector("[data-custom-inpaint-status]");
|
|
1012
|
+
const emptyState = document.querySelector("[data-custom-inpaint-empty]");
|
|
1013
|
+
const canvasWrap = document.querySelector("[data-custom-inpaint-canvas-wrap]");
|
|
1014
|
+
const outputPanel = document.querySelector("[data-custom-inpaint-output]");
|
|
1015
|
+
const resultOpenButton = document.querySelector("[data-custom-inpaint-result-open]");
|
|
1016
|
+
const resultImage = document.querySelector("[data-custom-inpaint-result]");
|
|
1017
|
+
const resultDownload = document.querySelector("[data-custom-inpaint-download]");
|
|
1018
|
+
const historyList = document.querySelector("[data-custom-inpaint-history-list]");
|
|
1019
|
+
const historyRefresh = document.querySelector("[data-custom-inpaint-history-refresh]");
|
|
1020
|
+
const inpaintImage = document.querySelector("[data-editor-inpaint-image]");
|
|
1021
|
+
const inpaintCanvas = document.querySelector("[data-editor-inpaint-canvas]");
|
|
1022
|
+
const inpaintBrushCursor = document.querySelector("[data-editor-inpaint-brush-cursor]");
|
|
1023
|
+
const inpaintUndo = document.querySelector("[data-editor-inpaint-undo]");
|
|
1024
|
+
const inpaintRedo = document.querySelector("[data-editor-inpaint-redo]");
|
|
1025
|
+
const inpaintClear = document.querySelector("[data-editor-inpaint-clear]");
|
|
1026
|
+
const inpaintBrush = document.querySelector("[data-editor-inpaint-brush]");
|
|
1027
|
+
const inpaintBrushLabel = document.querySelector("[data-editor-inpaint-brush-label]");
|
|
1028
|
+
const inpaintColorPicker = document.querySelector("[data-editor-inpaint-color-picker]");
|
|
1029
|
+
const inpaintAddColor = document.querySelector("[data-editor-inpaint-add-color]");
|
|
1030
|
+
const inpaintProviderWrap = document.querySelector("[data-editor-inpaint-provider-wrap]");
|
|
1031
|
+
const inpaintProviderSelect = document.querySelector("[data-editor-inpaint-provider]");
|
|
1032
|
+
const inpaintGlobalPrompt = document.querySelector("[data-editor-inpaint-global-prompt]");
|
|
1033
|
+
const inpaintAttachmentInput = document.querySelector("[data-editor-inpaint-attachment-input]");
|
|
1034
|
+
const inpaintGlobalAttachments = document.querySelector('[data-editor-inpaint-attachments="global"]');
|
|
1035
|
+
const inpaintSwatches = document.querySelector("[data-editor-inpaint-swatches]");
|
|
1036
|
+
const inpaintPrompts = document.querySelector("[data-editor-inpaint-prompts]");
|
|
1037
|
+
const inpaintSubmit = document.querySelector("[data-editor-inpaint-submit]");
|
|
1038
|
+
const inpaintSubmitLabel = document.querySelector("[data-editor-inpaint-submit-label]");
|
|
1039
|
+
const settingsButton = document.querySelector("[data-custom-inpaint-settings-button]");
|
|
1040
|
+
const settingsPopover = document.querySelector("[data-custom-inpaint-settings]");
|
|
1041
|
+
const tracerInput = document.querySelector("[data-custom-inpaint-tracer]");
|
|
1042
|
+
const lightbox = document.querySelector("[data-custom-inpaint-lightbox]");
|
|
1043
|
+
const lightboxTitle = document.querySelector("[data-custom-inpaint-lightbox-title]");
|
|
1044
|
+
const lightboxImage = document.querySelector("[data-custom-inpaint-lightbox-image]");
|
|
1045
|
+
const lightboxCopy = document.querySelector("[data-custom-inpaint-lightbox-copy]");
|
|
1046
|
+
const lightboxDownload = document.querySelector("[data-custom-inpaint-lightbox-download]");
|
|
1047
|
+
const lightboxClose = document.querySelector("[data-custom-inpaint-lightbox-close]");
|
|
1048
|
+
if (!(uploadButton instanceof HTMLButtonElement)
|
|
1049
|
+
|| !(startNewButton instanceof HTMLButtonElement)
|
|
1050
|
+
|| !(fileInput instanceof HTMLInputElement)
|
|
1051
|
+
|| !(sourceName instanceof HTMLElement)
|
|
1052
|
+
|| !(statusEl instanceof HTMLElement)
|
|
1053
|
+
|| !(emptyState instanceof HTMLElement)
|
|
1054
|
+
|| !(canvasWrap instanceof HTMLElement)
|
|
1055
|
+
|| !(outputPanel instanceof HTMLElement)
|
|
1056
|
+
|| !(resultOpenButton instanceof HTMLButtonElement)
|
|
1057
|
+
|| !(resultImage instanceof HTMLImageElement)
|
|
1058
|
+
|| !(resultDownload instanceof HTMLAnchorElement)
|
|
1059
|
+
|| !(historyList instanceof HTMLElement)
|
|
1060
|
+
|| !(historyRefresh instanceof HTMLButtonElement)
|
|
1061
|
+
|| !(inpaintImage instanceof HTMLImageElement)
|
|
1062
|
+
|| !(inpaintCanvas instanceof HTMLCanvasElement)
|
|
1063
|
+
|| !(inpaintBrushCursor instanceof HTMLElement)
|
|
1064
|
+
|| !(inpaintUndo instanceof HTMLButtonElement)
|
|
1065
|
+
|| !(inpaintRedo instanceof HTMLButtonElement)
|
|
1066
|
+
|| !(inpaintClear instanceof HTMLButtonElement)
|
|
1067
|
+
|| !(inpaintBrush instanceof HTMLInputElement)
|
|
1068
|
+
|| !(inpaintBrushLabel instanceof HTMLElement)
|
|
1069
|
+
|| !(inpaintColorPicker instanceof HTMLInputElement)
|
|
1070
|
+
|| !(inpaintAddColor instanceof HTMLButtonElement)
|
|
1071
|
+
|| !(inpaintProviderWrap instanceof HTMLElement)
|
|
1072
|
+
|| !(inpaintProviderSelect instanceof HTMLSelectElement)
|
|
1073
|
+
|| !(inpaintGlobalPrompt instanceof HTMLTextAreaElement)
|
|
1074
|
+
|| !(inpaintAttachmentInput instanceof HTMLInputElement)
|
|
1075
|
+
|| !(inpaintGlobalAttachments instanceof HTMLElement)
|
|
1076
|
+
|| !(inpaintSwatches instanceof HTMLElement)
|
|
1077
|
+
|| !(inpaintPrompts instanceof HTMLElement)
|
|
1078
|
+
|| !(inpaintSubmit instanceof HTMLButtonElement)
|
|
1079
|
+
|| !(inpaintSubmitLabel instanceof HTMLElement)
|
|
1080
|
+
|| !(settingsButton instanceof HTMLButtonElement)
|
|
1081
|
+
|| !(settingsPopover instanceof HTMLElement)
|
|
1082
|
+
|| !(tracerInput instanceof HTMLInputElement)
|
|
1083
|
+
|| !(lightbox instanceof HTMLElement)
|
|
1084
|
+
|| !(lightboxTitle instanceof HTMLElement)
|
|
1085
|
+
|| !(lightboxImage instanceof HTMLImageElement)
|
|
1086
|
+
|| !(lightboxCopy instanceof HTMLButtonElement)
|
|
1087
|
+
|| !(lightboxDownload instanceof HTMLAnchorElement)
|
|
1088
|
+
|| !(lightboxClose instanceof HTMLButtonElement)) {
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
977
1091
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1092
|
+
let sourceImageUrl = "";
|
|
1093
|
+
let sourceImageName = "";
|
|
1094
|
+
let inpaintMaskColors = [{ color: "#ff2d55", prompt: "" }];
|
|
1095
|
+
let activeInpaintColor = "#ff2d55";
|
|
1096
|
+
let inpaintStrokes = [];
|
|
1097
|
+
let redoStrokes = [];
|
|
1098
|
+
let currentInpaintStroke = null;
|
|
1099
|
+
let isInpaintPainting = false;
|
|
1100
|
+
let inpaintAttachments = [];
|
|
1101
|
+
let pendingInpaintAttachmentScope = null;
|
|
1102
|
+
let inpaintHistoryItems = [];
|
|
1103
|
+
let historyTracerRefreshTimer = 0;
|
|
1104
|
+
const hiddenInpaintHistoryKeys = new Set();
|
|
1105
|
+
const maxInpaintAttachments = 8;
|
|
1106
|
+
const allowedInpaintAttachmentTypes = new Set(["image/jpeg", "image/png", "image/webp"]);
|
|
988
1107
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1108
|
+
function makeTracerId() {
|
|
1109
|
+
return "custom-inpaint-" + (globalThis.crypto && typeof globalThis.crypto.randomUUID === "function"
|
|
1110
|
+
? globalThis.crypto.randomUUID().replace(/-/g, "").slice(0, 8)
|
|
1111
|
+
: Math.random().toString(16).slice(2, 10).padEnd(8, "0").slice(0, 8));
|
|
1112
|
+
}
|
|
993
1113
|
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1114
|
+
function getTracerValue() {
|
|
1115
|
+
const value = tracerInput.value.trim();
|
|
1116
|
+
if (value) return value;
|
|
1117
|
+
const tracer = makeTracerId();
|
|
1118
|
+
tracerInput.value = tracer;
|
|
1119
|
+
return tracer;
|
|
1120
|
+
}
|
|
998
1121
|
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
}
|
|
1122
|
+
function queueInpaintHistoryReload() {
|
|
1123
|
+
window.clearTimeout(historyTracerRefreshTimer);
|
|
1124
|
+
historyTracerRefreshTimer = window.setTimeout(() => {
|
|
1125
|
+
void loadInpaintHistory();
|
|
1126
|
+
}, 300);
|
|
1127
|
+
}
|
|
1006
1128
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
}
|
|
1129
|
+
function setSubmitLoading(isLoading) {
|
|
1130
|
+
inpaintSubmit.disabled = isLoading;
|
|
1131
|
+
inpaintSubmit.setAttribute("data-loading", isLoading ? "true" : "false");
|
|
1132
|
+
inpaintSubmitLabel.textContent = isLoading ? "Creating" : "Create image edit";
|
|
1133
|
+
}
|
|
1013
1134
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
font: inherit;
|
|
1023
|
-
}
|
|
1135
|
+
function setStatus(message, tone) {
|
|
1136
|
+
statusEl.textContent = message || "";
|
|
1137
|
+
if (tone) {
|
|
1138
|
+
statusEl.setAttribute("data-tone", tone);
|
|
1139
|
+
} else {
|
|
1140
|
+
statusEl.removeAttribute("data-tone");
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1024
1143
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1144
|
+
function imageDownloadName(prefix, createdAt) {
|
|
1145
|
+
const stamp = String(createdAt || new Date().toISOString()).replace(/[^0-9a-z]+/gi, "-");
|
|
1146
|
+
return prefix + "-" + stamp + ".png";
|
|
1147
|
+
}
|
|
1029
1148
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1149
|
+
function openImagePreview(url, label, downloadName) {
|
|
1150
|
+
if (!url) return;
|
|
1151
|
+
lightboxImage.src = url;
|
|
1152
|
+
lightboxImage.alt = label || "Image edit output preview";
|
|
1153
|
+
lightboxTitle.textContent = label || "Image edit output";
|
|
1154
|
+
lightboxCopy.dataset.copyUrl = url;
|
|
1155
|
+
lightboxCopy.setAttribute("title", "Copy URL");
|
|
1156
|
+
lightboxDownload.href = url;
|
|
1157
|
+
lightboxDownload.setAttribute("download", downloadName || imageDownloadName("inpaint-output"));
|
|
1158
|
+
lightbox.hidden = false;
|
|
1159
|
+
lightbox.setAttribute("data-open", "true");
|
|
1160
|
+
document.body.style.overflow = "hidden";
|
|
1161
|
+
}
|
|
1035
1162
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1163
|
+
function closeImagePreview() {
|
|
1164
|
+
lightbox.setAttribute("data-open", "false");
|
|
1165
|
+
lightbox.hidden = true;
|
|
1166
|
+
lightboxImage.removeAttribute("src");
|
|
1167
|
+
lightboxCopy.dataset.copyUrl = "";
|
|
1168
|
+
lightboxCopy.setAttribute("title", "Copy URL");
|
|
1169
|
+
lightboxDownload.href = "#";
|
|
1170
|
+
document.body.style.overflow = "";
|
|
1171
|
+
}
|
|
1041
1172
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
border-radius: 28px;
|
|
1049
|
-
border: 1px solid rgba(191, 164, 109, 0.28);
|
|
1050
|
-
background: rgba(255, 251, 244, 0.985);
|
|
1051
|
-
box-shadow: 0 28px 80px rgba(15, 23, 42, 0.28);
|
|
1052
|
-
}
|
|
1173
|
+
function updateBrushCursorSize() {
|
|
1174
|
+
const rect = inpaintCanvas.getBoundingClientRect();
|
|
1175
|
+
const brushSize = Math.max(1, Number(inpaintBrush.value) || 40);
|
|
1176
|
+
const displayScale = inpaintCanvas.width && rect.width ? rect.width / inpaintCanvas.width : 1;
|
|
1177
|
+
canvasWrap.style.setProperty("--inpaint-brush-cursor-size", Math.max(6, brushSize * displayScale) + "px");
|
|
1178
|
+
}
|
|
1053
1179
|
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1180
|
+
function updateBrushCursorPosition(event) {
|
|
1181
|
+
const rect = inpaintCanvas.getBoundingClientRect();
|
|
1182
|
+
canvasWrap.style.setProperty("--inpaint-brush-cursor-x", Math.max(0, Math.min(rect.width, event.clientX - rect.left)) + "px");
|
|
1183
|
+
canvasWrap.style.setProperty("--inpaint-brush-cursor-y", Math.max(0, Math.min(rect.height, event.clientY - rect.top)) + "px");
|
|
1184
|
+
updateBrushCursorSize();
|
|
1185
|
+
inpaintBrushCursor.dataset.visible = "true";
|
|
1186
|
+
}
|
|
1060
1187
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
gap: 10px;
|
|
1065
|
-
flex: 0 0 auto;
|
|
1066
|
-
}
|
|
1188
|
+
function hideBrushCursor() {
|
|
1189
|
+
inpaintBrushCursor.dataset.visible = "false";
|
|
1190
|
+
}
|
|
1067
1191
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1192
|
+
function normalizeHexColor(value) {
|
|
1193
|
+
const raw = String(value || "").trim().toLowerCase();
|
|
1194
|
+
if (/^#[0-9a-f]{6}$/i.test(raw)) return raw;
|
|
1195
|
+
if (/^#[0-9a-f]{3}$/i.test(raw)) {
|
|
1196
|
+
return "#" + raw.slice(1).split("").map((part) => part + part).join("");
|
|
1197
|
+
}
|
|
1198
|
+
return "#ff2d55";
|
|
1199
|
+
}
|
|
1075
1200
|
|
|
1076
|
-
|
|
1077
|
-
margin: 0;
|
|
1078
|
-
color: #324156;
|
|
1079
|
-
font-size: 0.95rem;
|
|
1080
|
-
font-weight: 700;
|
|
1081
|
-
word-break: break-all;
|
|
1082
|
-
}
|
|
1201
|
+
const inpaintColorPalette = ["#ff2d55", "#3b82f6", "#22c55e", "#f59e0b", "#a855f7", "#06b6d4", "#f97316", "#ec4899"];
|
|
1083
1202
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1203
|
+
function syncInpaintPromptValuesFromDom() {
|
|
1204
|
+
const fields = Array.from(inpaintPrompts.querySelectorAll("[data-color]"));
|
|
1205
|
+
for (const field of fields) {
|
|
1206
|
+
if (field instanceof HTMLTextAreaElement) {
|
|
1207
|
+
updateInpaintPrompt(field.dataset.color || "", field.value);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1092
1211
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
max-height: calc(100vh - 180px);
|
|
1097
|
-
display: block;
|
|
1098
|
-
object-fit: contain;
|
|
1099
|
-
}
|
|
1212
|
+
function attachmentId() {
|
|
1213
|
+
return "att_" + Date.now().toString(36) + "_" + Math.random().toString(16).slice(2, 8);
|
|
1214
|
+
}
|
|
1100
1215
|
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1216
|
+
function attachmentsForScope(scope, color) {
|
|
1217
|
+
const normalizedColor = color ? normalizeHexColor(color) : "";
|
|
1218
|
+
return inpaintAttachments.filter((attachment) => (
|
|
1219
|
+
attachment.scope === scope && (!normalizedColor || attachment.color === normalizedColor)
|
|
1220
|
+
));
|
|
1221
|
+
}
|
|
1106
1222
|
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1223
|
+
function renderAttachmentChips(container, scope, color) {
|
|
1224
|
+
container.innerHTML = "";
|
|
1225
|
+
for (const attachment of attachmentsForScope(scope, color)) {
|
|
1226
|
+
const chip = document.createElement("span");
|
|
1227
|
+
chip.className = "editor-inpaint-attachment-chip";
|
|
1228
|
+
const preview = document.createElement("img");
|
|
1229
|
+
preview.src = attachment.url;
|
|
1230
|
+
preview.alt = "";
|
|
1231
|
+
preview.loading = "lazy";
|
|
1232
|
+
const label = document.createElement("span");
|
|
1233
|
+
label.textContent = attachment.fileName || "reference image";
|
|
1234
|
+
const remove = document.createElement("button");
|
|
1235
|
+
remove.type = "button";
|
|
1236
|
+
remove.textContent = "×";
|
|
1237
|
+
remove.dataset.editorInpaintRemoveAttachment = attachment.id;
|
|
1238
|
+
remove.setAttribute("aria-label", "Remove " + (attachment.fileName || "reference image"));
|
|
1239
|
+
chip.append(preview, label, remove);
|
|
1240
|
+
container.append(chip);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1110
1243
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1244
|
+
function renderAllAttachmentChips() {
|
|
1245
|
+
renderAttachmentChips(inpaintGlobalAttachments, "global");
|
|
1246
|
+
for (const container of Array.from(inpaintPrompts.querySelectorAll("[data-editor-inpaint-attachments]"))) {
|
|
1247
|
+
if (container instanceof HTMLElement && container.dataset.color) {
|
|
1248
|
+
renderAttachmentChips(container, "color", container.dataset.color);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1114
1252
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
}
|
|
1253
|
+
function openAttachmentPicker(scope, color) {
|
|
1254
|
+
pendingInpaintAttachmentScope = { scope, color: color ? normalizeHexColor(color) : "" };
|
|
1255
|
+
inpaintAttachmentInput.click();
|
|
1256
|
+
}
|
|
1120
1257
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
}
|
|
1258
|
+
function isAllowedInpaintAttachment(file) {
|
|
1259
|
+
return file instanceof File && allowedInpaintAttachmentTypes.has(file.type);
|
|
1260
|
+
}
|
|
1125
1261
|
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1262
|
+
function imageFilesFromPaste(event) {
|
|
1263
|
+
const clipboard = event.clipboardData;
|
|
1264
|
+
if (!clipboard) return [];
|
|
1265
|
+
const files = [];
|
|
1266
|
+
for (const item of Array.from(clipboard.items || [])) {
|
|
1267
|
+
if (item.kind !== "file") continue;
|
|
1268
|
+
const file = item.getAsFile();
|
|
1269
|
+
if (file) files.push(file);
|
|
1270
|
+
}
|
|
1271
|
+
if (!files.length) {
|
|
1272
|
+
for (const file of Array.from(clipboard.files || [])) {
|
|
1273
|
+
files.push(file);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
return files;
|
|
1277
|
+
}
|
|
1129
1278
|
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1279
|
+
function handleAttachmentPaste(event, scope, color) {
|
|
1280
|
+
const files = imageFilesFromPaste(event);
|
|
1281
|
+
if (!files.length) return;
|
|
1282
|
+
event.preventDefault();
|
|
1283
|
+
const allowed = files.filter(isAllowedInpaintAttachment);
|
|
1284
|
+
if (!allowed.length) {
|
|
1285
|
+
setStatus("Reference attachments must be JPEG, PNG, or WebP images.", "error");
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
void addAttachmentFilesForScope(allowed, {
|
|
1289
|
+
scope,
|
|
1290
|
+
color: scope === "color" ? normalizeHexColor(color) : ""
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1134
1293
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1294
|
+
function pickNextInpaintColor(requestedColor) {
|
|
1295
|
+
const requested = normalizeHexColor(requestedColor);
|
|
1296
|
+
const used = new Set(inpaintMaskColors.map((entry) => entry.color));
|
|
1297
|
+
if (!used.has(requested)) {
|
|
1298
|
+
return requested;
|
|
1299
|
+
}
|
|
1300
|
+
for (const color of inpaintColorPalette) {
|
|
1301
|
+
if (!used.has(color)) {
|
|
1302
|
+
return color;
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
for (let index = 0; index < 32; index += 1) {
|
|
1306
|
+
const color = "#" + Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, "0");
|
|
1307
|
+
if (!used.has(color)) {
|
|
1308
|
+
return color;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
return requested;
|
|
1312
|
+
}
|
|
1138
1313
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1314
|
+
function renderInpaintControls() {
|
|
1315
|
+
syncInpaintPromptValuesFromDom();
|
|
1316
|
+
inpaintBrushLabel.textContent = inpaintBrush.value;
|
|
1317
|
+
inpaintSwatches.innerHTML = "";
|
|
1318
|
+
inpaintPrompts.innerHTML = "";
|
|
1319
|
+
for (const entry of inpaintMaskColors) {
|
|
1320
|
+
const row = document.createElement("div");
|
|
1321
|
+
row.className = "editor-inpaint-color-row";
|
|
1322
|
+
row.dataset.color = entry.color;
|
|
1323
|
+
row.dataset.active = entry.color === activeInpaintColor ? "true" : "false";
|
|
1324
|
+
const dot = document.createElement("button");
|
|
1325
|
+
dot.type = "button";
|
|
1326
|
+
dot.className = "editor-inpaint-color-dot";
|
|
1327
|
+
dot.style.setProperty("--inpaint-color", entry.color);
|
|
1328
|
+
dot.dataset.color = entry.color;
|
|
1329
|
+
dot.setAttribute("aria-label", "Use mask color " + entry.color);
|
|
1330
|
+
dot.setAttribute("title", entry.color);
|
|
1331
|
+
const colorTools = document.createElement("div");
|
|
1332
|
+
colorTools.className = "editor-inpaint-color-tools";
|
|
1333
|
+
colorTools.append(dot);
|
|
1334
|
+
if (inpaintMaskColors.length > 1) {
|
|
1335
|
+
const removeColor = document.createElement("button");
|
|
1336
|
+
removeColor.type = "button";
|
|
1337
|
+
removeColor.className = "editor-inpaint-color-remove";
|
|
1338
|
+
removeColor.dataset.editorInpaintRemoveColor = entry.color;
|
|
1339
|
+
removeColor.setAttribute("aria-label", "Remove mask color " + entry.color);
|
|
1340
|
+
removeColor.setAttribute("title", "Remove color");
|
|
1341
|
+
removeColor.innerHTML = '<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M5 5l10 10"></path><path d="M15 5 5 15"></path></svg>';
|
|
1342
|
+
removeColor.addEventListener("click", (event) => {
|
|
1343
|
+
event.preventDefault();
|
|
1344
|
+
event.stopPropagation();
|
|
1345
|
+
removeInpaintColor(entry.color);
|
|
1346
|
+
});
|
|
1347
|
+
colorTools.append(removeColor);
|
|
1348
|
+
}
|
|
1349
|
+
const prompt = document.createElement("textarea");
|
|
1350
|
+
prompt.className = "editor-inpaint-prompt";
|
|
1351
|
+
prompt.dataset.color = entry.color;
|
|
1352
|
+
prompt.value = entry.prompt || "";
|
|
1353
|
+
prompt.placeholder = "In color " + entry.color + " replace with...";
|
|
1354
|
+
const stack = document.createElement("div");
|
|
1355
|
+
stack.className = "editor-inpaint-prompt-stack";
|
|
1356
|
+
const field = document.createElement("div");
|
|
1357
|
+
field.className = "editor-inpaint-prompt-field";
|
|
1358
|
+
const tools = document.createElement("div");
|
|
1359
|
+
tools.className = "editor-inpaint-attachment-tools";
|
|
1360
|
+
const attach = document.createElement("button");
|
|
1361
|
+
attach.type = "button";
|
|
1362
|
+
attach.className = "editor-inpaint-attach-button";
|
|
1363
|
+
attach.dataset.editorInpaintAttachScope = "color";
|
|
1364
|
+
attach.dataset.color = entry.color;
|
|
1365
|
+
attach.setAttribute("aria-label", "Attach reference image for " + entry.color);
|
|
1366
|
+
attach.setAttribute("title", "Attach reference image");
|
|
1367
|
+
attach.innerHTML = '<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m8.1 10.7 4.8-4.8a2.5 2.5 0 0 1 3.5 3.5l-6.1 6.1a4 4 0 0 1-5.7-5.7l6.3-6.3"></path><path d="m10 8.8-4.1 4.1a1.6 1.6 0 0 0 2.3 2.3l5.6-5.6"></path></svg>';
|
|
1368
|
+
const chips = document.createElement("span");
|
|
1369
|
+
chips.className = "editor-inpaint-attachment-chips";
|
|
1370
|
+
chips.dataset.editorInpaintAttachments = "color";
|
|
1371
|
+
chips.dataset.color = entry.color;
|
|
1372
|
+
field.append(prompt, attach);
|
|
1373
|
+
tools.append(chips);
|
|
1374
|
+
stack.append(field, tools);
|
|
1375
|
+
row.append(colorTools, stack);
|
|
1376
|
+
inpaintPrompts.append(row);
|
|
1377
|
+
}
|
|
1378
|
+
renderAllAttachmentChips();
|
|
1379
|
+
}
|
|
1143
1380
|
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1381
|
+
function redrawInpaintMask() {
|
|
1382
|
+
const ctx = inpaintCanvas.getContext("2d");
|
|
1383
|
+
if (!ctx) return;
|
|
1384
|
+
ctx.clearRect(0, 0, inpaintCanvas.width, inpaintCanvas.height);
|
|
1385
|
+
ctx.lineCap = "round";
|
|
1386
|
+
ctx.lineJoin = "round";
|
|
1387
|
+
for (const stroke of inpaintStrokes) {
|
|
1388
|
+
if (!stroke.points || stroke.points.length < 1) continue;
|
|
1389
|
+
ctx.strokeStyle = stroke.color;
|
|
1390
|
+
ctx.lineWidth = stroke.size;
|
|
1391
|
+
ctx.globalAlpha = 1;
|
|
1392
|
+
ctx.beginPath();
|
|
1393
|
+
ctx.moveTo(stroke.points[0].x, stroke.points[0].y);
|
|
1394
|
+
if (stroke.points.length === 1) {
|
|
1395
|
+
ctx.lineTo(stroke.points[0].x + 0.01, stroke.points[0].y + 0.01);
|
|
1396
|
+
} else {
|
|
1397
|
+
for (let index = 1; index < stroke.points.length; index += 1) {
|
|
1398
|
+
ctx.lineTo(stroke.points[index].x, stroke.points[index].y);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
ctx.stroke();
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1150
1404
|
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1405
|
+
function syncInpaintCanvasSize() {
|
|
1406
|
+
if (!inpaintImage.naturalWidth || !inpaintImage.naturalHeight) return;
|
|
1407
|
+
if (inpaintCanvas.width !== inpaintImage.naturalWidth || inpaintCanvas.height !== inpaintImage.naturalHeight) {
|
|
1408
|
+
inpaintCanvas.width = inpaintImage.naturalWidth;
|
|
1409
|
+
inpaintCanvas.height = inpaintImage.naturalHeight;
|
|
1410
|
+
redrawInpaintMask();
|
|
1411
|
+
}
|
|
1412
|
+
updateBrushCursorSize();
|
|
1413
|
+
}
|
|
1155
1414
|
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1415
|
+
function getInpaintPoint(event) {
|
|
1416
|
+
const rect = inpaintCanvas.getBoundingClientRect();
|
|
1417
|
+
return {
|
|
1418
|
+
x: Math.max(0, Math.min(inpaintCanvas.width, (event.clientX - rect.left) * (inpaintCanvas.width / rect.width))),
|
|
1419
|
+
y: Math.max(0, Math.min(inpaintCanvas.height, (event.clientY - rect.top) * (inpaintCanvas.height / rect.height)))
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1163
1422
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1423
|
+
function beginInpaintStroke(event) {
|
|
1424
|
+
if (!sourceImageUrl || !inpaintCanvas.width || inpaintSubmit.disabled) return;
|
|
1425
|
+
event.preventDefault();
|
|
1426
|
+
inpaintCanvas.setPointerCapture(event.pointerId);
|
|
1427
|
+
isInpaintPainting = true;
|
|
1428
|
+
currentInpaintStroke = {
|
|
1429
|
+
color: activeInpaintColor,
|
|
1430
|
+
size: Number(inpaintBrush.value) || 40,
|
|
1431
|
+
points: [getInpaintPoint(event)]
|
|
1432
|
+
};
|
|
1433
|
+
inpaintStrokes.push(currentInpaintStroke);
|
|
1434
|
+
redoStrokes = [];
|
|
1435
|
+
redrawInpaintMask();
|
|
1436
|
+
}
|
|
1169
1437
|
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
text-overflow: ellipsis;
|
|
1176
|
-
white-space: nowrap;
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
`,
|
|
1180
|
-
body: `
|
|
1181
|
-
<section class="editor-workbench">
|
|
1182
|
-
${renderTemplateEditorChrome({
|
|
1183
|
-
backHref,
|
|
1184
|
-
templateTitle: input.template.title,
|
|
1185
|
-
templateId: input.template.id,
|
|
1186
|
-
templateSlug: input.template.slugId
|
|
1187
|
-
})}
|
|
1188
|
-
<main class="editor-main">
|
|
1189
|
-
<section class="editor-shell page-content-start">
|
|
1190
|
-
<details class="editor-panel" open>
|
|
1191
|
-
<summary>
|
|
1192
|
-
<div class="editor-panel-summary">
|
|
1193
|
-
<h2>Character card</h2>
|
|
1194
|
-
<p>Preview the format, metadata, and editorial notes without losing space for the sections that follow.</p>
|
|
1195
|
-
</div>
|
|
1196
|
-
<span class="editor-panel-toggle" aria-hidden="true">Toggle</span>
|
|
1197
|
-
</summary>
|
|
1198
|
-
<div class="editor-panel-body">
|
|
1199
|
-
<section class="editor-hero">
|
|
1200
|
-
${preview}
|
|
1201
|
-
<div class="editor-copy">
|
|
1202
|
-
<div class="editor-kicker">Template Editor Docs</div>
|
|
1203
|
-
<h1 class="editor-title">${escapeHtml(input.template.title)}</h1>
|
|
1204
|
-
<div class="editor-pills">
|
|
1205
|
-
<span class="editor-pill">${escapeHtml(input.template.id)}</span>
|
|
1206
|
-
<span class="editor-pill">${escapeHtml(input.template.slugId)}</span>
|
|
1207
|
-
<span class="editor-pill">v${escapeHtml(input.template.version)}</span>
|
|
1208
|
-
</div>
|
|
1209
|
-
<p>${escapeHtml(input.template.viralDna || input.template.visualDna || "No editorial summary is available for this template yet.")}</p>
|
|
1210
|
-
${input.directorSkill?.trim()
|
|
1211
|
-
? `<div class="editor-toolbar">
|
|
1212
|
-
<button
|
|
1213
|
-
type="button"
|
|
1214
|
-
class="editor-copy-button"
|
|
1215
|
-
data-editor-copy-content="${escapeAttribute(input.directorSkill)}"
|
|
1216
|
-
data-editor-copy-label="Director SKILL.md"
|
|
1217
|
-
>Copy Director SKILL.md</button>
|
|
1218
|
-
</div>`
|
|
1219
|
-
: ""}
|
|
1220
|
-
</div>
|
|
1221
|
-
</section>
|
|
1222
|
-
</div>
|
|
1223
|
-
</details>
|
|
1224
|
-
<section class="editor-grid">
|
|
1225
|
-
<details class="editor-panel" open>
|
|
1226
|
-
<summary>
|
|
1227
|
-
<div class="editor-panel-summary">
|
|
1228
|
-
<h2>Quickstart</h2>
|
|
1229
|
-
<p>Use the template prompt scaffolds as readonly references before calling operations or continuing in Copilot.</p>
|
|
1230
|
-
</div>
|
|
1231
|
-
<span class="editor-panel-toggle" aria-hidden="true">Toggle</span>
|
|
1232
|
-
</summary>
|
|
1233
|
-
<div class="editor-panel-body">
|
|
1234
|
-
<section class="editor-section">
|
|
1235
|
-
<div class="editor-quickstart-grid">
|
|
1236
|
-
${renderQuickstartTextarea("Skeleton prompt", input.template.skeletonPrompt, 10)}
|
|
1237
|
-
${renderQuickstartTextarea("Proposal Generator", input.template.proposalGenerator, 10)}
|
|
1238
|
-
</div>
|
|
1239
|
-
<div class="editor-quickstart-samples">
|
|
1240
|
-
<h3>Sample prompts</h3>
|
|
1241
|
-
<div class="editor-sample-prompt-row" aria-label="Sample prompts">
|
|
1242
|
-
${input.template.samplePrompts.length
|
|
1243
|
-
? input.template.samplePrompts.map((sample, index) => `
|
|
1244
|
-
<div class="editor-sample-prompt">
|
|
1245
|
-
${renderQuickstartTextarea(`Sample ${index + 1}`, sample, 7)}
|
|
1246
|
-
</div>
|
|
1247
|
-
`).join("")
|
|
1248
|
-
: `<div class="editor-sample-prompt">${renderQuickstartTextarea("Sample 1", "No sample prompts are defined for this template yet.", 7)}</div>`}
|
|
1249
|
-
</div>
|
|
1250
|
-
</div>
|
|
1251
|
-
</section>
|
|
1252
|
-
</div>
|
|
1253
|
-
</details>
|
|
1254
|
-
<details class="editor-panel">
|
|
1255
|
-
<summary>
|
|
1256
|
-
<div class="editor-panel-summary">
|
|
1257
|
-
<h2>REST API docs</h2>
|
|
1258
|
-
<p>This editor combines template routes with platform primitive routes so asset generation, image edits, and template runs stay in one tracer-aware workflow.</p>
|
|
1259
|
-
</div>
|
|
1260
|
-
<span class="editor-panel-toggle" aria-hidden="true">Toggle</span>
|
|
1261
|
-
</summary>
|
|
1262
|
-
<div class="editor-panel-body">
|
|
1263
|
-
<section class="editor-section">
|
|
1264
|
-
<div class="editor-section-header">
|
|
1265
|
-
<p>Use tracer IDs to group a run across requests.</p>
|
|
1266
|
-
</div>
|
|
1267
|
-
<div class="editor-route-list">
|
|
1268
|
-
${input.docsRoutes.map((route) => `
|
|
1269
|
-
<article class="editor-route-card">
|
|
1270
|
-
<div class="editor-route-head">
|
|
1271
|
-
<span class="editor-route-method" data-method="${escapeAttribute(route.method)}">${escapeHtml(route.method)}</span>
|
|
1272
|
-
<div class="editor-route-path">${escapeHtml(route.path)}</div>
|
|
1273
|
-
</div>
|
|
1274
|
-
<div class="editor-note">${escapeHtml(route.summary)}</div>
|
|
1275
|
-
</article>
|
|
1276
|
-
`).join("")}
|
|
1277
|
-
</div>
|
|
1278
|
-
</section>
|
|
1279
|
-
</div>
|
|
1280
|
-
</details>
|
|
1281
|
-
<details class="editor-panel" open>
|
|
1282
|
-
<summary>
|
|
1283
|
-
<div class="editor-panel-summary">
|
|
1284
|
-
<h2>Tracer Logs</h2>
|
|
1285
|
-
<p>Recent POST calls are grouped by tracer so parallel workstreams stay readable. Newest requests appear first inside each tracer group.</p>
|
|
1286
|
-
</div>
|
|
1287
|
-
<span class="editor-panel-toggle" aria-hidden="true">Toggle</span>
|
|
1288
|
-
</summary>
|
|
1289
|
-
<div class="editor-panel-body">
|
|
1290
|
-
<section class="editor-section">
|
|
1291
|
-
<div class="editor-history-toolbar">
|
|
1292
|
-
<p class="editor-note">Tracer visibility lives here. Removing one only clears it from this page view; the underlying runs remain available in Job Runs.</p>
|
|
1293
|
-
<div class="editor-history-toolbar-actions">
|
|
1294
|
-
<button type="button" class="editor-history-button is-primary" data-editor-history-add>Add tracer</button>
|
|
1295
|
-
<div class="editor-history-chip-row" data-editor-history-tracers>
|
|
1296
|
-
${input.initialTracers.length
|
|
1297
|
-
? input.initialTracers.map((tracer) => `<span class="editor-history-chip">${escapeHtml(tracer)}</span>`).join("")
|
|
1298
|
-
: `<span class="editor-history-chip is-empty">No tracer selected yet</span>`}
|
|
1299
|
-
</div>
|
|
1300
|
-
</div>
|
|
1301
|
-
</div>
|
|
1302
|
-
<div class="editor-history-groups" data-editor-history-root>
|
|
1303
|
-
<div class="editor-history-empty">${hasHistoryAccess ? "Loading POST history…" : "Sign in to load tracer-scoped POST history."}</div>
|
|
1304
|
-
</div>
|
|
1305
|
-
</section>
|
|
1306
|
-
</div>
|
|
1307
|
-
</details>
|
|
1308
|
-
</section>
|
|
1309
|
-
</section>
|
|
1310
|
-
</main>
|
|
1311
|
-
</section>
|
|
1312
|
-
<div class="editor-modal-backdrop" data-editor-tracer-modal data-open="false" hidden>
|
|
1313
|
-
<div class="editor-modal" role="dialog" aria-modal="true" aria-labelledby="editor-tracer-modal-title">
|
|
1314
|
-
<div class="editor-modal-head">
|
|
1315
|
-
<h3 id="editor-tracer-modal-title">Add tracer</h3>
|
|
1316
|
-
<p>Type a tracer label to pin it into this page view. Existing labels stay untouched.</p>
|
|
1317
|
-
</div>
|
|
1318
|
-
<label class="editor-modal-label" for="editor-tracer-input">Tracer id</label>
|
|
1319
|
-
<input id="editor-tracer-input" class="editor-modal-input" type="text" autocomplete="off" spellcheck="false" placeholder="${escapeAttribute(`${input.template.slugId}-manual`)}" />
|
|
1320
|
-
<p class="editor-modal-error" data-editor-tracer-modal-error aria-live="polite"></p>
|
|
1321
|
-
<div class="editor-modal-actions">
|
|
1322
|
-
<button type="button" class="editor-history-button" data-editor-tracer-cancel>Cancel</button>
|
|
1323
|
-
<button type="button" class="editor-history-button is-primary" data-editor-tracer-submit>Add tracer</button>
|
|
1324
|
-
</div>
|
|
1325
|
-
</div>
|
|
1326
|
-
</div>
|
|
1327
|
-
<div class="editor-modal-backdrop" data-editor-media-modal data-open="false" hidden>
|
|
1328
|
-
<div class="editor-media-modal" role="dialog" aria-modal="true" aria-labelledby="editor-media-modal-title">
|
|
1329
|
-
<div class="editor-media-modal-head">
|
|
1330
|
-
<p id="editor-media-modal-title" class="editor-media-modal-title">Output preview</p>
|
|
1331
|
-
<div class="editor-media-modal-actions">
|
|
1332
|
-
<a class="editor-media-modal-source" data-editor-media-source href="#" target="_blank" rel="noreferrer">Open source</a>
|
|
1333
|
-
<button type="button" class="editor-history-button" data-editor-media-close>Close</button>
|
|
1334
|
-
</div>
|
|
1335
|
-
</div>
|
|
1336
|
-
<div class="editor-media-modal-body" data-editor-media-body></div>
|
|
1337
|
-
</div>
|
|
1338
|
-
</div>
|
|
1339
|
-
<div class="vf-editor-chat-http-modal-backdrop" data-editor-http-modal hidden>
|
|
1340
|
-
<div class="vf-editor-chat-http-modal" role="dialog" aria-modal="true" aria-labelledby="editor-http-modal-title">
|
|
1341
|
-
<div class="vf-editor-chat-http-modal-header">
|
|
1342
|
-
<div class="vf-editor-chat-http-modal-header-main" data-editor-http-modal-head>
|
|
1343
|
-
<span id="editor-http-modal-title" class="vf-editor-chat-http-modal-url">HTTP details</span>
|
|
1344
|
-
</div>
|
|
1345
|
-
<button type="button" class="vf-editor-chat-http-modal-close" data-editor-http-close aria-label="Close HTTP details">×</button>
|
|
1346
|
-
</div>
|
|
1347
|
-
<div class="vf-editor-chat-http-card-body is-modal" data-editor-http-modal-body></div>
|
|
1348
|
-
</div>
|
|
1349
|
-
</div>
|
|
1350
|
-
<script>
|
|
1351
|
-
${TEMPLATE_EDITOR_SHELL_SCRIPT}
|
|
1352
|
-
(() => {
|
|
1353
|
-
const historyRoot = document.querySelector("[data-editor-history-root]");
|
|
1354
|
-
const tracerChipRow = document.querySelector("[data-editor-history-tracers]");
|
|
1355
|
-
const addTracerButton = document.querySelector("[data-editor-history-add]");
|
|
1356
|
-
const modalBackdrop = document.querySelector("[data-editor-tracer-modal]");
|
|
1357
|
-
const modalInput = document.getElementById("editor-tracer-input");
|
|
1358
|
-
const modalError = document.querySelector("[data-editor-tracer-modal-error]");
|
|
1359
|
-
const modalSubmit = document.querySelector("[data-editor-tracer-submit]");
|
|
1360
|
-
const modalCancel = document.querySelector("[data-editor-tracer-cancel]");
|
|
1361
|
-
const mediaModalBackdrop = document.querySelector("[data-editor-media-modal]");
|
|
1362
|
-
const mediaModalBody = document.querySelector("[data-editor-media-body]");
|
|
1363
|
-
const mediaModalTitle = document.getElementById("editor-media-modal-title");
|
|
1364
|
-
const mediaModalClose = document.querySelector("[data-editor-media-close]");
|
|
1365
|
-
const mediaModalSource = document.querySelector("[data-editor-media-source]");
|
|
1366
|
-
const httpModalBackdrop = document.querySelector("[data-editor-http-modal]");
|
|
1367
|
-
const httpModalHead = document.querySelector("[data-editor-http-modal-head]");
|
|
1368
|
-
const httpModalBody = document.querySelector("[data-editor-http-modal-body]");
|
|
1369
|
-
const httpModalClose = document.querySelector("[data-editor-http-close]");
|
|
1370
|
-
if (!(historyRoot instanceof HTMLElement) || !(tracerChipRow instanceof HTMLElement)) {
|
|
1371
|
-
return;
|
|
1438
|
+
function continueInpaintStroke(event) {
|
|
1439
|
+
if (!isInpaintPainting || !currentInpaintStroke) return;
|
|
1440
|
+
event.preventDefault();
|
|
1441
|
+
currentInpaintStroke.points.push(getInpaintPoint(event));
|
|
1442
|
+
redrawInpaintMask();
|
|
1372
1443
|
}
|
|
1373
1444
|
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
&& modalCancel instanceof HTMLButtonElement;
|
|
1380
|
-
const hasMediaModal = mediaModalBackdrop instanceof HTMLElement
|
|
1381
|
-
&& mediaModalBody instanceof HTMLElement
|
|
1382
|
-
&& mediaModalTitle instanceof HTMLElement
|
|
1383
|
-
&& mediaModalClose instanceof HTMLButtonElement
|
|
1384
|
-
&& mediaModalSource instanceof HTMLAnchorElement;
|
|
1385
|
-
const hasHttpModal = httpModalBackdrop instanceof HTMLElement
|
|
1386
|
-
&& httpModalHead instanceof HTMLElement
|
|
1387
|
-
&& httpModalBody instanceof HTMLElement
|
|
1388
|
-
&& httpModalClose instanceof HTMLButtonElement;
|
|
1389
|
-
|
|
1390
|
-
const boot = {
|
|
1391
|
-
historyEndpoint: ${JSON.stringify(input.historyEndpoint)},
|
|
1392
|
-
tracerEndpoint: ${JSON.stringify(input.tracerEndpoint)},
|
|
1393
|
-
fallbackHistoryEndpoint: ${JSON.stringify(withAccountQuery(`/job-runs/history?limit=250&template_id=${encodeURIComponent(input.template.id)}`, input.account.userId))},
|
|
1394
|
-
isLoggedIn: ${input.account.isLoggedIn ? "true" : "false"}
|
|
1395
|
-
};
|
|
1396
|
-
let lastHistoryEntries = [];
|
|
1397
|
-
let lastSelectedTracers = [];
|
|
1398
|
-
const expandedPreviewGroups = new Set();
|
|
1399
|
-
|
|
1400
|
-
function escapeHtml(value) {
|
|
1401
|
-
return String(value)
|
|
1402
|
-
.replace(/&/g, "&")
|
|
1403
|
-
.replace(/</g, "<")
|
|
1404
|
-
.replace(/>/g, ">")
|
|
1405
|
-
.replace(/"/g, """);
|
|
1406
|
-
}
|
|
1407
|
-
|
|
1408
|
-
function formatJson(value) {
|
|
1409
|
-
if (value == null) {
|
|
1410
|
-
return "null";
|
|
1411
|
-
}
|
|
1412
|
-
if (typeof value === "string") {
|
|
1413
|
-
return value;
|
|
1414
|
-
}
|
|
1445
|
+
function endInpaintStroke(event) {
|
|
1446
|
+
if (!isInpaintPainting) return;
|
|
1447
|
+
event.preventDefault();
|
|
1448
|
+
isInpaintPainting = false;
|
|
1449
|
+
currentInpaintStroke = null;
|
|
1415
1450
|
try {
|
|
1416
|
-
|
|
1417
|
-
} catch {
|
|
1418
|
-
|
|
1451
|
+
inpaintCanvas.releasePointerCapture(event.pointerId);
|
|
1452
|
+
} catch {}
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
function addInpaintColor(color) {
|
|
1456
|
+
syncInpaintPromptValuesFromDom();
|
|
1457
|
+
const normalized = pickNextInpaintColor(color);
|
|
1458
|
+
if (!inpaintMaskColors.some((entry) => entry.color === normalized)) {
|
|
1459
|
+
inpaintMaskColors.push({ color: normalized, prompt: "" });
|
|
1419
1460
|
}
|
|
1461
|
+
activeInpaintColor = normalized;
|
|
1462
|
+
inpaintColorPicker.value = normalized;
|
|
1463
|
+
renderInpaintControls();
|
|
1420
1464
|
}
|
|
1421
1465
|
|
|
1422
|
-
function
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1466
|
+
function removeInpaintColor(color) {
|
|
1467
|
+
syncInpaintPromptValuesFromDom();
|
|
1468
|
+
const normalized = normalizeHexColor(color);
|
|
1469
|
+
if (inpaintMaskColors.length <= 1 || !inpaintMaskColors.some((entry) => entry.color === normalized)) {
|
|
1470
|
+
setStatus("Keep at least one mask color.", "error");
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1473
|
+
inpaintMaskColors = inpaintMaskColors.filter((entry) => entry.color !== normalized);
|
|
1474
|
+
inpaintAttachments = inpaintAttachments.filter((attachment) => attachment.scope !== "color" || attachment.color !== normalized);
|
|
1475
|
+
inpaintStrokes = inpaintStrokes.filter((stroke) => normalizeHexColor(stroke.color) !== normalized);
|
|
1476
|
+
redoStrokes = redoStrokes.filter((stroke) => normalizeHexColor(stroke.color) !== normalized);
|
|
1477
|
+
if (activeInpaintColor === normalized) {
|
|
1478
|
+
activeInpaintColor = inpaintMaskColors[0]?.color || "#ff2d55";
|
|
1479
|
+
inpaintColorPicker.value = activeInpaintColor;
|
|
1480
|
+
}
|
|
1481
|
+
redrawInpaintMask();
|
|
1482
|
+
renderInpaintControls();
|
|
1483
|
+
setStatus("", "");
|
|
1433
1484
|
}
|
|
1434
1485
|
|
|
1435
|
-
function
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
request: {
|
|
1442
|
-
query: entry.request?.query || {},
|
|
1443
|
-
body: entry.request?.body ?? null,
|
|
1444
|
-
omitted: entry.request?.bodyOmittedReason || null
|
|
1445
|
-
},
|
|
1446
|
-
response: {
|
|
1447
|
-
status: entry.status,
|
|
1448
|
-
body: entry.response?.body ?? null,
|
|
1449
|
-
omitted: entry.response?.bodyOmittedReason || null
|
|
1486
|
+
function selectInpaintColor(color) {
|
|
1487
|
+
activeInpaintColor = normalizeHexColor(color);
|
|
1488
|
+
inpaintColorPicker.value = activeInpaintColor;
|
|
1489
|
+
for (const row of Array.from(inpaintPrompts.querySelectorAll(".editor-inpaint-color-row"))) {
|
|
1490
|
+
if (row instanceof HTMLElement) {
|
|
1491
|
+
row.dataset.active = row.dataset.color === activeInpaintColor ? "true" : "false";
|
|
1450
1492
|
}
|
|
1451
|
-
};
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
function encodeHttpPayload(entry) {
|
|
1455
|
-
try {
|
|
1456
|
-
return encodeURIComponent(JSON.stringify(makeHttpPayload(entry)));
|
|
1457
|
-
} catch {
|
|
1458
|
-
return encodeURIComponent(JSON.stringify({
|
|
1459
|
-
method: entry.method,
|
|
1460
|
-
url: entry.path,
|
|
1461
|
-
status: Number(entry.status) || 0,
|
|
1462
|
-
statusText: "",
|
|
1463
|
-
request: { query: {}, body: null, omitted: "Request sample could not be encoded for display." },
|
|
1464
|
-
response: { status: entry.status, body: null, omitted: "Response sample could not be encoded for display." }
|
|
1465
|
-
}));
|
|
1466
1493
|
}
|
|
1467
1494
|
}
|
|
1468
1495
|
|
|
1469
|
-
function
|
|
1470
|
-
const
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
return '<div class="vf-editor-chat-http-card">' +
|
|
1475
|
-
'<button type="button" class="vf-editor-chat-http-card-header" data-editor-http-detail="' + escapeHtml(payload) + '" aria-label="Open HTTP details for ' + escapeHtml(entry.method) + ' ' + escapeHtml(entry.path) + '">' +
|
|
1476
|
-
'<span class="vf-editor-chat-http-pill">' + escapeHtml(entry.method) + '</span>' +
|
|
1477
|
-
'<span class="vf-editor-chat-http-url" title="' + escapeHtml(entry.path) + '">' + escapeHtml(entry.path) + '</span>' +
|
|
1478
|
-
'<span class="vf-editor-chat-http-status" data-error="' + (isError ? "true" : "false") + '" data-success="' + (isSuccess ? "true" : "false") + '">' + escapeHtml(status) + '</span>' +
|
|
1479
|
-
'<span class="vf-editor-chat-http-expand" aria-hidden="true">+</span>' +
|
|
1480
|
-
'</button>' +
|
|
1481
|
-
'</div>';
|
|
1496
|
+
function updateInpaintPrompt(color, prompt) {
|
|
1497
|
+
const normalized = normalizeHexColor(color);
|
|
1498
|
+
inpaintMaskColors = inpaintMaskColors.map((entry) => (
|
|
1499
|
+
entry.color === normalized ? { ...entry, prompt: String(prompt || "") } : entry
|
|
1500
|
+
));
|
|
1482
1501
|
}
|
|
1483
1502
|
|
|
1484
|
-
function
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
const
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
url.className = "vf-editor-chat-http-modal-url";
|
|
1496
|
-
url.textContent = payload.url || "";
|
|
1497
|
-
const status = document.createElement("span");
|
|
1498
|
-
status.className = "vf-editor-chat-http-status";
|
|
1499
|
-
status.dataset.error = isError ? "true" : "false";
|
|
1500
|
-
status.dataset.success = isSuccess ? "true" : "false";
|
|
1501
|
-
status.textContent = String(payload.status || "");
|
|
1502
|
-
httpModalHead.append(method, url, status);
|
|
1503
|
-
httpModalBody.innerHTML = "";
|
|
1504
|
-
setTextBlock(httpModalBody, "URL", payload.url || "");
|
|
1505
|
-
if (payload.request?.omitted) {
|
|
1506
|
-
setTextBlock(httpModalBody, "Request note", payload.request.omitted);
|
|
1507
|
-
}
|
|
1508
|
-
setTextBlock(httpModalBody, "Request", {
|
|
1509
|
-
query: payload.request?.query || {},
|
|
1510
|
-
body: payload.request?.body ?? null
|
|
1503
|
+
function buildInpaintInstruction() {
|
|
1504
|
+
const globalPrompt = inpaintGlobalPrompt.value.trim();
|
|
1505
|
+
const regions = inpaintMaskColors
|
|
1506
|
+
.map((entry) => ({ color: entry.color.toUpperCase(), prompt: String(entry.prompt || "").trim() }))
|
|
1507
|
+
.filter((entry) => entry.prompt);
|
|
1508
|
+
if (!globalPrompt && !regions.length) return "";
|
|
1509
|
+
const attachmentNotes = inpaintAttachments.map((attachment, index) => {
|
|
1510
|
+
const label = "reference image " + (index + 1) + " (" + (attachment.fileName || "attachment") + ")";
|
|
1511
|
+
return attachment.scope === "global"
|
|
1512
|
+
? label + " applies to the whole image instruction"
|
|
1513
|
+
: label + " applies only to the UI mask color " + String(attachment.color || "").toUpperCase();
|
|
1511
1514
|
});
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1515
|
+
return [
|
|
1516
|
+
"INPAINTING SYSTEM WRAPPER:",
|
|
1517
|
+
"You are editing an existing image with an alpha mask. Treat transparent mask pixels as the only editable region. Everything outside the editable mask must remain the same image: preserve identity, layout, pose, camera angle, framing, background, lighting, colors, textures, text, logos, and all unmasked details.",
|
|
1518
|
+
"Do not redraw, reinterpret, beautify, crop, zoom, rotate, expand, stylize, or otherwise alter unmasked content. Do not render the UI mask colors; they are labels only.",
|
|
1519
|
+
"The final image must keep the original dimensions and aspect ratio unless the user explicitly asks for a dimension change.",
|
|
1520
|
+
globalPrompt ? "Whole image prompt: " + globalPrompt + ". Apply this only as guidance for the masked edit and overall consistency; it does not permit changing unmasked content." : "",
|
|
1521
|
+
regions.map((entry) => (
|
|
1522
|
+
"Masked region " + entry.color + ": replace only that marked area with " + entry.prompt + ". Match the surrounding perspective, lighting, grain, shadows, and edge blending."
|
|
1523
|
+
)).join(" "),
|
|
1524
|
+
attachmentNotes.length ? "Reference image scope: " + attachmentNotes.join("; ") + ". Use each reference only for its stated scope and do not copy unrelated reference details into unmasked areas." : ""
|
|
1525
|
+
].filter(Boolean).join(" ");
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
function getInpaintAspectRatio() {
|
|
1529
|
+
const width = inpaintCanvas.width || inpaintImage.naturalWidth || 0;
|
|
1530
|
+
const height = inpaintCanvas.height || inpaintImage.naturalHeight || 0;
|
|
1531
|
+
if (!width || !height) return undefined;
|
|
1532
|
+
const ratio = width / height;
|
|
1533
|
+
if (Math.abs(ratio - 1) < 0.08) return "1:1";
|
|
1534
|
+
if (ratio > 1) return ratio >= 1.55 ? "16:9" : "4:3";
|
|
1535
|
+
return ratio <= 0.65 ? "9:16" : "3:4";
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
function canvasToPngBlob(canvas) {
|
|
1539
|
+
return new Promise((resolve, reject) => {
|
|
1540
|
+
canvas.toBlob((blob) => blob ? resolve(blob) : reject(new Error("Unable to prepare image.")), "image/png");
|
|
1518
1541
|
});
|
|
1519
|
-
httpModalBackdrop.hidden = false;
|
|
1520
1542
|
}
|
|
1521
1543
|
|
|
1522
|
-
function
|
|
1523
|
-
|
|
1524
|
-
|
|
1544
|
+
async function createProviderMaskBlob() {
|
|
1545
|
+
const maskCanvas = document.createElement("canvas");
|
|
1546
|
+
maskCanvas.width = inpaintCanvas.width;
|
|
1547
|
+
maskCanvas.height = inpaintCanvas.height;
|
|
1548
|
+
const ctx = maskCanvas.getContext("2d");
|
|
1549
|
+
if (!ctx) throw new Error("Unable to prepare mask image.");
|
|
1550
|
+
ctx.fillStyle = "#ffffff";
|
|
1551
|
+
ctx.fillRect(0, 0, maskCanvas.width, maskCanvas.height);
|
|
1552
|
+
ctx.globalCompositeOperation = "destination-out";
|
|
1553
|
+
ctx.lineCap = "round";
|
|
1554
|
+
ctx.lineJoin = "round";
|
|
1555
|
+
for (const stroke of inpaintStrokes) {
|
|
1556
|
+
if (!stroke.points || stroke.points.length < 1) continue;
|
|
1557
|
+
ctx.strokeStyle = "rgba(0, 0, 0, 1)";
|
|
1558
|
+
ctx.lineWidth = stroke.size;
|
|
1559
|
+
ctx.beginPath();
|
|
1560
|
+
ctx.moveTo(stroke.points[0].x, stroke.points[0].y);
|
|
1561
|
+
if (stroke.points.length === 1) {
|
|
1562
|
+
ctx.lineTo(stroke.points[0].x + 0.01, stroke.points[0].y + 0.01);
|
|
1563
|
+
} else {
|
|
1564
|
+
for (let index = 1; index < stroke.points.length; index += 1) {
|
|
1565
|
+
ctx.lineTo(stroke.points[index].x, stroke.points[index].y);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
ctx.stroke();
|
|
1525
1569
|
}
|
|
1526
|
-
|
|
1527
|
-
httpModalBody.innerHTML = "";
|
|
1570
|
+
return canvasToPngBlob(maskCanvas);
|
|
1528
1571
|
}
|
|
1529
1572
|
|
|
1530
|
-
function
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
const
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
const
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1573
|
+
function parseJsonResponse(response) {
|
|
1574
|
+
return response.json().catch(() => ({}));
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
async function loadInpaintProviders() {
|
|
1578
|
+
try {
|
|
1579
|
+
const response = await fetch("/api/v1/user/me/provider-keys", { credentials: "same-origin" });
|
|
1580
|
+
const payload = await parseJsonResponse(response);
|
|
1581
|
+
if (!response.ok) return;
|
|
1582
|
+
const supported = new Set(["openai", "gemini", "openrouter"]);
|
|
1583
|
+
const providers = [];
|
|
1584
|
+
const seen = new Set();
|
|
1585
|
+
for (const key of Array.isArray(payload.provider_keys) ? payload.provider_keys : []) {
|
|
1586
|
+
const provider = String(key.provider || "");
|
|
1587
|
+
const status = String(key.status || "active");
|
|
1588
|
+
const cooldownUntil = key.cooldown_until ? Date.parse(String(key.cooldown_until)) : 0;
|
|
1589
|
+
if (!supported.has(provider) || status !== "active" || (cooldownUntil && cooldownUntil > Date.now()) || seen.has(provider)) {
|
|
1544
1590
|
continue;
|
|
1545
1591
|
}
|
|
1546
|
-
seen.add(
|
|
1547
|
-
|
|
1592
|
+
seen.add(provider);
|
|
1593
|
+
providers.push(provider);
|
|
1594
|
+
}
|
|
1595
|
+
inpaintProviderSelect.innerHTML = '<option value="">Auto</option>' + providers.map((provider) => (
|
|
1596
|
+
'<option value="' + provider + '">' + provider.charAt(0).toUpperCase() + provider.slice(1) + '</option>'
|
|
1597
|
+
)).join("");
|
|
1598
|
+
inpaintProviderWrap.setAttribute("data-visible", providers.length > 1 ? "true" : "false");
|
|
1599
|
+
} catch {}
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
async function uploadTemporaryBlob(blob, fileName, contentType) {
|
|
1603
|
+
const presignResponse = await fetch("/api/v1/user/me/temporary-files/presign", {
|
|
1604
|
+
method: "POST",
|
|
1605
|
+
credentials: "same-origin",
|
|
1606
|
+
headers: { "content-type": "application/json" },
|
|
1607
|
+
body: JSON.stringify({
|
|
1608
|
+
file_name: fileName,
|
|
1609
|
+
content_type: contentType,
|
|
1610
|
+
size_bytes: blob.size,
|
|
1611
|
+
folder_path: "inpaint"
|
|
1612
|
+
})
|
|
1613
|
+
});
|
|
1614
|
+
const presignPayload = await parseJsonResponse(presignResponse);
|
|
1615
|
+
if (!presignResponse.ok || !presignPayload || !presignPayload.transport) {
|
|
1616
|
+
throw new Error(presignPayload && presignPayload.error ? presignPayload.error : "Unable to prepare upload.");
|
|
1617
|
+
}
|
|
1618
|
+
if (presignPayload.transport === "server") {
|
|
1619
|
+
const formData = new FormData();
|
|
1620
|
+
formData.append(presignPayload.upload?.form_field || "file", new File([blob], fileName, { type: contentType }));
|
|
1621
|
+
formData.append("folder_path", "inpaint");
|
|
1622
|
+
const uploadResponse = await fetch(presignPayload.upload?.url || "/api/v1/user/me/temporary-files/upload", {
|
|
1623
|
+
method: presignPayload.upload?.method || "POST",
|
|
1624
|
+
credentials: "same-origin",
|
|
1625
|
+
body: formData
|
|
1626
|
+
});
|
|
1627
|
+
const uploadPayload = await parseJsonResponse(uploadResponse);
|
|
1628
|
+
if (!uploadResponse.ok || !uploadPayload.file) {
|
|
1629
|
+
throw new Error(uploadPayload.error || "Unable to upload file.");
|
|
1548
1630
|
}
|
|
1631
|
+
return uploadPayload.file.viewUrl || uploadPayload.file.view_url || uploadPayload.file.s3Url || uploadPayload.file.s3_url;
|
|
1632
|
+
}
|
|
1633
|
+
const uploadResponse = await fetch(presignPayload.upload.url, {
|
|
1634
|
+
method: presignPayload.upload.method,
|
|
1635
|
+
headers: presignPayload.upload.headers,
|
|
1636
|
+
body: blob
|
|
1637
|
+
});
|
|
1638
|
+
if (!uploadResponse.ok) throw new Error("Upload failed with status " + uploadResponse.status + ".");
|
|
1639
|
+
const finalizeResponse = await fetch("/api/v1/user/me/temporary-files", {
|
|
1640
|
+
method: "POST",
|
|
1641
|
+
credentials: "same-origin",
|
|
1642
|
+
headers: { "content-type": "application/json" },
|
|
1643
|
+
body: JSON.stringify({
|
|
1644
|
+
file_id: presignPayload.file_id,
|
|
1645
|
+
file_name: presignPayload.file_name,
|
|
1646
|
+
content_type: presignPayload.content_type,
|
|
1647
|
+
size_bytes: blob.size,
|
|
1648
|
+
storage_key: presignPayload.storage_key,
|
|
1649
|
+
folder_path: presignPayload.folder_path || "inpaint"
|
|
1650
|
+
})
|
|
1651
|
+
});
|
|
1652
|
+
const finalizePayload = await parseJsonResponse(finalizeResponse);
|
|
1653
|
+
if (!finalizeResponse.ok || !finalizePayload.file) {
|
|
1654
|
+
throw new Error(finalizePayload.error || "Unable to save file.");
|
|
1549
1655
|
}
|
|
1550
|
-
return
|
|
1656
|
+
return finalizePayload.file.viewUrl || finalizePayload.file.view_url || finalizePayload.file.s3Url || finalizePayload.file.s3_url;
|
|
1551
1657
|
}
|
|
1552
1658
|
|
|
1553
|
-
function
|
|
1554
|
-
|
|
1555
|
-
? globalThis.crypto.randomUUID().replace(/-/g, "").slice(-8)
|
|
1556
|
-
: Math.random().toString(16).slice(2, 10).padEnd(8, "0").slice(0, 8));
|
|
1557
|
-
return ${JSON.stringify(input.template.slugId)}
|
|
1558
|
-
.replace(/_/g, "-")
|
|
1559
|
-
.replace(/[^a-zA-Z0-9-]/g, "-")
|
|
1560
|
-
.toLowerCase()
|
|
1561
|
-
.replace(/-+/g, "-")
|
|
1562
|
-
.replace(/^-|-$/g, "") + "-" + suffix;
|
|
1659
|
+
function absoluteUrl(value) {
|
|
1660
|
+
return new URL(value, window.location.href).toString();
|
|
1563
1661
|
}
|
|
1564
1662
|
|
|
1565
|
-
function
|
|
1566
|
-
const
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
url.searchParams.delete("tracers");
|
|
1663
|
+
function firstSearchParam(names) {
|
|
1664
|
+
const params = new URLSearchParams(window.location.search);
|
|
1665
|
+
for (const name of names) {
|
|
1666
|
+
const value = params.get(name);
|
|
1667
|
+
if (value && value.trim()) return value.trim();
|
|
1571
1668
|
}
|
|
1572
|
-
|
|
1573
|
-
window.dispatchEvent(new CustomEvent("vidfarm:editor-tracers-changed", {
|
|
1574
|
-
detail: { tracers }
|
|
1575
|
-
}));
|
|
1669
|
+
return "";
|
|
1576
1670
|
}
|
|
1577
1671
|
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
const
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
}
|
|
1586
|
-
|
|
1587
|
-
get() {
|
|
1588
|
-
return parseTracersFromUrl();
|
|
1589
|
-
},
|
|
1590
|
-
set(tracers) {
|
|
1591
|
-
const normalized = [...new Set((Array.isArray(tracers) ? tracers : []).map((value) => String(value || "").trim()).filter(Boolean))];
|
|
1592
|
-
syncTracerUrl(normalized);
|
|
1593
|
-
notify();
|
|
1594
|
-
},
|
|
1595
|
-
add(tracer) {
|
|
1596
|
-
const nextTracer = String(tracer || "").trim() || makeTracerId();
|
|
1597
|
-
const current = this.get().filter((entry) => entry !== nextTracer);
|
|
1598
|
-
this.set([nextTracer, ...current]);
|
|
1599
|
-
return nextTracer;
|
|
1600
|
-
},
|
|
1601
|
-
remove(tracer) {
|
|
1602
|
-
const current = this.get().filter((entry) => entry !== tracer);
|
|
1603
|
-
this.set(current);
|
|
1604
|
-
},
|
|
1605
|
-
activate(tracer) {
|
|
1606
|
-
const current = this.get();
|
|
1607
|
-
if (!current.includes(tracer)) {
|
|
1608
|
-
this.add(tracer);
|
|
1609
|
-
return;
|
|
1610
|
-
}
|
|
1611
|
-
this.set([tracer, ...current.filter((entry) => entry !== tracer)]);
|
|
1612
|
-
},
|
|
1613
|
-
subscribe(listener) {
|
|
1614
|
-
listeners.push(listener);
|
|
1615
|
-
return () => {
|
|
1616
|
-
listeners = listeners.filter((entry) => entry !== listener);
|
|
1617
|
-
};
|
|
1618
|
-
}
|
|
1619
|
-
};
|
|
1620
|
-
})();
|
|
1621
|
-
window.__vidfarmEditorTracers = tracerStore;
|
|
1672
|
+
function sourceNameFromUrl(value) {
|
|
1673
|
+
try {
|
|
1674
|
+
const pathName = new URL(value, window.location.href).pathname;
|
|
1675
|
+
const fileName = decodeURIComponent(pathName.split("/").filter(Boolean).pop() || "");
|
|
1676
|
+
return fileName || "Source image";
|
|
1677
|
+
} catch {
|
|
1678
|
+
return "Source image";
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1622
1681
|
|
|
1623
|
-
function
|
|
1624
|
-
if (!
|
|
1682
|
+
function loadSourceUrl(value) {
|
|
1683
|
+
if (!value) return;
|
|
1684
|
+
let url = "";
|
|
1685
|
+
try {
|
|
1686
|
+
url = absoluteUrl(value);
|
|
1687
|
+
} catch {
|
|
1688
|
+
setStatus("The source image URL is invalid.", "error");
|
|
1625
1689
|
return;
|
|
1626
1690
|
}
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1691
|
+
sourceImageUrl = url;
|
|
1692
|
+
sourceImageName = firstSearchParam(["source_image_name", "image_name", "file_name"]) || sourceNameFromUrl(url);
|
|
1693
|
+
sourceName.textContent = sourceImageName;
|
|
1694
|
+
emptyState.hidden = true;
|
|
1695
|
+
canvasWrap.hidden = false;
|
|
1696
|
+
outputPanel.hidden = true;
|
|
1697
|
+
resultImage.removeAttribute("src");
|
|
1698
|
+
resultDownload.href = "#";
|
|
1699
|
+
resultDownload.removeAttribute("download");
|
|
1700
|
+
resetMask();
|
|
1701
|
+
inpaintImage.onload = () => {
|
|
1702
|
+
syncInpaintCanvasSize();
|
|
1703
|
+
resetMask();
|
|
1704
|
+
};
|
|
1705
|
+
inpaintImage.src = sourceImageUrl;
|
|
1706
|
+
setStatus("Source ready from URL.", "success");
|
|
1632
1707
|
}
|
|
1633
1708
|
|
|
1634
|
-
function
|
|
1635
|
-
|
|
1636
|
-
|
|
1709
|
+
function clearInpaintUrlParams() {
|
|
1710
|
+
const url = new URL(window.location.href);
|
|
1711
|
+
for (const key of ["tracer", "tracer_id", "trace", "source_image_url", "image_url", "input_image_url", "image", "source_image_name", "image_name", "file_name"]) {
|
|
1712
|
+
url.searchParams.delete(key);
|
|
1637
1713
|
}
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1714
|
+
window.history.replaceState({}, "", url.toString());
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
function initializeFromUrl() {
|
|
1718
|
+
tracerInput.value = firstSearchParam(["tracer", "tracer_id", "trace"]) || makeTracerId();
|
|
1719
|
+
loadSourceUrl(firstSearchParam(["source_image_url", "image_url", "input_image_url", "image"]));
|
|
1641
1720
|
}
|
|
1642
1721
|
|
|
1643
|
-
function
|
|
1644
|
-
if (!
|
|
1722
|
+
async function addAttachmentFilesForScope(files, scope) {
|
|
1723
|
+
if (!scope) return;
|
|
1724
|
+
const selected = Array.from(files || []);
|
|
1725
|
+
if (!selected.length) return;
|
|
1726
|
+
if (inpaintAttachments.length + selected.length > maxInpaintAttachments) {
|
|
1727
|
+
setStatus("Use up to " + maxInpaintAttachments + " reference images.", "error");
|
|
1645
1728
|
return;
|
|
1646
1729
|
}
|
|
1647
|
-
const
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1730
|
+
const previousDisabled = inpaintSubmit.disabled;
|
|
1731
|
+
inpaintSubmit.disabled = true;
|
|
1732
|
+
setStatus("Uploading reference image...", "");
|
|
1733
|
+
try {
|
|
1734
|
+
for (const file of selected) {
|
|
1735
|
+
if (!isAllowedInpaintAttachment(file)) {
|
|
1736
|
+
throw new Error("Reference attachments must be JPEG, PNG, or WebP images.");
|
|
1737
|
+
}
|
|
1738
|
+
const fileName = file.name || "reference.png";
|
|
1739
|
+
const uploadedUrl = await uploadTemporaryBlob(file, fileName, file.type || "image/png");
|
|
1740
|
+
inpaintAttachments.push({
|
|
1741
|
+
id: attachmentId(),
|
|
1742
|
+
scope: scope.scope === "color" ? "color" : "global",
|
|
1743
|
+
color: scope.scope === "color" ? normalizeHexColor(scope.color) : "",
|
|
1744
|
+
fileName,
|
|
1745
|
+
url: absoluteUrl(uploadedUrl)
|
|
1746
|
+
});
|
|
1747
|
+
}
|
|
1748
|
+
renderAllAttachmentChips();
|
|
1749
|
+
setStatus("Reference image ready.", "success");
|
|
1750
|
+
} catch (error) {
|
|
1751
|
+
console.error(error);
|
|
1752
|
+
setStatus(error instanceof Error ? error.message : "Unable to upload reference image.", "error");
|
|
1753
|
+
} finally {
|
|
1754
|
+
inpaintSubmit.disabled = previousDisabled;
|
|
1652
1755
|
}
|
|
1653
|
-
tracerStore.add(tracer);
|
|
1654
|
-
closeTracerModal();
|
|
1655
1756
|
}
|
|
1656
1757
|
|
|
1657
|
-
function
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1758
|
+
async function addAttachmentFiles(files) {
|
|
1759
|
+
const scope = pendingInpaintAttachmentScope;
|
|
1760
|
+
pendingInpaintAttachmentScope = null;
|
|
1761
|
+
await addAttachmentFilesForScope(files, scope);
|
|
1661
1762
|
}
|
|
1662
1763
|
|
|
1663
|
-
function
|
|
1664
|
-
|
|
1764
|
+
function resetMask() {
|
|
1765
|
+
inpaintStrokes = [];
|
|
1766
|
+
redoStrokes = [];
|
|
1767
|
+
currentInpaintStroke = null;
|
|
1768
|
+
isInpaintPainting = false;
|
|
1769
|
+
redrawInpaintMask();
|
|
1665
1770
|
}
|
|
1666
1771
|
|
|
1667
|
-
function
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1772
|
+
function resetWorkspace() {
|
|
1773
|
+
sourceImageUrl = "";
|
|
1774
|
+
sourceImageName = "";
|
|
1775
|
+
sourceName.textContent = "No image selected";
|
|
1776
|
+
emptyState.hidden = false;
|
|
1777
|
+
canvasWrap.hidden = true;
|
|
1778
|
+
outputPanel.hidden = true;
|
|
1779
|
+
resultImage.removeAttribute("src");
|
|
1780
|
+
resultDownload.href = "#";
|
|
1781
|
+
resultDownload.removeAttribute("download");
|
|
1782
|
+
inpaintImage.removeAttribute("src");
|
|
1783
|
+
inpaintCanvas.width = 1;
|
|
1784
|
+
inpaintCanvas.height = 1;
|
|
1785
|
+
inpaintGlobalPrompt.value = "";
|
|
1786
|
+
inpaintMaskColors = [{ color: "#ff2d55", prompt: "" }];
|
|
1787
|
+
inpaintAttachments = [];
|
|
1788
|
+
pendingInpaintAttachmentScope = null;
|
|
1789
|
+
activeInpaintColor = "#ff2d55";
|
|
1790
|
+
inpaintColorPicker.value = activeInpaintColor;
|
|
1791
|
+
resetMask();
|
|
1792
|
+
renderInpaintControls();
|
|
1793
|
+
setStatus("", "");
|
|
1674
1794
|
}
|
|
1675
1795
|
|
|
1676
|
-
function
|
|
1677
|
-
if (
|
|
1678
|
-
|
|
1796
|
+
async function loadSourceFile(file) {
|
|
1797
|
+
if (!file || !file.type.startsWith("image/")) {
|
|
1798
|
+
setStatus("Choose an image file.", "error");
|
|
1799
|
+
return;
|
|
1679
1800
|
}
|
|
1680
|
-
|
|
1681
|
-
|
|
1801
|
+
uploadButton.disabled = true;
|
|
1802
|
+
inpaintSubmit.disabled = true;
|
|
1803
|
+
setStatus("Uploading source image...", "");
|
|
1804
|
+
try {
|
|
1805
|
+
const fileName = file.name || "source.png";
|
|
1806
|
+
const uploadedUrl = await uploadTemporaryBlob(file, fileName, file.type || "image/png");
|
|
1807
|
+
sourceImageUrl = absoluteUrl(uploadedUrl);
|
|
1808
|
+
sourceImageName = fileName;
|
|
1809
|
+
sourceName.textContent = fileName;
|
|
1810
|
+
emptyState.hidden = true;
|
|
1811
|
+
canvasWrap.hidden = false;
|
|
1812
|
+
outputPanel.hidden = true;
|
|
1813
|
+
resultImage.removeAttribute("src");
|
|
1814
|
+
resultDownload.href = "#";
|
|
1815
|
+
resultDownload.removeAttribute("download");
|
|
1816
|
+
resetMask();
|
|
1817
|
+
inpaintImage.onload = () => {
|
|
1818
|
+
syncInpaintCanvasSize();
|
|
1819
|
+
resetMask();
|
|
1820
|
+
};
|
|
1821
|
+
inpaintImage.src = sourceImageUrl;
|
|
1822
|
+
setStatus("Source ready.", "success");
|
|
1823
|
+
} catch (error) {
|
|
1824
|
+
console.error(error);
|
|
1825
|
+
setStatus(error instanceof Error ? error.message : "Unable to upload source image.", "error");
|
|
1826
|
+
} finally {
|
|
1827
|
+
uploadButton.disabled = false;
|
|
1828
|
+
inpaintSubmit.disabled = false;
|
|
1682
1829
|
}
|
|
1683
|
-
return "file";
|
|
1684
1830
|
}
|
|
1685
1831
|
|
|
1686
|
-
function
|
|
1687
|
-
|
|
1832
|
+
function extractOutputUrl(payload) {
|
|
1833
|
+
const result = payload && payload.result ? payload.result : {};
|
|
1834
|
+
return result.output?.image?.file_url
|
|
1835
|
+
|| result.output?.primary_file_url
|
|
1836
|
+
|| result.image?.file_url
|
|
1837
|
+
|| result.primary_file_url
|
|
1838
|
+
|| (Array.isArray(result.output?.files) ? result.output.files[0] : "")
|
|
1839
|
+
|| (Array.isArray(result.files) ? result.files[0] : "");
|
|
1688
1840
|
}
|
|
1689
1841
|
|
|
1690
|
-
function
|
|
1691
|
-
|
|
1692
|
-
window.open(url, "_blank", "noopener,noreferrer");
|
|
1693
|
-
return;
|
|
1694
|
-
}
|
|
1695
|
-
mediaModalTitle.textContent = label;
|
|
1696
|
-
mediaModalSource.href = url;
|
|
1697
|
-
if (kind === "image") {
|
|
1698
|
-
mediaModalBody.innerHTML = '<img src="' + escapeHtml(url) + '" alt="' + escapeHtml(label) + '" />';
|
|
1699
|
-
} else if (kind === "video") {
|
|
1700
|
-
mediaModalBody.innerHTML = '<video src="' + escapeHtml(url) + '" controls autoplay playsinline preload="metadata"></video>';
|
|
1701
|
-
} else {
|
|
1702
|
-
mediaModalBody.innerHTML = '<a class="editor-history-button is-primary" href="' + escapeHtml(url) + '" target="_blank" rel="noreferrer">Open file</a>';
|
|
1703
|
-
}
|
|
1704
|
-
mediaModalBackdrop.hidden = false;
|
|
1705
|
-
mediaModalBackdrop.setAttribute("data-open", "true");
|
|
1842
|
+
function isPendingJobStatus(status) {
|
|
1843
|
+
return ["queued", "running", "waiting_for_provider", "waiting_for_child", "waiting_for_human"].includes(String(status || ""));
|
|
1706
1844
|
}
|
|
1707
1845
|
|
|
1708
|
-
function
|
|
1709
|
-
|
|
1710
|
-
return;
|
|
1711
|
-
}
|
|
1712
|
-
mediaModalBackdrop.setAttribute("data-open", "false");
|
|
1713
|
-
mediaModalBackdrop.hidden = true;
|
|
1714
|
-
mediaModalSource.href = "#";
|
|
1715
|
-
mediaModalBody.innerHTML = "";
|
|
1846
|
+
function pendingHistoryItemKey(jobId) {
|
|
1847
|
+
return "job:" + String(jobId || "");
|
|
1716
1848
|
}
|
|
1717
1849
|
|
|
1718
|
-
function
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
'</button>';
|
|
1731
|
-
}
|
|
1732
|
-
if (kind === "video") {
|
|
1733
|
-
return '<button type="button" class="editor-history-card-preview" data-preview-url="' + escapeHtml(url) + '" data-preview-kind="' + kind + '" data-preview-label="' + escapeHtml(label) + '">' +
|
|
1734
|
-
'<video src="' + escapeHtml(url) + '" muted playsinline preload="metadata" aria-hidden="true"></video>' +
|
|
1735
|
-
'<span class="editor-history-video-play" aria-hidden="true"></span>' +
|
|
1736
|
-
'<span class="editor-history-card-preview-label">' + escapeHtml(label) + '</span>' +
|
|
1737
|
-
'</button>';
|
|
1738
|
-
}
|
|
1739
|
-
return '<button type="button" class="editor-history-card-preview" data-preview-url="' + escapeHtml(url) + '" data-preview-kind="' + kind + '" data-preview-label="' + escapeHtml(label) + '">' +
|
|
1740
|
-
'<span class="editor-history-card-preview-fallback">' + escapeHtml(label) + '</span>' +
|
|
1741
|
-
'</button>';
|
|
1742
|
-
})()
|
|
1743
|
-
)).join("") + '</div>';
|
|
1850
|
+
function makePendingInpaintHistoryItem(input) {
|
|
1851
|
+
const jobId = String(input?.jobId || input?.job_id || "");
|
|
1852
|
+
if (!jobId) return null;
|
|
1853
|
+
return {
|
|
1854
|
+
key: pendingHistoryItemKey(jobId),
|
|
1855
|
+
kind: "pending",
|
|
1856
|
+
jobId,
|
|
1857
|
+
status: String(input?.status || "queued"),
|
|
1858
|
+
progress: Number(input?.progress || 0),
|
|
1859
|
+
createdAt: String(input?.createdAt || input?.created_at || new Date().toISOString()),
|
|
1860
|
+
updatedAt: String(input?.updatedAt || input?.updated_at || new Date().toISOString())
|
|
1861
|
+
};
|
|
1744
1862
|
}
|
|
1745
1863
|
|
|
1746
|
-
function
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
if (!url || seen.has(url) || !isPreviewableMediaUrl(url)) {
|
|
1756
|
-
continue;
|
|
1757
|
-
}
|
|
1758
|
-
seen.add(url);
|
|
1759
|
-
urls.push(url);
|
|
1760
|
-
}
|
|
1761
|
-
}
|
|
1762
|
-
return urls;
|
|
1864
|
+
function upsertInpaintHistoryItem(item) {
|
|
1865
|
+
if (!item || !item.key) return;
|
|
1866
|
+
inpaintHistoryItems = inpaintHistoryItems.filter((entry) => (
|
|
1867
|
+
entry.key !== item.key
|
|
1868
|
+
&& (!item.jobId || entry.jobId !== item.jobId)
|
|
1869
|
+
));
|
|
1870
|
+
inpaintHistoryItems.unshift(item);
|
|
1871
|
+
inpaintHistoryItems.sort((a, b) => Date.parse(b.createdAt || 0) - Date.parse(a.createdAt || 0));
|
|
1872
|
+
renderInpaintHistory();
|
|
1763
1873
|
}
|
|
1764
1874
|
|
|
1765
|
-
function
|
|
1766
|
-
const
|
|
1767
|
-
if (!
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1875
|
+
function applyCompletedInpaintJob(payload) {
|
|
1876
|
+
const outputUrl = extractOutputUrl(payload);
|
|
1877
|
+
if (!outputUrl) return "";
|
|
1878
|
+
const url = absoluteUrl(outputUrl);
|
|
1879
|
+
const jobId = String(payload?.job_id || payload?.jobId || "");
|
|
1880
|
+
resultImage.src = url;
|
|
1881
|
+
resultDownload.href = url;
|
|
1882
|
+
resultDownload.setAttribute("download", "inpaint-" + Date.now() + ".png");
|
|
1883
|
+
outputPanel.hidden = false;
|
|
1884
|
+
upsertInpaintHistoryItem({
|
|
1885
|
+
key: historyItemKey({ jobId: jobId || "inpaint" }, url, 0),
|
|
1886
|
+
kind: "output",
|
|
1887
|
+
jobId,
|
|
1888
|
+
url,
|
|
1889
|
+
createdAt: String(payload?.completed_at || payload?.updated_at || new Date().toISOString())
|
|
1890
|
+
});
|
|
1891
|
+
return url;
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
function historyEntryUrls(entry) {
|
|
1895
|
+
const urls = Array.isArray(entry?.outputMediaUrls) ? entry.outputMediaUrls : [];
|
|
1896
|
+
return urls.filter((url) => {
|
|
1897
|
+
const value = String(url || "");
|
|
1898
|
+
if (!/\\.(png|jpe?g|webp|gif|avif)(\\?|#|$)/i.test(value)) {
|
|
1899
|
+
return false;
|
|
1780
1900
|
}
|
|
1781
|
-
|
|
1782
|
-
const
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1901
|
+
try {
|
|
1902
|
+
const parsed = new URL(value, window.location.href);
|
|
1903
|
+
const storageKey = parsed.searchParams.get("key") || decodeURIComponent(parsed.pathname).replace(/^\\/+/, "");
|
|
1904
|
+
return !/\\/temporary\\/inpaint(?:-|\\/|$)/i.test(storageKey)
|
|
1905
|
+
&& !/\\/temporary\\/inpaint-masks(?:\\/|$)/i.test(storageKey)
|
|
1906
|
+
&& !/inpaint-mask-[^/]+\\.png/i.test(storageKey);
|
|
1907
|
+
} catch {
|
|
1908
|
+
return !/\\/temporary\\/inpaint(?:-|\\/|$)/i.test(value)
|
|
1909
|
+
&& !/\\/temporary\\/inpaint-masks(?:\\/|$)/i.test(value)
|
|
1910
|
+
&& !/inpaint-mask-[^/]+\\.png/i.test(value);
|
|
1786
1911
|
}
|
|
1787
|
-
|
|
1788
|
-
}).join("");
|
|
1789
|
-
const more = !isExpanded && urls.length > visibleUrls.length
|
|
1790
|
-
? '<button type="button" class="editor-history-group-preview-more" data-editor-expand-media="' + escapeHtml(groupKey) + '" aria-label="Show all media previews">+' + escapeHtml(urls.length - visibleUrls.length) + '</button>'
|
|
1791
|
-
: "";
|
|
1792
|
-
return '<div class="editor-history-group-previews' + (isExpanded ? ' is-expanded' : '') + '" aria-label="Generated media previews">' + visible + more + '</div>';
|
|
1912
|
+
});
|
|
1793
1913
|
}
|
|
1794
1914
|
|
|
1795
|
-
function
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1915
|
+
function isInpaintHistoryEntry(entry) {
|
|
1916
|
+
const templateId = String(entry?.linkedJob?.template_id || entry?.linkedJob?.templateId || "").toLowerCase();
|
|
1917
|
+
const path = String(entry?.path || "").toLowerCase();
|
|
1918
|
+
return templateId === "primitive:image_inpaint"
|
|
1919
|
+
|| path.includes("/primitives/images/inpaint")
|
|
1920
|
+
|| path.includes("/primitives/image_inpaint");
|
|
1801
1921
|
}
|
|
1802
1922
|
|
|
1803
|
-
function
|
|
1804
|
-
return
|
|
1805
|
-
renderHttpAction(entry) +
|
|
1806
|
-
'<div class="editor-history-card-foot">' +
|
|
1807
|
-
'<span>' + escapeHtml(new Date(entry.createdAt).toLocaleString()) + '</span>' +
|
|
1808
|
-
'</div>' +
|
|
1809
|
-
renderPreviewLinks(entry.outputMediaUrls && entry.outputMediaUrls.length ? entry.outputMediaUrls : entry.mediaUrls) +
|
|
1810
|
-
'</article>';
|
|
1923
|
+
function historyItemKey(entry, url, index) {
|
|
1924
|
+
return String(entry?.jobId || entry?.job_id || entry?.id || "inpaint") + ":" + index + ":" + String(url || "");
|
|
1811
1925
|
}
|
|
1812
1926
|
|
|
1813
|
-
function
|
|
1814
|
-
|
|
1927
|
+
function setHistoryEmpty(message) {
|
|
1928
|
+
historyList.innerHTML = "";
|
|
1929
|
+
const empty = document.createElement("div");
|
|
1930
|
+
empty.className = "custom-inpaint-history-empty";
|
|
1931
|
+
empty.textContent = message;
|
|
1932
|
+
historyList.append(empty);
|
|
1815
1933
|
}
|
|
1816
1934
|
|
|
1817
|
-
function
|
|
1818
|
-
|
|
1819
|
-
const
|
|
1820
|
-
if (!
|
|
1821
|
-
|
|
1935
|
+
function renderInpaintHistory() {
|
|
1936
|
+
historyList.innerHTML = "";
|
|
1937
|
+
const visible = inpaintHistoryItems.filter((item) => !hiddenInpaintHistoryKeys.has(item.key));
|
|
1938
|
+
if (!visible.length) {
|
|
1939
|
+
setHistoryEmpty("No inpaint outputs yet.");
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
|
+
for (const item of visible) {
|
|
1943
|
+
const row = document.createElement("div");
|
|
1944
|
+
row.className = "custom-inpaint-history-item";
|
|
1945
|
+
row.dataset.historyKey = item.key;
|
|
1946
|
+
let thumbButton;
|
|
1947
|
+
if (item.kind === "pending") {
|
|
1948
|
+
thumbButton = document.createElement("div");
|
|
1949
|
+
thumbButton.className = "custom-inpaint-history-generating";
|
|
1950
|
+
thumbButton.setAttribute("aria-label", "Image edit generating");
|
|
1951
|
+
thumbButton.setAttribute("role", "img");
|
|
1952
|
+
} else {
|
|
1953
|
+
thumbButton = document.createElement("button");
|
|
1954
|
+
thumbButton.type = "button";
|
|
1955
|
+
thumbButton.className = "custom-inpaint-history-thumb-button";
|
|
1956
|
+
thumbButton.dataset.historyPreview = item.key;
|
|
1957
|
+
thumbButton.setAttribute("aria-label", "View inpaint output");
|
|
1958
|
+
const thumb = document.createElement("img");
|
|
1959
|
+
thumb.className = "custom-inpaint-history-thumb";
|
|
1960
|
+
thumb.src = item.url;
|
|
1961
|
+
thumb.alt = "Image edit output";
|
|
1962
|
+
thumb.loading = "lazy";
|
|
1963
|
+
thumbButton.append(thumb);
|
|
1964
|
+
}
|
|
1965
|
+
const meta = document.createElement("div");
|
|
1966
|
+
meta.className = "custom-inpaint-history-meta";
|
|
1967
|
+
const title = document.createElement("div");
|
|
1968
|
+
title.className = "custom-inpaint-history-title";
|
|
1969
|
+
title.textContent = item.kind === "pending"
|
|
1970
|
+
? "Generating inpaint"
|
|
1971
|
+
: (item.createdAt ? new Date(item.createdAt).toLocaleString() : "Image edit output");
|
|
1972
|
+
if (item.kind === "pending") {
|
|
1973
|
+
const subtitle = document.createElement("div");
|
|
1974
|
+
subtitle.className = "custom-inpaint-history-subtitle";
|
|
1975
|
+
const progress = Number(item.progress || 0);
|
|
1976
|
+
subtitle.textContent = "Job " + (item.status || "queued") + (progress > 0 ? " · " + Math.round(progress * 100) + "%" : "");
|
|
1977
|
+
meta.append(title, subtitle);
|
|
1978
|
+
} else {
|
|
1979
|
+
meta.append(title);
|
|
1980
|
+
}
|
|
1981
|
+
const actions = document.createElement("div");
|
|
1982
|
+
actions.className = "custom-inpaint-history-actions";
|
|
1983
|
+
if (item.kind === "pending") {
|
|
1984
|
+
const check = document.createElement("button");
|
|
1985
|
+
check.type = "button";
|
|
1986
|
+
check.className = "custom-inpaint-history-check";
|
|
1987
|
+
check.dataset.historyCheck = item.jobId || "";
|
|
1988
|
+
check.textContent = "Check Status";
|
|
1989
|
+
actions.append(check);
|
|
1990
|
+
} else {
|
|
1991
|
+
const download = document.createElement("a");
|
|
1992
|
+
download.className = "custom-inpaint-history-icon";
|
|
1993
|
+
download.href = item.url;
|
|
1994
|
+
download.target = "_blank";
|
|
1995
|
+
download.rel = "noopener noreferrer";
|
|
1996
|
+
download.download = imageDownloadName("inpaint-history", item.createdAt);
|
|
1997
|
+
download.setAttribute("aria-label", "Download inpaint output");
|
|
1998
|
+
download.setAttribute("title", "Download");
|
|
1999
|
+
download.innerHTML = '<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 3v9"></path><path d="m6.5 8.5 3.5 3.5 3.5-3.5"></path><path d="M4 16h12"></path></svg>';
|
|
2000
|
+
const copy = document.createElement("button");
|
|
2001
|
+
copy.type = "button";
|
|
2002
|
+
copy.className = "custom-inpaint-history-icon";
|
|
2003
|
+
copy.dataset.historyCopy = item.key;
|
|
2004
|
+
copy.setAttribute("aria-label", "Copy inpaint output URL");
|
|
2005
|
+
copy.setAttribute("title", "Copy URL");
|
|
2006
|
+
copy.innerHTML = '<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8.4 11.6a3.2 3.2 0 0 0 4.5 0l2.2-2.2a3.2 3.2 0 0 0-4.5-4.5L9.4 6.1"></path><path d="M11.6 8.4a3.2 3.2 0 0 0-4.5 0L4.9 10.6a3.2 3.2 0 0 0 4.5 4.5l1.2-1.2"></path></svg>';
|
|
2007
|
+
actions.append(download, copy);
|
|
2008
|
+
}
|
|
2009
|
+
const remove = document.createElement("button");
|
|
2010
|
+
remove.type = "button";
|
|
2011
|
+
remove.className = "custom-inpaint-history-icon";
|
|
2012
|
+
remove.dataset.historyRemove = item.key;
|
|
2013
|
+
remove.setAttribute("aria-label", "Remove inpaint output from history strip");
|
|
2014
|
+
remove.setAttribute("title", "Remove");
|
|
2015
|
+
remove.innerHTML = '<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 5l10 10"></path><path d="M15 5 5 15"></path></svg>';
|
|
2016
|
+
actions.append(remove);
|
|
2017
|
+
meta.append(actions);
|
|
2018
|
+
const confirm = document.createElement("div");
|
|
2019
|
+
confirm.className = "custom-inpaint-history-popconfirm";
|
|
2020
|
+
confirm.hidden = true;
|
|
2021
|
+
confirm.dataset.historyConfirm = item.key;
|
|
2022
|
+
const confirmText = document.createElement("p");
|
|
2023
|
+
confirmText.textContent = "Remove from this history strip?";
|
|
2024
|
+
const confirmActions = document.createElement("div");
|
|
2025
|
+
confirmActions.className = "custom-inpaint-history-popconfirm-actions";
|
|
2026
|
+
const cancel = document.createElement("button");
|
|
2027
|
+
cancel.type = "button";
|
|
2028
|
+
cancel.dataset.historyCancel = item.key;
|
|
2029
|
+
cancel.textContent = "Cancel";
|
|
2030
|
+
const confirmRemove = document.createElement("button");
|
|
2031
|
+
confirmRemove.type = "button";
|
|
2032
|
+
confirmRemove.dataset.historyConfirmRemove = item.key;
|
|
2033
|
+
confirmRemove.dataset.danger = "true";
|
|
2034
|
+
confirmRemove.textContent = "Remove";
|
|
2035
|
+
confirmActions.append(cancel, confirmRemove);
|
|
2036
|
+
confirm.append(confirmText, confirmActions);
|
|
2037
|
+
row.append(thumbButton, meta, confirm);
|
|
2038
|
+
historyList.append(row);
|
|
1822
2039
|
}
|
|
1823
|
-
|
|
1824
|
-
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
function closeInpaintHistoryConfirms(exceptKey) {
|
|
2043
|
+
historyList.querySelectorAll("[data-history-confirm]").forEach((entry) => {
|
|
2044
|
+
if (!(entry instanceof HTMLElement)) return;
|
|
2045
|
+
entry.hidden = exceptKey ? entry.dataset.historyConfirm !== exceptKey : true;
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
function extractInpaintHistoryItems(entries) {
|
|
2050
|
+
const items = [];
|
|
2051
|
+
const seen = new Set();
|
|
2052
|
+
for (const entry of Array.isArray(entries) ? entries : []) {
|
|
2053
|
+
if (!isInpaintHistoryEntry(entry)) continue;
|
|
2054
|
+
const linkedJob = entry?.linkedJob || null;
|
|
2055
|
+
if (linkedJob && isPendingJobStatus(linkedJob.status)) {
|
|
2056
|
+
const pending = makePendingInpaintHistoryItem({
|
|
2057
|
+
job_id: linkedJob.job_id,
|
|
2058
|
+
status: linkedJob.status,
|
|
2059
|
+
progress: linkedJob.progress,
|
|
2060
|
+
created_at: linkedJob.created_at || entry.createdAt,
|
|
2061
|
+
updated_at: linkedJob.updated_at || entry.createdAt
|
|
2062
|
+
});
|
|
2063
|
+
if (pending && !seen.has(pending.key)) {
|
|
2064
|
+
seen.add(pending.key);
|
|
2065
|
+
items.push(pending);
|
|
2066
|
+
}
|
|
2067
|
+
continue;
|
|
2068
|
+
}
|
|
2069
|
+
historyEntryUrls(entry).forEach((rawUrl, index) => {
|
|
2070
|
+
const url = absoluteUrl(rawUrl);
|
|
2071
|
+
const key = historyItemKey(entry, url, index);
|
|
2072
|
+
if (seen.has(key)) return;
|
|
2073
|
+
seen.add(key);
|
|
2074
|
+
items.push({
|
|
2075
|
+
key,
|
|
2076
|
+
kind: "output",
|
|
2077
|
+
jobId: String(entry?.jobId || entry?.job_id || entry?.linkedJob?.job_id || ""),
|
|
2078
|
+
url,
|
|
2079
|
+
createdAt: String(entry.createdAt || new Date().toISOString())
|
|
2080
|
+
});
|
|
2081
|
+
});
|
|
1825
2082
|
}
|
|
1826
|
-
return
|
|
2083
|
+
return items;
|
|
1827
2084
|
}
|
|
1828
2085
|
|
|
1829
2086
|
async function copyText(value) {
|
|
@@ -1831,9 +2088,7 @@ export function renderTemplateEditorDocsPage(input) {
|
|
|
1831
2088
|
try {
|
|
1832
2089
|
await navigator.clipboard.writeText(value);
|
|
1833
2090
|
return true;
|
|
1834
|
-
} catch {
|
|
1835
|
-
// Fall back to the textarea path when browser permissions block clipboard.writeText.
|
|
1836
|
-
}
|
|
2091
|
+
} catch {}
|
|
1837
2092
|
}
|
|
1838
2093
|
const field = document.createElement("textarea");
|
|
1839
2094
|
field.value = value;
|
|
@@ -1851,459 +2106,386 @@ export function renderTemplateEditorDocsPage(input) {
|
|
|
1851
2106
|
return copied;
|
|
1852
2107
|
}
|
|
1853
2108
|
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
button.setAttribute("title", "Copy " + label);
|
|
1873
|
-
copyFeedbackTimers.delete(button);
|
|
1874
|
-
}
|
|
1875
|
-
|
|
1876
|
-
function showCopyingFeedback(button, label) {
|
|
1877
|
-
clearCopyFeedback(button);
|
|
1878
|
-
button.setAttribute("data-copying", "true");
|
|
1879
|
-
button.setAttribute("aria-label", "Copying " + label);
|
|
1880
|
-
button.setAttribute("title", "Copying");
|
|
1881
|
-
}
|
|
1882
|
-
|
|
1883
|
-
function showCopyFeedback(button, label) {
|
|
1884
|
-
clearCopyFeedback(button);
|
|
1885
|
-
button.setAttribute("data-copied", "true");
|
|
1886
|
-
button.setAttribute("aria-label", "Copied " + label);
|
|
1887
|
-
button.setAttribute("title", "Copied");
|
|
1888
|
-
const timer = window.setTimeout(() => {
|
|
1889
|
-
resetCopyFeedback(button, label);
|
|
1890
|
-
}, 1400);
|
|
1891
|
-
copyFeedbackTimers.set(button, timer);
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
|
-
function showCopyErrorFeedback(button, label) {
|
|
1895
|
-
clearCopyFeedback(button);
|
|
1896
|
-
button.setAttribute("data-copy-error", "true");
|
|
1897
|
-
button.setAttribute("aria-label", "Copy failed for " + label);
|
|
1898
|
-
button.setAttribute("title", "Copy failed");
|
|
1899
|
-
const timer = window.setTimeout(() => {
|
|
1900
|
-
resetCopyFeedback(button, label);
|
|
1901
|
-
}, 1800);
|
|
1902
|
-
copyFeedbackTimers.set(button, timer);
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
|
-
function renderGroups(entries, tracers) {
|
|
1906
|
-
if (!tracers.length) {
|
|
1907
|
-
renderEmpty("Use Copilot to start or select a tracer, then its grouped POST history will appear here.");
|
|
1908
|
-
return;
|
|
2109
|
+
async function loadInpaintHistory() {
|
|
2110
|
+
const tracer = getTracerValue();
|
|
2111
|
+
historyRefresh.disabled = true;
|
|
2112
|
+
try {
|
|
2113
|
+
const endpoint = new URL("/job-runs/history", window.location.origin);
|
|
2114
|
+
endpoint.searchParams.set("limit", "120");
|
|
2115
|
+
endpoint.searchParams.set("template_id", "primitive:image_inpaint");
|
|
2116
|
+
endpoint.searchParams.set("tracer", tracer);
|
|
2117
|
+
const response = await fetch(endpoint, { credentials: "same-origin" });
|
|
2118
|
+
const payload = await parseJsonResponse(response);
|
|
2119
|
+
if (!response.ok || (payload && payload.error)) throw new Error(payload.error || "Unable to load inpaint history.");
|
|
2120
|
+
inpaintHistoryItems = extractInpaintHistoryItems(payload.entries);
|
|
2121
|
+
renderInpaintHistory();
|
|
2122
|
+
} catch (error) {
|
|
2123
|
+
console.error(error);
|
|
2124
|
+
setHistoryEmpty(error instanceof Error ? error.message : "Unable to load inpaint history.");
|
|
2125
|
+
} finally {
|
|
2126
|
+
historyRefresh.disabled = false;
|
|
1909
2127
|
}
|
|
2128
|
+
}
|
|
1910
2129
|
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
grouped.set(key, existing);
|
|
2130
|
+
async function checkInpaintJobStatus(jobId, button) {
|
|
2131
|
+
if (!jobId) return null;
|
|
2132
|
+
if (button instanceof HTMLButtonElement) {
|
|
2133
|
+
button.disabled = true;
|
|
2134
|
+
button.textContent = "Checking";
|
|
1917
2135
|
}
|
|
1918
|
-
|
|
1919
|
-
historyRoot.innerHTML = tracers.map((tracer, index) => {
|
|
1920
|
-
const groupEntries = grouped.get(tracer) || [];
|
|
1921
|
-
const activeRefresh = index === 0
|
|
1922
|
-
? '<button type="button" class="editor-history-button editor-history-refresh" data-editor-history-refresh="' + escapeHtml(tracer) + '" aria-label="Refresh tracer history" title="Refresh tracer history">' + renderRefreshIcon() + '</button>'
|
|
1923
|
-
: '';
|
|
1924
|
-
const menu = '<span class="editor-history-menu-wrap" data-editor-history-menu-wrap>' +
|
|
1925
|
-
'<button type="button" class="editor-history-button editor-history-menu-button" data-editor-history-menu-toggle="' + escapeHtml(tracer) + '" aria-label="Tracer actions" title="Tracer actions">...</button>' +
|
|
1926
|
-
'<span class="editor-history-menu" role="menu">' +
|
|
1927
|
-
'<button type="button" data-editor-history-copy="' + escapeHtml(tracer) + '">Copy Tracer</button>' +
|
|
1928
|
-
'<button type="button" data-editor-history-rename="' + escapeHtml(tracer) + '">Rename tracer</button>' +
|
|
1929
|
-
'<button type="button" data-editor-history-remove="' + escapeHtml(tracer) + '">Remove from view</button>' +
|
|
1930
|
-
'<button type="button" data-editor-history-archive="' + escapeHtml(tracer) + '" data-danger="true">Archive tracer</button>' +
|
|
1931
|
-
'</span>' +
|
|
1932
|
-
'</span>';
|
|
1933
|
-
const callsLabel = groupEntries.length
|
|
1934
|
-
? escapeHtml(groupEntries.length) + ' POST call' + (groupEntries.length === 1 ? '' : 's')
|
|
1935
|
-
: 'No POST calls yet';
|
|
1936
|
-
return (
|
|
1937
|
-
'<details class="editor-history-group"' + (index === 0 ? ' open' : '') + '>' +
|
|
1938
|
-
'<summary>' +
|
|
1939
|
-
'<div class="editor-history-group-main">' +
|
|
1940
|
-
'<h3>' + escapeHtml(tracer) + '</h3>' +
|
|
1941
|
-
'<p>' + callsLabel + '</p>' +
|
|
1942
|
-
renderGroupPreviewStrip(groupEntries, tracer) +
|
|
1943
|
-
'</div>' +
|
|
1944
|
-
'<div class="editor-history-group-actions">' +
|
|
1945
|
-
activeRefresh +
|
|
1946
|
-
menu +
|
|
1947
|
-
'<span class="editor-history-group-toggle">Toggle</span>' +
|
|
1948
|
-
'</div>' +
|
|
1949
|
-
'</summary>' +
|
|
1950
|
-
'<div class="editor-history-group-body">' +
|
|
1951
|
-
(groupEntries.length
|
|
1952
|
-
? groupEntries.map(renderEntry).join("")
|
|
1953
|
-
: '<div class="editor-history-empty">No POST history exists for this tracer yet.</div>') +
|
|
1954
|
-
'</div>' +
|
|
1955
|
-
'</details>'
|
|
1956
|
-
)}).join("");
|
|
1957
|
-
}
|
|
1958
|
-
|
|
1959
|
-
async function fetchHistoryPayload(endpoint) {
|
|
1960
|
-
const response = await fetch(endpoint.toString(), { credentials: "same-origin" });
|
|
1961
|
-
let payload = null;
|
|
1962
2136
|
try {
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
2137
|
+
const response = await fetch("/api/v1/primitives/image_inpaint/jobs/" + encodeURIComponent(jobId), {
|
|
2138
|
+
credentials: "same-origin"
|
|
2139
|
+
});
|
|
2140
|
+
const payload = await parseJsonResponse(response);
|
|
2141
|
+
if (!response.ok) throw new Error(payload.error || "Unable to check inpaint job.");
|
|
2142
|
+
if (payload.status === "failed" || payload.status === "cancelled") {
|
|
2143
|
+
const pending = makePendingInpaintHistoryItem(payload);
|
|
2144
|
+
upsertInpaintHistoryItem({
|
|
2145
|
+
...(pending || { key: pendingHistoryItemKey(jobId), kind: "pending", jobId, createdAt: new Date().toISOString() }),
|
|
2146
|
+
status: payload.status,
|
|
2147
|
+
progress: Number(payload.progress || 0)
|
|
2148
|
+
});
|
|
2149
|
+
throw new Error(payload.error?.message || "Image edit job " + payload.status + ".");
|
|
2150
|
+
}
|
|
2151
|
+
const outputUrl = extractOutputUrl(payload);
|
|
2152
|
+
if (payload.status === "succeeded" || payload.status === "complete" || outputUrl) {
|
|
2153
|
+
const url = applyCompletedInpaintJob(payload);
|
|
2154
|
+
if (!url) throw new Error("Image edit completed without an output URL.");
|
|
2155
|
+
setStatus("Image edit complete.", "success");
|
|
2156
|
+
return payload;
|
|
2157
|
+
}
|
|
2158
|
+
upsertInpaintHistoryItem(makePendingInpaintHistoryItem(payload));
|
|
2159
|
+
setStatus("Image edit job " + jobId + " is " + (payload.status || "running") + ".", "");
|
|
2160
|
+
return payload;
|
|
2161
|
+
} catch (error) {
|
|
2162
|
+
console.error(error);
|
|
2163
|
+
setStatus(error instanceof Error ? error.message : "Unable to check inpaint job.", "error");
|
|
2164
|
+
return null;
|
|
2165
|
+
} finally {
|
|
2166
|
+
if (button instanceof HTMLButtonElement) {
|
|
2167
|
+
button.disabled = false;
|
|
2168
|
+
button.textContent = "Check Status";
|
|
2169
|
+
}
|
|
1972
2170
|
}
|
|
1973
|
-
return payload || {};
|
|
1974
2171
|
}
|
|
1975
2172
|
|
|
1976
|
-
async function
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
for (const entry of Array.isArray(payload.entries) ? payload.entries : []) {
|
|
1986
|
-
byId.set(entry.id, entry);
|
|
2173
|
+
async function pollInpaintJob(jobId) {
|
|
2174
|
+
for (let attempt = 0; attempt < 90; attempt += 1) {
|
|
2175
|
+
const response = await fetch("/api/v1/primitives/image_inpaint/jobs/" + encodeURIComponent(jobId), {
|
|
2176
|
+
credentials: "same-origin"
|
|
2177
|
+
});
|
|
2178
|
+
const payload = await parseJsonResponse(response);
|
|
2179
|
+
if (!response.ok) throw new Error(payload.error || "Unable to poll inpaint job.");
|
|
2180
|
+
if (payload.status === "failed" || payload.status === "cancelled") {
|
|
2181
|
+
throw new Error(payload.error?.message || "Image edit job " + payload.status + ".");
|
|
1987
2182
|
}
|
|
2183
|
+
const outputUrl = extractOutputUrl(payload);
|
|
2184
|
+
if (payload.status === "succeeded" || payload.status === "complete" || outputUrl) {
|
|
2185
|
+
if (!outputUrl) throw new Error("Image edit completed without an output URL.");
|
|
2186
|
+
applyCompletedInpaintJob(payload);
|
|
2187
|
+
setStatus("Image edit complete.", "success");
|
|
2188
|
+
return outputUrl;
|
|
2189
|
+
}
|
|
2190
|
+
upsertInpaintHistoryItem(makePendingInpaintHistoryItem(payload));
|
|
2191
|
+
setStatus("Image edit job " + jobId + " is " + (payload.status || "running") + ".", "");
|
|
2192
|
+
await new Promise((resolve) => window.setTimeout(resolve, attempt < 8 ? 1500 : 3000));
|
|
1988
2193
|
}
|
|
1989
|
-
|
|
2194
|
+
upsertInpaintHistoryItem(makePendingInpaintHistoryItem({ job_id: jobId, status: "running" }));
|
|
2195
|
+
setStatus("Image edit job is still running. Use Check Status in History.", "");
|
|
2196
|
+
return null;
|
|
1990
2197
|
}
|
|
1991
2198
|
|
|
1992
|
-
async function
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
const selectedTracers = tracerStore.get();
|
|
1996
|
-
renderTracerChips(selectedTracers);
|
|
1997
|
-
if (!selectedTracers.length) {
|
|
1998
|
-
renderEmpty("No tracer selected yet.");
|
|
2199
|
+
async function submitInpaintJob() {
|
|
2200
|
+
if (!sourceImageUrl || inpaintSubmit.disabled) {
|
|
2201
|
+
setStatus("Upload a source image first.", "error");
|
|
1999
2202
|
return;
|
|
2000
2203
|
}
|
|
2001
|
-
if (!
|
|
2002
|
-
|
|
2204
|
+
if (!inpaintStrokes.length) {
|
|
2205
|
+
setStatus("Paint at least one mask region.", "error");
|
|
2003
2206
|
return;
|
|
2004
2207
|
}
|
|
2005
|
-
|
|
2006
|
-
const
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2208
|
+
const usedColors = new Set(inpaintStrokes.map((stroke) => normalizeHexColor(stroke.color)));
|
|
2209
|
+
const globalPrompt = inpaintGlobalPrompt.value.trim();
|
|
2210
|
+
const missingPromptColor = inpaintMaskColors.find((entry) => usedColors.has(entry.color) && !String(entry.prompt || "").trim());
|
|
2211
|
+
if (!globalPrompt && missingPromptColor) {
|
|
2212
|
+
setStatus("Add a replacement prompt for " + missingPromptColor.color.toUpperCase() + ".", "error");
|
|
2213
|
+
return;
|
|
2011
2214
|
}
|
|
2012
|
-
|
|
2013
|
-
if (
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
} else if (showLoading) {
|
|
2017
|
-
historyRoot.innerHTML = '<div class="editor-history-empty">Loading POST history…</div>';
|
|
2215
|
+
const instruction = buildInpaintInstruction();
|
|
2216
|
+
if (!instruction) {
|
|
2217
|
+
setStatus("Add a replacement prompt for at least one color.", "error");
|
|
2218
|
+
return;
|
|
2018
2219
|
}
|
|
2220
|
+
setSubmitLoading(true);
|
|
2221
|
+
uploadButton.disabled = true;
|
|
2222
|
+
setStatus("Uploading masks...", "");
|
|
2019
2223
|
try {
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2224
|
+
const maskUrl = await uploadTemporaryBlob(await createProviderMaskBlob(), "inpaint-mask-" + Date.now() + ".png", "image/png");
|
|
2225
|
+
const tracer = getTracerValue();
|
|
2226
|
+
setStatus("Creating image edit job...", "");
|
|
2227
|
+
const response = await fetch("/api/v1/primitives/images/inpaint", {
|
|
2228
|
+
method: "POST",
|
|
2229
|
+
credentials: "same-origin",
|
|
2230
|
+
headers: { "content-type": "application/json" },
|
|
2231
|
+
body: JSON.stringify({
|
|
2232
|
+
tracer,
|
|
2233
|
+
payload: {
|
|
2234
|
+
source_image_url: sourceImageUrl,
|
|
2235
|
+
mask_url: absoluteUrl(maskUrl),
|
|
2236
|
+
instruction,
|
|
2237
|
+
reference_attachments: inpaintAttachments.map((attachment) => attachment.url),
|
|
2238
|
+
...(inpaintProviderSelect.value ? { provider: inpaintProviderSelect.value } : {}),
|
|
2239
|
+
size: "auto",
|
|
2240
|
+
...(getInpaintAspectRatio() ? { aspect_ratio: getInpaintAspectRatio() } : {}),
|
|
2241
|
+
output_format: "png"
|
|
2242
|
+
}
|
|
2243
|
+
})
|
|
2244
|
+
});
|
|
2245
|
+
const payload = await parseJsonResponse(response);
|
|
2246
|
+
if (!response.ok) throw new Error(payload.error || "Unable to create image edit job.");
|
|
2247
|
+
setStatus("Image edit job " + (payload.job_id || "") + " started.", "success");
|
|
2248
|
+
upsertInpaintHistoryItem(makePendingInpaintHistoryItem(payload));
|
|
2249
|
+
await pollInpaintJob(payload.job_id);
|
|
2250
|
+
await loadInpaintHistory();
|
|
2031
2251
|
} catch (error) {
|
|
2032
2252
|
console.error(error);
|
|
2033
|
-
|
|
2253
|
+
setStatus(error instanceof Error ? error.message : "Unable to create image edit.", "error");
|
|
2034
2254
|
} finally {
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
triggerButton.setAttribute("data-loading", "false");
|
|
2038
|
-
}
|
|
2255
|
+
setSubmitLoading(false);
|
|
2256
|
+
uploadButton.disabled = false;
|
|
2039
2257
|
}
|
|
2040
2258
|
}
|
|
2041
2259
|
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
method: "PATCH",
|
|
2050
|
-
credentials: "same-origin",
|
|
2051
|
-
headers: { "content-type": "application/json" },
|
|
2052
|
-
body: JSON.stringify({
|
|
2053
|
-
from_tracer: normalizedFrom,
|
|
2054
|
-
to_tracer: normalizedTo
|
|
2055
|
-
})
|
|
2056
|
-
});
|
|
2057
|
-
if (!response.ok) {
|
|
2058
|
-
throw new Error("Unable to update tracer.");
|
|
2059
|
-
}
|
|
2060
|
-
const nextTracers = tracerStore.get().map((entry) => entry === normalizedFrom ? normalizedTo : entry);
|
|
2061
|
-
tracerStore.set(nextTracers);
|
|
2062
|
-
await loadHistory({ showLoading: true });
|
|
2063
|
-
}
|
|
2064
|
-
|
|
2065
|
-
loadHistory({ showLoading: true });
|
|
2066
|
-
window.addEventListener("popstate", () => loadHistory({ showLoading: true }));
|
|
2067
|
-
window.addEventListener("vidfarm:editor-tracers-changed", () => loadHistory());
|
|
2068
|
-
if (hasTracerModal) {
|
|
2069
|
-
addTracerButton.addEventListener("click", () => openTracerModal());
|
|
2070
|
-
modalSubmit.addEventListener("click", () => submitTracerModal());
|
|
2071
|
-
modalCancel.addEventListener("click", () => closeTracerModal());
|
|
2072
|
-
modalInput.addEventListener("keydown", (event) => {
|
|
2073
|
-
if (event.key === "Enter") {
|
|
2074
|
-
event.preventDefault();
|
|
2075
|
-
submitTracerModal();
|
|
2076
|
-
return;
|
|
2077
|
-
}
|
|
2078
|
-
if (event.key === "Escape") {
|
|
2079
|
-
event.preventDefault();
|
|
2080
|
-
closeTracerModal();
|
|
2081
|
-
}
|
|
2082
|
-
});
|
|
2083
|
-
modalBackdrop.addEventListener("click", (event) => {
|
|
2084
|
-
if (event.target === modalBackdrop) {
|
|
2085
|
-
closeTracerModal();
|
|
2086
|
-
}
|
|
2087
|
-
});
|
|
2088
|
-
}
|
|
2089
|
-
if (hasMediaModal) {
|
|
2090
|
-
mediaModalClose.addEventListener("click", () => closeMediaModal());
|
|
2091
|
-
mediaModalBackdrop.addEventListener("click", (event) => {
|
|
2092
|
-
if (event.target === mediaModalBackdrop) {
|
|
2093
|
-
closeMediaModal();
|
|
2094
|
-
}
|
|
2095
|
-
});
|
|
2096
|
-
}
|
|
2097
|
-
if (hasHttpModal) {
|
|
2098
|
-
httpModalClose.addEventListener("click", () => closeHttpModal());
|
|
2099
|
-
httpModalBackdrop.addEventListener("click", (event) => {
|
|
2100
|
-
if (event.target === httpModalBackdrop) {
|
|
2101
|
-
closeHttpModal();
|
|
2102
|
-
}
|
|
2103
|
-
});
|
|
2104
|
-
}
|
|
2105
|
-
window.addEventListener("keydown", (event) => {
|
|
2106
|
-
if (event.key === "Escape") {
|
|
2107
|
-
if (hasHttpModal && httpModalBackdrop.hidden === false) {
|
|
2108
|
-
closeHttpModal();
|
|
2109
|
-
return;
|
|
2110
|
-
}
|
|
2111
|
-
if (hasMediaModal && mediaModalBackdrop.getAttribute("data-open") === "true") {
|
|
2112
|
-
closeMediaModal();
|
|
2113
|
-
return;
|
|
2114
|
-
}
|
|
2115
|
-
if (hasTracerModal && modalBackdrop.getAttribute("data-open") === "true") {
|
|
2116
|
-
closeTracerModal();
|
|
2117
|
-
}
|
|
2118
|
-
}
|
|
2260
|
+
renderInpaintControls();
|
|
2261
|
+
void loadInpaintProviders();
|
|
2262
|
+
initializeFromUrl();
|
|
2263
|
+
void loadInpaintHistory();
|
|
2264
|
+
uploadButton.addEventListener("click", () => fileInput.click());
|
|
2265
|
+
historyRefresh.addEventListener("click", () => {
|
|
2266
|
+
void loadInpaintHistory();
|
|
2119
2267
|
});
|
|
2120
|
-
|
|
2268
|
+
historyList.addEventListener("pointerdown", (event) => {
|
|
2121
2269
|
const target = event.target;
|
|
2122
|
-
if (
|
|
2123
|
-
return;
|
|
2124
|
-
}
|
|
2125
|
-
const previewButton = target.closest("[data-preview-url]");
|
|
2126
|
-
if (previewButton instanceof HTMLButtonElement) {
|
|
2127
|
-
event.preventDefault();
|
|
2270
|
+
if (target instanceof Element && target.closest("[data-history-confirm]")) {
|
|
2128
2271
|
event.stopPropagation();
|
|
2129
|
-
const url = previewButton.getAttribute("data-preview-url") || "";
|
|
2130
|
-
const kind = previewButton.getAttribute("data-preview-kind") || "file";
|
|
2131
|
-
const label = previewButton.getAttribute("data-preview-label") || "Output preview";
|
|
2132
|
-
if (url) {
|
|
2133
|
-
openMediaModal(url, kind, label);
|
|
2134
|
-
}
|
|
2135
|
-
return;
|
|
2136
2272
|
}
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2273
|
+
});
|
|
2274
|
+
historyList.addEventListener("click", (event) => {
|
|
2275
|
+
const target = event.target;
|
|
2276
|
+
if (!(target instanceof Element)) return;
|
|
2277
|
+
const confirmPopover = target.closest("[data-history-confirm]");
|
|
2278
|
+
if (confirmPopover instanceof HTMLElement) {
|
|
2140
2279
|
event.stopPropagation();
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2280
|
+
}
|
|
2281
|
+
const previewButton = target.closest("[data-history-preview]");
|
|
2282
|
+
if (previewButton instanceof HTMLElement) {
|
|
2283
|
+
const key = previewButton.dataset.historyPreview || "";
|
|
2284
|
+
const item = inpaintHistoryItems.find((entry) => entry.key === key);
|
|
2285
|
+
if (!item) return;
|
|
2286
|
+
openImagePreview(item.url, item.createdAt ? new Date(item.createdAt).toLocaleString() : "Image edit output", imageDownloadName("image-edit-history", item.createdAt));
|
|
2146
2287
|
return;
|
|
2147
2288
|
}
|
|
2148
|
-
const
|
|
2149
|
-
if (
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
}
|
|
2289
|
+
const copyButton = target.closest("[data-history-copy]");
|
|
2290
|
+
if (copyButton instanceof HTMLElement) {
|
|
2291
|
+
const key = copyButton.dataset.historyCopy || "";
|
|
2292
|
+
const item = inpaintHistoryItems.find((entry) => entry.key === key);
|
|
2293
|
+
if (!item) return;
|
|
2294
|
+
void copyText(item.url).then((copied) => {
|
|
2295
|
+
copyButton.setAttribute("title", copied ? "Copied" : "Copy failed");
|
|
2296
|
+
window.setTimeout(() => copyButton.setAttribute("title", "Copy URL"), 1200);
|
|
2297
|
+
});
|
|
2157
2298
|
return;
|
|
2158
2299
|
}
|
|
2159
|
-
const
|
|
2160
|
-
if (
|
|
2161
|
-
|
|
2162
|
-
event.stopPropagation();
|
|
2163
|
-
void loadHistory({ triggerButton: refreshButton });
|
|
2300
|
+
const checkButton = target.closest("[data-history-check]");
|
|
2301
|
+
if (checkButton instanceof HTMLButtonElement) {
|
|
2302
|
+
void checkInpaintJobStatus(checkButton.dataset.historyCheck || "", checkButton);
|
|
2164
2303
|
return;
|
|
2165
2304
|
}
|
|
2166
|
-
const
|
|
2167
|
-
if (
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
const
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
wrap.setAttribute("data-open", "true");
|
|
2305
|
+
const removeButton = target.closest("[data-history-remove]");
|
|
2306
|
+
if (removeButton instanceof HTMLElement) {
|
|
2307
|
+
const key = removeButton.dataset.historyRemove || "";
|
|
2308
|
+
const confirm = historyList.querySelector('[data-history-confirm="' + CSS.escape(key) + '"]');
|
|
2309
|
+
const shouldOpen = !(confirm instanceof HTMLElement) || confirm.hidden;
|
|
2310
|
+
closeInpaintHistoryConfirms();
|
|
2311
|
+
if (confirm instanceof HTMLElement) {
|
|
2312
|
+
confirm.hidden = !shouldOpen;
|
|
2175
2313
|
}
|
|
2314
|
+
removeButton.setAttribute("aria-expanded", shouldOpen ? "true" : "false");
|
|
2176
2315
|
return;
|
|
2177
2316
|
}
|
|
2178
|
-
const
|
|
2179
|
-
if (
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
const tracer = copyButton.getAttribute("data-editor-history-copy") || "";
|
|
2183
|
-
document.querySelectorAll("[data-editor-history-menu-wrap]").forEach((entry) => entry.removeAttribute("data-open"));
|
|
2184
|
-
if (!tracer) {
|
|
2185
|
-
return;
|
|
2186
|
-
}
|
|
2187
|
-
void copyText(tracer).then((copied) => {
|
|
2188
|
-
if (!copied) {
|
|
2189
|
-
window.prompt("Copy tracer", tracer);
|
|
2190
|
-
}
|
|
2191
|
-
}).catch((error) => {
|
|
2192
|
-
console.error(error);
|
|
2193
|
-
window.prompt("Copy tracer", tracer);
|
|
2194
|
-
});
|
|
2317
|
+
const cancelButton = target.closest("[data-history-cancel]");
|
|
2318
|
+
if (cancelButton instanceof HTMLElement) {
|
|
2319
|
+
const confirm = cancelButton.closest("[data-history-confirm]");
|
|
2320
|
+
if (confirm instanceof HTMLElement) confirm.hidden = true;
|
|
2195
2321
|
return;
|
|
2196
2322
|
}
|
|
2197
|
-
const
|
|
2198
|
-
if (
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
const nextTracer = window.prompt("Rename tracer", tracer);
|
|
2204
|
-
if (nextTracer === null || !nextTracer.trim() || nextTracer.trim() === tracer) {
|
|
2205
|
-
return;
|
|
2323
|
+
const confirmRemove = target.closest("[data-history-confirm-remove]");
|
|
2324
|
+
if (confirmRemove instanceof HTMLElement) {
|
|
2325
|
+
const key = confirmRemove.dataset.historyConfirmRemove || "";
|
|
2326
|
+
if (key) {
|
|
2327
|
+
hiddenInpaintHistoryKeys.add(key);
|
|
2328
|
+
renderInpaintHistory();
|
|
2206
2329
|
}
|
|
2207
|
-
void renameStoredTracer(tracer, nextTracer.trim()).catch((error) => {
|
|
2208
|
-
console.error(error);
|
|
2209
|
-
window.alert("Unable to rename tracer.");
|
|
2210
|
-
});
|
|
2211
2330
|
return;
|
|
2212
2331
|
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2332
|
+
});
|
|
2333
|
+
resultOpenButton.addEventListener("click", () => {
|
|
2334
|
+
if (!resultImage.currentSrc && !resultImage.src) return;
|
|
2335
|
+
openImagePreview(resultImage.currentSrc || resultImage.src, "Image edit result", imageDownloadName("image-edit-result"));
|
|
2336
|
+
});
|
|
2337
|
+
lightboxCopy.addEventListener("click", () => {
|
|
2338
|
+
const url = lightboxCopy.dataset.copyUrl || lightboxImage.currentSrc || lightboxImage.src || "";
|
|
2339
|
+
if (!url) return;
|
|
2340
|
+
void copyText(url).then((copied) => {
|
|
2341
|
+
lightboxCopy.setAttribute("title", copied ? "Copied" : "Copy failed");
|
|
2342
|
+
window.setTimeout(() => lightboxCopy.setAttribute("title", "Copy URL"), 1200);
|
|
2343
|
+
});
|
|
2344
|
+
});
|
|
2345
|
+
lightboxClose.addEventListener("click", () => closeImagePreview());
|
|
2346
|
+
lightbox.addEventListener("click", (event) => {
|
|
2347
|
+
if (event.target === lightbox) closeImagePreview();
|
|
2348
|
+
});
|
|
2349
|
+
startNewButton.addEventListener("click", () => {
|
|
2350
|
+
tracerInput.value = makeTracerId();
|
|
2351
|
+
clearInpaintUrlParams();
|
|
2352
|
+
resetWorkspace();
|
|
2353
|
+
void loadInpaintHistory();
|
|
2354
|
+
});
|
|
2355
|
+
tracerInput.addEventListener("input", () => {
|
|
2356
|
+
queueInpaintHistoryReload();
|
|
2357
|
+
});
|
|
2358
|
+
fileInput.addEventListener("change", () => {
|
|
2359
|
+
void loadSourceFile(fileInput.files && fileInput.files[0]);
|
|
2360
|
+
fileInput.value = "";
|
|
2361
|
+
});
|
|
2362
|
+
inpaintAttachmentInput.addEventListener("change", () => {
|
|
2363
|
+
void addAttachmentFiles(inpaintAttachmentInput.files);
|
|
2364
|
+
inpaintAttachmentInput.value = "";
|
|
2365
|
+
});
|
|
2366
|
+
inpaintGlobalAttachments.addEventListener("click", (event) => {
|
|
2367
|
+
const target = event.target;
|
|
2368
|
+
if (!(target instanceof Element)) return;
|
|
2369
|
+
const removeColor = target.closest("[data-editor-inpaint-remove-color]");
|
|
2370
|
+
if (removeColor instanceof HTMLElement && removeColor.dataset.editorInpaintRemoveColor) {
|
|
2371
|
+
removeInpaintColor(removeColor.dataset.editorInpaintRemoveColor);
|
|
2229
2372
|
return;
|
|
2230
2373
|
}
|
|
2231
|
-
const
|
|
2232
|
-
if (
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
const tracer = removeButton.getAttribute("data-editor-history-remove") || "";
|
|
2236
|
-
document.querySelectorAll("[data-editor-history-menu-wrap]").forEach((entry) => entry.removeAttribute("data-open"));
|
|
2237
|
-
if (!tracer) {
|
|
2238
|
-
return;
|
|
2239
|
-
}
|
|
2240
|
-
const confirmed = window.confirm('Remove "' + tracer + '" from this page view? This does not delete its runs. You can still find them in Job Runs.');
|
|
2241
|
-
if (confirmed) {
|
|
2242
|
-
tracerStore.remove(tracer);
|
|
2243
|
-
}
|
|
2374
|
+
const remove = target.closest("[data-editor-inpaint-remove-attachment]");
|
|
2375
|
+
if (remove instanceof HTMLElement) {
|
|
2376
|
+
inpaintAttachments = inpaintAttachments.filter((attachment) => attachment.id !== remove.dataset.editorInpaintRemoveAttachment);
|
|
2377
|
+
renderAllAttachmentChips();
|
|
2244
2378
|
}
|
|
2245
2379
|
});
|
|
2246
|
-
document.addEventListener("click", (
|
|
2380
|
+
document.querySelector('[data-editor-inpaint-attach-scope="global"]')?.addEventListener("click", () => {
|
|
2381
|
+
openAttachmentPicker("global");
|
|
2382
|
+
});
|
|
2383
|
+
inpaintGlobalPrompt.addEventListener("paste", (event) => {
|
|
2384
|
+
handleAttachmentPaste(event, "global");
|
|
2385
|
+
});
|
|
2386
|
+
inpaintUndo.addEventListener("click", () => {
|
|
2387
|
+
const stroke = inpaintStrokes.pop();
|
|
2388
|
+
if (stroke) redoStrokes.push(stroke);
|
|
2389
|
+
redrawInpaintMask();
|
|
2390
|
+
setStatus("", "");
|
|
2391
|
+
});
|
|
2392
|
+
inpaintRedo.addEventListener("click", () => {
|
|
2393
|
+
const stroke = redoStrokes.pop();
|
|
2394
|
+
if (stroke) inpaintStrokes.push(stroke);
|
|
2395
|
+
redrawInpaintMask();
|
|
2396
|
+
setStatus("", "");
|
|
2397
|
+
});
|
|
2398
|
+
inpaintClear.addEventListener("click", () => {
|
|
2399
|
+
resetMask();
|
|
2400
|
+
setStatus("", "");
|
|
2401
|
+
});
|
|
2402
|
+
inpaintBrush.addEventListener("input", () => {
|
|
2403
|
+
inpaintBrushLabel.textContent = inpaintBrush.value;
|
|
2404
|
+
updateBrushCursorSize();
|
|
2405
|
+
});
|
|
2406
|
+
inpaintColorPicker.addEventListener("input", () => {
|
|
2407
|
+
activeInpaintColor = normalizeHexColor(inpaintColorPicker.value);
|
|
2408
|
+
renderInpaintControls();
|
|
2409
|
+
});
|
|
2410
|
+
inpaintAddColor.addEventListener("click", () => addInpaintColor(inpaintColorPicker.value));
|
|
2411
|
+
inpaintPrompts.addEventListener("click", (event) => {
|
|
2247
2412
|
const target = event.target;
|
|
2248
|
-
if (!(target instanceof Element))
|
|
2413
|
+
if (!(target instanceof Element)) return;
|
|
2414
|
+
const remove = target.closest("[data-editor-inpaint-remove-attachment]");
|
|
2415
|
+
if (remove instanceof HTMLElement) {
|
|
2416
|
+
inpaintAttachments = inpaintAttachments.filter((attachment) => attachment.id !== remove.dataset.editorInpaintRemoveAttachment);
|
|
2417
|
+
renderAllAttachmentChips();
|
|
2249
2418
|
return;
|
|
2250
2419
|
}
|
|
2251
|
-
const
|
|
2252
|
-
if (
|
|
2420
|
+
const attach = target.closest("[data-editor-inpaint-attach-scope]");
|
|
2421
|
+
if (attach instanceof HTMLElement && attach.dataset.editorInpaintAttachScope === "color" && attach.dataset.color) {
|
|
2422
|
+
openAttachmentPicker("color", attach.dataset.color);
|
|
2253
2423
|
return;
|
|
2254
2424
|
}
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2425
|
+
const row = target.closest(".editor-inpaint-color-row");
|
|
2426
|
+
if (row instanceof HTMLElement && row.dataset.color) selectInpaintColor(row.dataset.color);
|
|
2427
|
+
});
|
|
2428
|
+
inpaintPrompts.addEventListener("focusin", (event) => {
|
|
2429
|
+
const target = event.target;
|
|
2430
|
+
if (!(target instanceof Element)) return;
|
|
2431
|
+
const row = target.closest(".editor-inpaint-color-row");
|
|
2432
|
+
if (row instanceof HTMLElement && row.dataset.color) selectInpaintColor(row.dataset.color);
|
|
2433
|
+
});
|
|
2434
|
+
inpaintPrompts.addEventListener("input", (event) => {
|
|
2435
|
+
const target = event.target;
|
|
2436
|
+
if (target instanceof HTMLTextAreaElement) updateInpaintPrompt(target.dataset.color || "", target.value);
|
|
2437
|
+
});
|
|
2438
|
+
inpaintPrompts.addEventListener("paste", (event) => {
|
|
2439
|
+
const target = event.target;
|
|
2440
|
+
if (target instanceof HTMLTextAreaElement && target.dataset.color) {
|
|
2441
|
+
handleAttachmentPaste(event, "color", target.dataset.color);
|
|
2442
|
+
}
|
|
2443
|
+
});
|
|
2444
|
+
inpaintCanvas.addEventListener("pointerenter", (event) => updateBrushCursorPosition(event));
|
|
2445
|
+
inpaintCanvas.addEventListener("pointerdown", (event) => {
|
|
2446
|
+
updateBrushCursorPosition(event);
|
|
2447
|
+
beginInpaintStroke(event);
|
|
2448
|
+
});
|
|
2449
|
+
inpaintCanvas.addEventListener("pointermove", (event) => {
|
|
2450
|
+
updateBrushCursorPosition(event);
|
|
2451
|
+
continueInpaintStroke(event);
|
|
2452
|
+
});
|
|
2453
|
+
inpaintCanvas.addEventListener("pointerup", (event) => endInpaintStroke(event));
|
|
2454
|
+
inpaintCanvas.addEventListener("pointercancel", (event) => {
|
|
2455
|
+
endInpaintStroke(event);
|
|
2456
|
+
hideBrushCursor();
|
|
2457
|
+
});
|
|
2458
|
+
inpaintCanvas.addEventListener("pointerleave", (event) => {
|
|
2459
|
+
endInpaintStroke(event);
|
|
2460
|
+
hideBrushCursor();
|
|
2461
|
+
});
|
|
2462
|
+
inpaintSubmit.addEventListener("click", () => {
|
|
2463
|
+
void submitInpaintJob();
|
|
2464
|
+
});
|
|
2465
|
+
settingsButton.addEventListener("click", () => {
|
|
2466
|
+
settingsPopover.hidden = !settingsPopover.hidden;
|
|
2467
|
+
if (!settingsPopover.hidden) tracerInput.focus();
|
|
2468
|
+
});
|
|
2469
|
+
window.addEventListener("click", (event) => {
|
|
2470
|
+
const target = event.target;
|
|
2471
|
+
if (!(target instanceof Node)) return;
|
|
2472
|
+
const isHistoryConfirmTarget = target instanceof Element && Boolean(target.closest("[data-history-confirm]"));
|
|
2473
|
+
if (!historyList.contains(target) && !isHistoryConfirmTarget) {
|
|
2474
|
+
closeInpaintHistoryConfirms();
|
|
2475
|
+
}
|
|
2476
|
+
if (!settingsPopover.hidden && !settingsPopover.contains(target) && !settingsButton.contains(target)) {
|
|
2477
|
+
settingsPopover.hidden = true;
|
|
2478
|
+
}
|
|
2479
|
+
});
|
|
2480
|
+
window.addEventListener("resize", () => syncInpaintCanvasSize());
|
|
2481
|
+
window.addEventListener("keydown", (event) => {
|
|
2482
|
+
if (event.key === "Escape" && lightbox.getAttribute("data-open") === "true") {
|
|
2483
|
+
closeImagePreview();
|
|
2260
2484
|
}
|
|
2261
|
-
showCopyingFeedback(copyButton, label);
|
|
2262
|
-
void copyText(value).then((copied) => {
|
|
2263
|
-
if (!copied) {
|
|
2264
|
-
showCopyErrorFeedback(copyButton, label);
|
|
2265
|
-
window.prompt(label, value);
|
|
2266
|
-
return;
|
|
2267
|
-
}
|
|
2268
|
-
showCopyFeedback(copyButton, label);
|
|
2269
|
-
}).catch((error) => {
|
|
2270
|
-
console.error(error);
|
|
2271
|
-
showCopyErrorFeedback(copyButton, label);
|
|
2272
|
-
window.prompt(label, value);
|
|
2273
|
-
});
|
|
2274
2485
|
});
|
|
2275
2486
|
})();
|
|
2276
2487
|
</script>
|
|
2277
2488
|
`
|
|
2278
2489
|
});
|
|
2279
2490
|
}
|
|
2280
|
-
|
|
2281
|
-
if (/\.(mp4|webm|ogg|mov)(\?|#|$)/i.test(url)) {
|
|
2282
|
-
return `<video class="editor-preview" src="${escapeAttribute(url)}" controls playsinline preload="metadata" aria-label="${escapeAttribute(alt)}"></video>`;
|
|
2283
|
-
}
|
|
2284
|
-
return `<img class="editor-preview" src="${escapeAttribute(url)}" alt="${escapeAttribute(alt)}" loading="lazy" decoding="async" />`;
|
|
2285
|
-
}
|
|
2286
|
-
function renderQuickstartTextarea(label, value, rows) {
|
|
2287
|
-
const text = value.trim() || "Not configured for this template yet.";
|
|
2288
|
-
return `
|
|
2289
|
-
<div class="editor-quickstart-field">
|
|
2290
|
-
<div class="editor-quickstart-field-head">
|
|
2291
|
-
<span>${escapeHtml(label)}</span>
|
|
2292
|
-
<button
|
|
2293
|
-
type="button"
|
|
2294
|
-
class="editor-quickstart-copy"
|
|
2295
|
-
data-editor-copy-content="${escapeAttribute(text)}"
|
|
2296
|
-
data-editor-copy-label="${escapeAttribute(label)}"
|
|
2297
|
-
aria-label="Copy ${escapeAttribute(label)}"
|
|
2298
|
-
title="Copy ${escapeAttribute(label)}"
|
|
2299
|
-
>
|
|
2300
|
-
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
|
2301
|
-
<rect x="9" y="9" width="10" height="10" rx="2" fill="none" stroke="currentColor" stroke-width="2"></rect>
|
|
2302
|
-
<path d="M6 15H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path>
|
|
2303
|
-
</svg>
|
|
2304
|
-
</button>
|
|
2305
|
-
</div>
|
|
2306
|
-
<textarea class="editor-quickstart-textarea" readonly rows="${rows}" spellcheck="false" aria-label="${escapeAttribute(label)}">${escapeHtml(text)}</textarea>
|
|
2307
|
-
</div>
|
|
2308
|
-
`;
|
|
2309
|
-
}
|
|
2491
|
+
//# sourceMappingURL=template-editor-pages.js.map
|