@officexapp/vidfarm-devcli 0.21.27 → 0.21.28
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/.agents/skills/editor-capabilities/SKILL.md +26 -0
- package/.agents/skills/vidfarm/SKILL.md +6 -4
- package/.agents/skills/vidfarm/recipes/local-edit-render-approve.md +3 -2
- package/.agents/skills/vidfarm/references/assets-and-sourcing.md +3 -3
- package/.agents/skills/vidfarm/references/automation-and-local-dev.md +32 -0
- package/.agents/skills/vidfarm/references/editor-workflows.md +41 -5
- package/.agents/skills/vidfarm/references/onboarding.md +4 -4
- package/.agents/skills/vidfarm/references/primitives.md +5 -1
- package/SKILL.director.md +94 -19
- package/SKILL.md +5 -2
- package/dist/src/cli.js +69 -0
- package/dist/src/devcli/qa-check.js +420 -0
- package/package.json +3 -1
- package/public/serve-shells/tools-image.html +378 -265
- package/public/serve-shells/tools-video.html +760 -167
|
@@ -1185,7 +1185,8 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
1185
1185
|
.rk-inpaint-workbench.is-locked .rk-inpaint-generate{opacity:1;cursor:default;background:var(--rk-n-50);color:var(--rk-text-muted);border-color:var(--rk-border);box-shadow:none;font-size:12.5px}
|
|
1186
1186
|
.rk-inpaint-workbench.is-locked .rk-inpaint-generate svg{display:none}
|
|
1187
1187
|
|
|
1188
|
-
|
|
1188
|
+
|
|
1189
|
+
/* ---- shared chat surface (tools/image + tools/video) ---------------------- */
|
|
1189
1190
|
.rk-ip-chat{display:grid;grid-template-rows:minmax(0,1fr) auto;height:min(76vh,860px);min-height:520px;overflow:hidden}
|
|
1190
1191
|
.rk-ip-feed{min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:18px 18px 6px;display:flex;flex-direction:column;gap:14px}
|
|
1191
1192
|
.rk-ip-canvasholder{display:none}
|
|
@@ -1211,43 +1212,24 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
1211
1212
|
.rk-ip-turn.is-flash .rk-ip-bubble,.rk-ip-turn.is-flash .rk-ip-media-frame{box-shadow:0 0 0 3px var(--rk-gold-600)}
|
|
1212
1213
|
.rk-ip-turn[data-kind="stopped"] .rk-ip-bubble{background:var(--rk-n-100);border-color:var(--rk-border-strong);color:var(--rk-text-muted);font-weight:700}
|
|
1213
1214
|
.rk-ip-turn[data-kind="error"] .rk-ip-bubble{background:#fdf3f2;border-color:#e6a8a0;color:#b42318;font-weight:600}
|
|
1214
|
-
.rk-inpaint-empty{display:grid;justify-items:center;gap:8px;text-align:center;color:var(--rk-text-muted);padding:34px 20px;margin:auto 0}
|
|
1215
|
-
.rk-inpaint-empty[hidden]{display:none}
|
|
1216
|
-
.rk-inpaint-empty-ic{color:var(--rk-n-400)}
|
|
1217
|
-
.rk-inpaint-empty-title{font-family:var(--rk-font-display);font-weight:800;font-size:var(--rk-text-lg);color:var(--rk-ink)}
|
|
1218
|
-
.rk-inpaint-empty-sub{font-size:13px}
|
|
1219
|
-
.editor-inpaint-canvas-wrap{position:relative;display:inline-block;line-height:0;max-width:100%;border-radius:var(--rk-r-2xl);overflow:hidden;box-shadow:var(--rk-shadow-md);
|
|
1220
|
-
--inpaint-brush-cursor-size:70px;--inpaint-brush-cursor-x:0px;--inpaint-brush-cursor-y:0px}
|
|
1221
|
-
.editor-inpaint-canvas-wrap[hidden]{display:none}
|
|
1222
|
-
.editor-inpaint-canvas-wrap img{display:block;max-width:100%;max-height:min(52vh,520px);width:auto;height:auto;object-fit:contain;user-select:none}
|
|
1223
|
-
/* paint toolbar — sits directly under the image it paints, never over it */
|
|
1224
|
-
/* align-items:center keeps the canvas wrap at the image's intrinsic width — stretching
|
|
1225
|
-
it would stretch the absolutely-positioned mask canvas and skew stroke coordinates. */
|
|
1226
|
-
.rk-ip-paintstack{display:inline-flex;flex-direction:column;align-items:center;max-width:100%}
|
|
1227
|
-
.rk-ip-painttools{width:100%;box-sizing:border-box;display:flex;align-items:center;justify-content:center;gap:6px;padding:6px 8px;border-top:1px solid var(--rk-border);background:var(--rk-n-50);line-height:1;flex-wrap:wrap}
|
|
1228
|
-
.rk-ip-painttools-brush{display:flex;align-items:center;gap:6px}
|
|
1229
|
-
.rk-ip-painttools-label{font-size:11px;font-weight:800;color:var(--rk-text-muted);white-space:nowrap}
|
|
1230
|
-
.rk-ip-painttools .rk-inpaint-range{width:86px}
|
|
1231
|
-
.rk-ip-painttool{display:grid;place-items:center;width:26px;height:26px;border-radius:50%;border:1px solid var(--rk-border);background:#fff;color:var(--rk-n-700);cursor:pointer}
|
|
1232
|
-
.rk-ip-painttool:hover:not(:disabled){background:var(--rk-gold-tint);color:var(--rk-gold-700);border-color:var(--rk-gold-600)}
|
|
1233
|
-
.rk-ip-painttool:disabled{opacity:.45;cursor:default}
|
|
1234
|
-
.rk-ip-painttool svg{width:13px;height:13px}
|
|
1235
|
-
.editor-inpaint-canvas-wrap canvas{position:absolute;inset:0;width:100%;height:100%;opacity:.86;cursor:none;touch-action:none}
|
|
1236
|
-
.editor-inpaint-brush-cursor{position:absolute;z-index:2;left:0;top:0;width:var(--inpaint-brush-cursor-size);height:var(--inpaint-brush-cursor-size);
|
|
1237
|
-
border:2px solid rgba(255,255,255,.95);border-radius:50%;box-shadow:0 0 0 1px rgba(28,25,23,.7),0 0 18px rgba(28,25,23,.25);opacity:0;pointer-events:none;
|
|
1238
|
-
transform:translate(var(--inpaint-brush-cursor-x),var(--inpaint-brush-cursor-y)) translate(-50%,-50%)}
|
|
1239
|
-
.editor-inpaint-brush-cursor[data-visible="true"]{opacity:1}
|
|
1240
1215
|
|
|
1241
|
-
/*
|
|
1216
|
+
/* ---- composer ------------------------------------------------------------ */
|
|
1242
1217
|
.rk-ip-composer{flex:none;margin:0;padding:12px 18px 16px;border-top:1px solid var(--rk-border);background:var(--rk-surface);display:grid;gap:8px;
|
|
1243
1218
|
border-bottom-left-radius:var(--rk-r-3xl);border-bottom-right-radius:var(--rk-r-3xl)}
|
|
1244
1219
|
.rk-ip-composer.is-dragref{background:var(--rk-gold-tint)}
|
|
1245
1220
|
.rk-ip-composer.is-busy .rk-ip-comp-status{color:var(--rk-gold-700)}
|
|
1246
|
-
|
|
1247
|
-
|
|
1221
|
+
/* The grip is anchored to this wrapper, not the textarea, so it never scrolls
|
|
1222
|
+
away with the textarea's own overflow once the message passes 10 rows. */
|
|
1223
|
+
.rk-ip-comp-field{position:relative;display:block}
|
|
1224
|
+
.rk-ip-comp-input{display:block;width:100%;border:1.5px solid var(--rk-border-strong);border-radius:var(--rk-r-2xl);background:#fff;outline:0;resize:none;font:inherit;font-size:14px;line-height:1.45;
|
|
1225
|
+
color:var(--rk-ink);min-height:56px;overflow-y:hidden;padding:10px 12px;transition:border-color var(--rk-dur) var(--rk-ease),box-shadow var(--rk-dur) var(--rk-ease)}
|
|
1248
1226
|
.rk-ip-comp-input:focus{border-color:var(--rk-gold-600);box-shadow:0 0 0 3px var(--rk-gold-tint)}
|
|
1249
1227
|
.rk-ip-comp-input::placeholder{color:var(--rk-text-faint)}
|
|
1250
1228
|
.rk-ip-comp-input:disabled{cursor:not-allowed;color:var(--rk-text-muted);background:var(--rk-n-50)}
|
|
1229
|
+
.rk-ip-comp-grip{position:absolute;right:5px;bottom:5px;width:16px;height:16px;display:grid;place-items:center;color:var(--rk-n-400);cursor:ns-resize;touch-action:none;
|
|
1230
|
+
border-radius:4px;opacity:.75;transition:color var(--rk-dur) var(--rk-ease),opacity var(--rk-dur) var(--rk-ease)}
|
|
1231
|
+
.rk-ip-comp-grip:hover,.rk-ip-comp-field.is-resizing .rk-ip-comp-grip{color:var(--rk-gold-700);opacity:1}
|
|
1232
|
+
.rk-ip-comp-field.is-resizing{user-select:none}
|
|
1251
1233
|
.rk-ip-comp-bar{display:flex;align-items:center;gap:8px}
|
|
1252
1234
|
.rk-ip-comp-modeselect{appearance:none;-webkit-appearance:none;border:1px solid var(--rk-border-strong);border-radius:999px;background:var(--rk-n-50);color:var(--rk-ink);
|
|
1253
1235
|
font:inherit;font-size:12px;font-weight:800;padding:7px 26px 7px 12px;cursor:pointer;outline:0;
|
|
@@ -1261,10 +1243,8 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
1261
1243
|
font-size:11.5px;font-weight:700;color:var(--rk-gold-700);justify-self:start;max-width:100%}
|
|
1262
1244
|
.rk-ip-comp-mask[hidden]{display:none}
|
|
1263
1245
|
.rk-ip-comp-mask button{border:0;background:transparent;color:inherit;font:inherit;font-size:11.5px;font-weight:800;text-decoration:underline;cursor:pointer;padding:0}
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
.rk-ip-preset{flex:none;border:1px dashed var(--rk-border-strong,var(--rk-border));background:var(--rk-n-50);color:var(--rk-n-700);border-radius:999px;padding:4px 10px;font-size:11.5px;font-weight:600;cursor:pointer;transition:background var(--rk-dur) var(--rk-ease),color var(--rk-dur) var(--rk-ease),border-color var(--rk-dur) var(--rk-ease)}
|
|
1267
|
-
.rk-ip-preset:hover{background:var(--rk-gold-tint);color:var(--rk-gold-700);border-color:var(--rk-gold-600)}
|
|
1246
|
+
|
|
1247
|
+
/* ---- attachment chips ---------------------------------------------------- */
|
|
1268
1248
|
.rk-ip-gen-refs{display:flex;flex-wrap:wrap;gap:6px}
|
|
1269
1249
|
.rk-ip-gen-refs[hidden]{display:none}
|
|
1270
1250
|
.rk-ip-gen-ref{display:inline-flex;align-items:center;gap:6px;padding:3px 4px 3px 6px;border:1px solid var(--rk-border);border-radius:var(--rk-r-lg);background:var(--rk-n-50);font-size:11.5px;color:var(--rk-n-700);max-width:180px}
|
|
@@ -1272,25 +1252,25 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
1272
1252
|
.rk-ip-gen-ref span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1273
1253
|
.rk-ip-gen-ref button{flex:none;border:0;background:transparent;cursor:pointer;color:var(--rk-text-faint);font-size:15px;line-height:1;padding:0 2px}
|
|
1274
1254
|
.rk-ip-gen-ref button:hover{color:var(--rk-ink)}
|
|
1275
|
-
.rk-ip-gen-
|
|
1276
|
-
.rk-ip-gen-hint{flex:1;font-size:11.5px;color:var(--rk-text-faint);line-height:1.35}
|
|
1255
|
+
.rk-ip-gen-ref.is-pending{opacity:.6}
|
|
1277
1256
|
.rk-ip-gen-clip,.rk-ip-gen-send{flex:none;display:grid;place-items:center;width:34px;height:34px;border-radius:50%;cursor:pointer;transition:background var(--rk-dur) var(--rk-ease),color var(--rk-dur) var(--rk-ease),opacity var(--rk-dur) var(--rk-ease)}
|
|
1278
1257
|
.rk-ip-gen-clip{border:1px solid var(--rk-border);background:#fff;color:var(--rk-n-600)}
|
|
1279
1258
|
.rk-ip-gen-clip:hover{background:var(--rk-gold-tint);color:var(--rk-gold-700);border-color:var(--rk-gold-600)}
|
|
1259
|
+
.rk-ip-gen-clip:disabled{opacity:.5;cursor:not-allowed}
|
|
1280
1260
|
.rk-ip-gen-send{border:0;background:var(--rk-gold-500,var(--rk-gold-600));color:var(--rk-ink)}
|
|
1281
1261
|
.rk-ip-gen-send:hover:not(:disabled){background:var(--rk-gold-600)}
|
|
1282
1262
|
.rk-ip-gen-send:disabled{opacity:.5;cursor:default}
|
|
1283
1263
|
.rk-ip-gen-send[data-mode="stop"]{background:var(--rk-ink);color:#fff}
|
|
1284
|
-
.rk-ip-gen-send[data-mode="stop"]:hover{background:#000}
|
|
1285
|
-
.rk-ip-gen-send.is-busy{background:var(--rk-gold-tint);pointer-events:none}
|
|
1286
|
-
.rk-ip-gen-spin,.rk-ip-gen-send .rk-ip-gen-spin{width:16px;height:16px;border-radius:50%;border:2.5px solid var(--rk-gold-tint2,rgba(252,185,0,.35));border-top-color:var(--rk-gold-700);animation:rk-ip-spin .8s linear infinite}
|
|
1287
1264
|
.rk-ip-gen-status{font-size:12px;font-weight:600;min-height:16px;color:var(--rk-text-muted);text-align:left;padding-left:2px}
|
|
1265
|
+
.rk-ip-gen-status[hidden]{display:none}
|
|
1288
1266
|
.rk-ip-gen-status[data-tone="error"]{color:#b42318}
|
|
1289
1267
|
.rk-ip-gen-status[data-tone="success"]{color:#1f7a44}
|
|
1268
|
+
.rk-ip-gen-presets{display:flex;flex-wrap:wrap;gap:6px}
|
|
1269
|
+
.rk-ip-preset{flex:none;border:1px dashed var(--rk-border-strong,var(--rk-border));background:var(--rk-n-50);color:var(--rk-n-700);border-radius:999px;padding:4px 10px;font-size:11.5px;font-weight:600;cursor:pointer;transition:background var(--rk-dur) var(--rk-ease),color var(--rk-dur) var(--rk-ease),border-color var(--rk-dur) var(--rk-ease)}
|
|
1270
|
+
.rk-ip-preset:hover{background:var(--rk-gold-tint);color:var(--rk-gold-700);border-color:var(--rk-gold-600)}
|
|
1290
1271
|
|
|
1291
|
-
/*
|
|
1272
|
+
/* ---- attach pop panel (shared file explorer) ----------------------------- */
|
|
1292
1273
|
.rk-ip-filemodal{position:fixed;inset:0;z-index:60;background:rgba(28,25,23,.44);backdrop-filter:blur(2px);display:grid;place-items:center;padding:20px;animation:rk-ip-fade .16s var(--rk-ease)}
|
|
1293
|
-
@keyframes rk-ip-fade{from{opacity:0}to{opacity:1}}
|
|
1294
1274
|
.rk-ip-filemodal-panel{width:min(560px,100%);max-height:min(80vh,680px);display:flex;flex-direction:column;background:var(--rk-surface);border:1px solid var(--rk-border);border-radius:var(--rk-r-3xl);box-shadow:var(--rk-shadow-lg,var(--rk-shadow-card));overflow:hidden}
|
|
1295
1275
|
.rk-ip-filemodal-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:14px 18px;border-bottom:1px solid var(--rk-border)}
|
|
1296
1276
|
.rk-ip-filemodal-title{font-family:var(--rk-font-display);font-weight:800;font-size:var(--rk-text-md,15px);color:var(--rk-ink)}
|
|
@@ -1301,6 +1281,37 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
1301
1281
|
.rk-ip-filemodal-close{border:0;background:transparent;font-size:22px;line-height:1;color:var(--rk-text-muted);cursor:pointer;padding:0 4px}
|
|
1302
1282
|
.rk-ip-filemodal-close:hover{color:var(--rk-ink)}
|
|
1303
1283
|
.rk-ip-filemodal-mount{padding:12px 14px;overflow:auto;flex:1;min-height:0}
|
|
1284
|
+
@keyframes rk-ip-spin{to{transform:rotate(360deg)}}
|
|
1285
|
+
@keyframes rk-ip-fade{from{opacity:0}to{opacity:1}}
|
|
1286
|
+
@media(max-width:820px){.rk-ip-chat{height:auto;min-height:0}.rk-ip-feed{max-height:62vh}}
|
|
1287
|
+
|
|
1288
|
+
.rk-inpaint-empty{display:grid;justify-items:center;gap:8px;text-align:center;color:var(--rk-text-muted);padding:34px 20px;margin:auto 0}
|
|
1289
|
+
.rk-inpaint-empty[hidden]{display:none}
|
|
1290
|
+
.rk-inpaint-empty-ic{color:var(--rk-n-400)}
|
|
1291
|
+
.rk-inpaint-empty-title{font-family:var(--rk-font-display);font-weight:800;font-size:var(--rk-text-lg);color:var(--rk-ink)}
|
|
1292
|
+
.rk-inpaint-empty-sub{font-size:13px}
|
|
1293
|
+
.editor-inpaint-canvas-wrap{position:relative;display:inline-block;line-height:0;max-width:100%;border-radius:var(--rk-r-2xl);overflow:hidden;box-shadow:var(--rk-shadow-md);
|
|
1294
|
+
--inpaint-brush-cursor-size:70px;--inpaint-brush-cursor-x:0px;--inpaint-brush-cursor-y:0px}
|
|
1295
|
+
.editor-inpaint-canvas-wrap[hidden]{display:none}
|
|
1296
|
+
.editor-inpaint-canvas-wrap img{display:block;max-width:100%;max-height:min(52vh,520px);width:auto;height:auto;object-fit:contain;user-select:none}
|
|
1297
|
+
/* paint toolbar — sits directly under the image it paints, never over it */
|
|
1298
|
+
/* align-items:center keeps the canvas wrap at the image's intrinsic width — stretching
|
|
1299
|
+
it would stretch the absolutely-positioned mask canvas and skew stroke coordinates. */
|
|
1300
|
+
.rk-ip-paintstack{display:inline-flex;flex-direction:column;align-items:center;max-width:100%}
|
|
1301
|
+
.rk-ip-painttools{width:100%;box-sizing:border-box;display:flex;align-items:center;justify-content:center;gap:6px;padding:6px 8px;border-top:1px solid var(--rk-border);background:var(--rk-n-50);line-height:1;flex-wrap:wrap}
|
|
1302
|
+
.rk-ip-painttools-brush{display:flex;align-items:center;gap:6px}
|
|
1303
|
+
.rk-ip-painttools-label{font-size:11px;font-weight:800;color:var(--rk-text-muted);white-space:nowrap}
|
|
1304
|
+
.rk-ip-painttools .rk-inpaint-range{width:86px}
|
|
1305
|
+
.rk-ip-painttool{display:grid;place-items:center;width:26px;height:26px;border-radius:50%;border:1px solid var(--rk-border);background:#fff;color:var(--rk-n-700);cursor:pointer}
|
|
1306
|
+
.rk-ip-painttool:hover:not(:disabled){background:var(--rk-gold-tint);color:var(--rk-gold-700);border-color:var(--rk-gold-600)}
|
|
1307
|
+
.rk-ip-painttool:disabled{opacity:.45;cursor:default}
|
|
1308
|
+
.rk-ip-painttool svg{width:13px;height:13px}
|
|
1309
|
+
.editor-inpaint-canvas-wrap canvas{position:absolute;inset:0;width:100%;height:100%;opacity:.86;cursor:none;touch-action:none}
|
|
1310
|
+
.editor-inpaint-brush-cursor{position:absolute;z-index:2;left:0;top:0;width:var(--inpaint-brush-cursor-size);height:var(--inpaint-brush-cursor-size);
|
|
1311
|
+
border:2px solid rgba(255,255,255,.95);border-radius:50%;box-shadow:0 0 0 1px rgba(28,25,23,.7),0 0 18px rgba(28,25,23,.25);opacity:0;pointer-events:none;
|
|
1312
|
+
transform:translate(var(--inpaint-brush-cursor-x),var(--inpaint-brush-cursor-y)) translate(-50%,-50%)}
|
|
1313
|
+
.editor-inpaint-brush-cursor[data-visible="true"]{opacity:1}
|
|
1314
|
+
|
|
1304
1315
|
|
|
1305
1316
|
/* right: result + history */
|
|
1306
1317
|
.rk-inpaint-output{padding:18px;display:grid;gap:18px;align-content:start}
|
|
@@ -1457,7 +1468,7 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
1457
1468
|
<div class="rk-inpaint-head-text">
|
|
1458
1469
|
<div class="rk-ip-tabs" role="tablist" aria-label="Editor mode"><a class="rk-ip-tab is-active" href="/tools/image" role="tab" aria-selected="true"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="8.5" cy="9" r="1.5"/><path d="m4 17 4.5-4.5 3 3L16 11l4 4"/></svg><span>Image</span></a><a class="rk-ip-tab" href="/tools/video" role="tab" aria-selected="false"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="5" width="14" height="14" rx="2"/><path d="m17 9 4-2v10l-4-2"/></svg><span>Video</span></a><a class="rk-ip-tab" href="/tools/clipper" role="tab" aria-selected="false"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M8.1 8.1 20 18M8.1 15.9 20 6"/></svg><span>Clipper</span></a></div>
|
|
1459
1470
|
<h1 class="rk-h1">Create Image</h1>
|
|
1460
|
-
<p class="rk-sub">Chat to create an image, then keep chatting to refine it — every message edits the newest image, and only that image, so instructions stay sharp. Paint a mask on it to change only that region
|
|
1471
|
+
<p class="rk-sub">Chat to create an image, then keep chatting to refine it — every message edits the newest image, and only that image, so instructions stay sharp. Paint a mask on it to change only that region. To animate an image, take it to the Video tool.</p>
|
|
1461
1472
|
</div>
|
|
1462
1473
|
<div class="rk-inpaint-toolbar">
|
|
1463
1474
|
<button type="button" class="rk-btn rk-btn-ghost rk-btn-sm rk-inpaint-startnew" data-custom-inpaint-start-new><svg width="14" height="14" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 4v12"/><path d="M4 10h12"/></svg> Start New</button>
|
|
@@ -1516,7 +1527,7 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
1516
1527
|
<div class="rk-inpaint-empty" data-custom-inpaint-empty>
|
|
1517
1528
|
<span class="rk-inpaint-empty-ic" aria-hidden="true"><svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.5l1.9 5.1 5.1 1.9-5.1 1.9L12 16.5l-1.9-5.1L5 9.5l5.1-1.9z"/></svg></span>
|
|
1518
1529
|
<p class="rk-inpaint-empty-title">Describe the image you want</p>
|
|
1519
|
-
<p class="rk-inpaint-empty-sub">Then keep chatting to refine it — every message edits the newest image. Paint a mask on it first to edit just that region
|
|
1530
|
+
<p class="rk-inpaint-empty-sub">Then keep chatting to refine it — every message edits the newest image. Paint a mask on it first to edit just that region.</p>
|
|
1520
1531
|
<div class="rk-ip-gen-presets" id="rk-ip-genpresets">
|
|
1521
1532
|
<button type="button" class="rk-ip-preset" data-preset="character-card" title="Fill the prompt with a character reference sheet (multi-angle, expressions, poses)">+ Character card</button>
|
|
1522
1533
|
<button type="button" class="rk-ip-preset" data-preset="storyboard" title="Fill the prompt with a numbered storyboard (sequential shot panels)">+ Storyboard</button>
|
|
@@ -1548,16 +1559,14 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
1548
1559
|
<div class="rk-ip-composer" data-ip-composer>
|
|
1549
1560
|
<div class="rk-ip-comp-mask" data-ip-comp-mask hidden></div>
|
|
1550
1561
|
<div class="rk-ip-gen-refs" id="rk-ip-genrefs" hidden></div>
|
|
1551
|
-
<
|
|
1562
|
+
<div class="rk-ip-comp-field">
|
|
1563
|
+
<textarea id="rk-ip-genprompt" class="rk-ip-comp-input" rows="2" placeholder="Describe the image you want…"></textarea>
|
|
1564
|
+
<span class="rk-ip-comp-grip" data-ip-grip role="separator" aria-label="Drag to resize the message box" title="Drag to resize"><svg width="11" height="11" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M11 4 4 11"/><path d="M11 8.5 8.5 11"/></svg></span>
|
|
1565
|
+
</div>
|
|
1552
1566
|
<div class="rk-ip-comp-bar">
|
|
1553
|
-
|
|
1554
|
-
<select class="rk-ip-comp-modeselect" data-ip-mode aria-label="What to create">
|
|
1555
|
-
<option value="image">Image</option>
|
|
1556
|
-
<option value="video">Video</option>
|
|
1557
|
-
</select>
|
|
1558
|
-
</span>
|
|
1567
|
+
|
|
1559
1568
|
<button type="button" class="rk-ip-gen-clip" id="rk-ip-genclip" aria-label="Attach reference images from your files" title="Attach reference images"><svg width="18" height="18" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><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 d="m10 8.8-4.1 4.1a1.6 1.6 0 0 0 2.3 2.3l5.6-5.6"/></svg></button>
|
|
1560
|
-
<p class="custom-inpaint-status
|
|
1569
|
+
<p class="rk-ip-comp-status custom-inpaint-status" data-ip-status data-custom-inpaint-status data-tone="" aria-live="polite"></p>
|
|
1561
1570
|
<button type="button" class="rk-ip-gen-send" id="rk-ip-gensend" aria-label="Send"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5"/><path d="m5 12 7-7 7 7"/></svg></button>
|
|
1562
1571
|
</div>
|
|
1563
1572
|
<p class="rk-ip-gen-status" id="rk-ip-genstatus" data-tone="" aria-live="polite" hidden></p>
|
|
@@ -1718,6 +1727,282 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
1718
1727
|
</aside>
|
|
1719
1728
|
</div>
|
|
1720
1729
|
<script>
|
|
1730
|
+
|
|
1731
|
+
(function(){
|
|
1732
|
+
var IMAGE_RE = /\.(png|jpe?g|gif|webp|avif)$/i;
|
|
1733
|
+
var SEND_ICON = "<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 19V5\"/><path d=\"m5 12 7-7 7 7\"/></svg>";
|
|
1734
|
+
var STOP_ICON = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\"><rect x=\"5\" y=\"5\" width=\"10\" height=\"10\" rx=\"2\"/></svg>";
|
|
1735
|
+
var MAX_ROWS = 10;
|
|
1736
|
+
|
|
1737
|
+
function isImageFile(f){ return /^image\//i.test((f && f.type) || '') || IMAGE_RE.test((f && f.name) || ''); }
|
|
1738
|
+
function imagesFromClipboard(e){
|
|
1739
|
+
var items = (e.clipboardData && e.clipboardData.items) || [], out = [];
|
|
1740
|
+
for(var i = 0; i < items.length; i++){
|
|
1741
|
+
if(items[i].kind === 'file'){ var f = items[i].getAsFile(); if(f && /^image\//i.test(f.type || '')) out.push(f); }
|
|
1742
|
+
}
|
|
1743
|
+
return out;
|
|
1744
|
+
}
|
|
1745
|
+
function dtHasFiles(e){
|
|
1746
|
+
var t = (e.dataTransfer && e.dataTransfer.types) || [];
|
|
1747
|
+
return Array.prototype.indexOf.call(t, 'Files') !== -1;
|
|
1748
|
+
}
|
|
1749
|
+
function plural(n, word){ return n + ' ' + word + (n > 1 ? 's' : ''); }
|
|
1750
|
+
|
|
1751
|
+
window.__rkChatComposer = function(cfg){
|
|
1752
|
+
cfg = cfg || {};
|
|
1753
|
+
var root = cfg.root, ta = cfg.textarea, refsEl = cfg.refsEl;
|
|
1754
|
+
var sendBtn = cfg.sendBtn, clipBtn = cfg.clipBtn;
|
|
1755
|
+
var uploadBlob = cfg.uploadBlob || function(){ return Promise.reject(new Error('Uploads are unavailable.')); };
|
|
1756
|
+
var status = cfg.onStatus || function(){};
|
|
1757
|
+
var canSendExtra = cfg.canSend || function(){ return false; };
|
|
1758
|
+
var refs = [];
|
|
1759
|
+
var busy = false;
|
|
1760
|
+
// Once the user drags the grip, their height wins: auto-grow stops fighting
|
|
1761
|
+
// them. Sending clears it so the next message starts compact again.
|
|
1762
|
+
var manualHeight = 0;
|
|
1763
|
+
|
|
1764
|
+
// ---- height ------------------------------------------------------------
|
|
1765
|
+
function maxHeight(){
|
|
1766
|
+
if(!ta) return 0;
|
|
1767
|
+
var s = window.getComputedStyle(ta);
|
|
1768
|
+
var line = parseFloat(s.lineHeight) || 20;
|
|
1769
|
+
var pad = (parseFloat(s.paddingTop) || 0) + (parseFloat(s.paddingBottom) || 0)
|
|
1770
|
+
+ (parseFloat(s.borderTopWidth) || 0) + (parseFloat(s.borderBottomWidth) || 0);
|
|
1771
|
+
return Math.round(line * MAX_ROWS + pad);
|
|
1772
|
+
}
|
|
1773
|
+
function applyHeight(px, cap){
|
|
1774
|
+
ta.style.height = px + 'px';
|
|
1775
|
+
ta.style.overflowY = ta.scrollHeight > px + 1 ? 'auto' : (cap ? 'auto' : 'hidden');
|
|
1776
|
+
}
|
|
1777
|
+
function autoGrow(){
|
|
1778
|
+
if(!ta) return;
|
|
1779
|
+
if(manualHeight){ applyHeight(manualHeight, true); return; }
|
|
1780
|
+
var max = maxHeight();
|
|
1781
|
+
ta.style.height = 'auto';
|
|
1782
|
+
applyHeight(Math.min(max, Math.max(56, ta.scrollHeight)), ta.scrollHeight > max);
|
|
1783
|
+
}
|
|
1784
|
+
function resetHeight(){ manualHeight = 0; if(ta){ ta.style.height = 'auto'; } autoGrow(); }
|
|
1785
|
+
|
|
1786
|
+
// Corner grip: pointer-drag the textarea taller/shorter. Bounded so it can
|
|
1787
|
+
// never collapse under one row or grow past the viewport.
|
|
1788
|
+
var field = ta && ta.parentElement;
|
|
1789
|
+
var grip = root && root.querySelector('[data-ip-grip]');
|
|
1790
|
+
if(grip && ta){
|
|
1791
|
+
grip.addEventListener('pointerdown', function(e){
|
|
1792
|
+
e.preventDefault();
|
|
1793
|
+
var startY = e.clientY, startH = ta.getBoundingClientRect().height;
|
|
1794
|
+
var maxPx = Math.max(120, Math.round(window.innerHeight * 0.6));
|
|
1795
|
+
try{ grip.setPointerCapture(e.pointerId); }catch(_){}
|
|
1796
|
+
if(field) field.classList.add('is-resizing');
|
|
1797
|
+
function move(ev){
|
|
1798
|
+
var next = Math.min(maxPx, Math.max(56, Math.round(startH + (ev.clientY - startY))));
|
|
1799
|
+
manualHeight = next;
|
|
1800
|
+
applyHeight(next, true);
|
|
1801
|
+
}
|
|
1802
|
+
function up(ev){
|
|
1803
|
+
try{ grip.releasePointerCapture(e.pointerId); }catch(_){}
|
|
1804
|
+
if(field) field.classList.remove('is-resizing');
|
|
1805
|
+
grip.removeEventListener('pointermove', move);
|
|
1806
|
+
grip.removeEventListener('pointerup', up);
|
|
1807
|
+
grip.removeEventListener('pointercancel', up);
|
|
1808
|
+
// A click with no travel resets to auto-grow — an escape hatch out of
|
|
1809
|
+
// a height you dragged and no longer want.
|
|
1810
|
+
if(Math.abs(ev.clientY - startY) < 3) resetHeight();
|
|
1811
|
+
}
|
|
1812
|
+
grip.addEventListener('pointermove', move);
|
|
1813
|
+
grip.addEventListener('pointerup', up);
|
|
1814
|
+
grip.addEventListener('pointercancel', up);
|
|
1815
|
+
});
|
|
1816
|
+
grip.addEventListener('dblclick', function(){ resetHeight(); });
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
// ---- send / stop button ------------------------------------------------
|
|
1820
|
+
function syncSend(){
|
|
1821
|
+
if(!sendBtn) return;
|
|
1822
|
+
if(busy){
|
|
1823
|
+
sendBtn.disabled = false;
|
|
1824
|
+
sendBtn.dataset.mode = 'stop';
|
|
1825
|
+
sendBtn.setAttribute('aria-label', 'Stop generating');
|
|
1826
|
+
sendBtn.setAttribute('title', 'Stop generating');
|
|
1827
|
+
sendBtn.innerHTML = STOP_ICON;
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
1830
|
+
sendBtn.dataset.mode = 'send';
|
|
1831
|
+
sendBtn.setAttribute('aria-label', 'Send');
|
|
1832
|
+
sendBtn.setAttribute('title', 'Send');
|
|
1833
|
+
sendBtn.disabled = !(Boolean((ta && ta.value || '').trim()) || canSendExtra());
|
|
1834
|
+
sendBtn.innerHTML = SEND_ICON;
|
|
1835
|
+
}
|
|
1836
|
+
function setBusy(v){
|
|
1837
|
+
busy = Boolean(v);
|
|
1838
|
+
if(root) root.classList.toggle('is-busy', busy);
|
|
1839
|
+
syncSend();
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
// ---- reference chips ---------------------------------------------------
|
|
1843
|
+
function renderRefs(){
|
|
1844
|
+
if(!refsEl) return;
|
|
1845
|
+
refsEl.hidden = refs.length === 0;
|
|
1846
|
+
refsEl.innerHTML = '';
|
|
1847
|
+
refs.forEach(function(url, idx){
|
|
1848
|
+
var chip = document.createElement('span'); chip.className = 'rk-ip-gen-ref';
|
|
1849
|
+
var im = document.createElement('img'); im.src = url; im.alt = ''; im.loading = 'lazy'; chip.appendChild(im);
|
|
1850
|
+
var lbl = document.createElement('span');
|
|
1851
|
+
var nm = url.split('/').pop() || 'reference';
|
|
1852
|
+
lbl.textContent = decodeURIComponent(nm).slice(0, 28);
|
|
1853
|
+
chip.appendChild(lbl);
|
|
1854
|
+
var x = document.createElement('button'); x.type = 'button';
|
|
1855
|
+
x.setAttribute('aria-label', 'Remove reference'); x.innerHTML = '×';
|
|
1856
|
+
x.addEventListener('click', function(){ refs.splice(idx, 1); renderRefs(); });
|
|
1857
|
+
chip.appendChild(x);
|
|
1858
|
+
refsEl.appendChild(chip);
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
// Paste or drop image files straight into the composer -> temp upload -> ref.
|
|
1863
|
+
function addFiles(fileList){
|
|
1864
|
+
var files = Array.prototype.slice.call(fileList || []).filter(isImageFile);
|
|
1865
|
+
if(!files.length) return Promise.resolve(0);
|
|
1866
|
+
status('Uploading ' + plural(files.length, 'reference') + '\u2026', '');
|
|
1867
|
+
var added = 0;
|
|
1868
|
+
return files.reduce(function(chain, f){
|
|
1869
|
+
return chain.then(function(){
|
|
1870
|
+
return uploadBlob(f, f.name || 'pasted-image.png', f.type || 'image/png').then(function(url){
|
|
1871
|
+
if(url && refs.indexOf(url) === -1){ refs.push(url); added++; renderRefs(); }
|
|
1872
|
+
});
|
|
1873
|
+
});
|
|
1874
|
+
}, Promise.resolve()).then(function(){
|
|
1875
|
+
if(added) status(plural(added, 'reference') + ' attached.', 'success');
|
|
1876
|
+
return added;
|
|
1877
|
+
}).catch(function(err){
|
|
1878
|
+
status((err && err.message) ? err.message : 'Upload failed.', 'error');
|
|
1879
|
+
return added;
|
|
1880
|
+
});
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
// ---- attach pop panel (shared file-directory explorer) -----------------
|
|
1884
|
+
var modal = null;
|
|
1885
|
+
function onEsc(e){ if(e.key === 'Escape') closePanel(); }
|
|
1886
|
+
function closePanel(){
|
|
1887
|
+
if(!modal) return;
|
|
1888
|
+
try{ modal._handle && modal._handle.destroy && modal._handle.destroy(); }catch(_){}
|
|
1889
|
+
modal.remove(); modal = null;
|
|
1890
|
+
document.removeEventListener('keydown', onEsc, true);
|
|
1891
|
+
}
|
|
1892
|
+
function openPanel(){
|
|
1893
|
+
if(modal) return;
|
|
1894
|
+
var overlay = document.createElement('div'); overlay.className = 'rk-ip-filemodal';
|
|
1895
|
+
var panel = document.createElement('div'); panel.className = 'rk-ip-filemodal-panel';
|
|
1896
|
+
var head = document.createElement('div'); head.className = 'rk-ip-filemodal-head';
|
|
1897
|
+
var title = document.createElement('span'); title.className = 'rk-ip-filemodal-title';
|
|
1898
|
+
title.textContent = cfg.attachTitle || 'Attach reference images';
|
|
1899
|
+
var actions = document.createElement('div'); actions.className = 'rk-ip-filemodal-actions';
|
|
1900
|
+
var uploadBtn = document.createElement('button'); uploadBtn.type = 'button';
|
|
1901
|
+
uploadBtn.className = 'rk-ip-filemodal-upload'; uploadBtn.textContent = '\u2191 Upload from computer';
|
|
1902
|
+
var uploadInput = document.createElement('input'); uploadInput.type = 'file';
|
|
1903
|
+
uploadInput.accept = 'image/png,image/jpeg,image/webp,image/gif,image/avif';
|
|
1904
|
+
uploadInput.multiple = true; uploadInput.hidden = true;
|
|
1905
|
+
var close = document.createElement('button'); close.type = 'button';
|
|
1906
|
+
close.className = 'rk-ip-filemodal-close'; close.setAttribute('aria-label', 'Close'); close.innerHTML = '×';
|
|
1907
|
+
actions.appendChild(uploadBtn); actions.appendChild(close);
|
|
1908
|
+
head.appendChild(title); head.appendChild(actions); head.appendChild(uploadInput);
|
|
1909
|
+
uploadBtn.addEventListener('click', function(){ if(!uploadBtn.disabled) uploadInput.click(); });
|
|
1910
|
+
uploadInput.addEventListener('change', function(){
|
|
1911
|
+
var files = Array.prototype.slice.call(uploadInput.files || []).filter(isImageFile);
|
|
1912
|
+
uploadInput.value = '';
|
|
1913
|
+
if(!files.length){ status('Pick an image file to upload as a reference.', 'error'); return; }
|
|
1914
|
+
var original = uploadBtn.textContent;
|
|
1915
|
+
uploadBtn.disabled = true; uploadBtn.textContent = 'Uploading\u2026';
|
|
1916
|
+
addFiles(files).then(function(added){
|
|
1917
|
+
uploadBtn.disabled = false; uploadBtn.textContent = original;
|
|
1918
|
+
if(added) closePanel();
|
|
1919
|
+
else status('Those files were already attached.', '');
|
|
1920
|
+
});
|
|
1921
|
+
});
|
|
1922
|
+
var mountEl = document.createElement('div'); mountEl.className = 'rk-ip-filemodal-mount';
|
|
1923
|
+
panel.appendChild(head); panel.appendChild(mountEl); overlay.appendChild(panel);
|
|
1924
|
+
document.body.appendChild(overlay); modal = overlay;
|
|
1925
|
+
close.addEventListener('click', closePanel);
|
|
1926
|
+
overlay.addEventListener('click', function(e){ if(e.target === overlay) closePanel(); });
|
|
1927
|
+
document.addEventListener('keydown', onEsc, true);
|
|
1928
|
+
if(window.__vidfarmDirectory && window.__vidfarmDirectory.mount){
|
|
1929
|
+
modal._handle = window.__vidfarmDirectory.mount(mountEl, {
|
|
1930
|
+
mode: 'attach', showMultiSelect: true, primaryClick: 'attach',
|
|
1931
|
+
onAttach: function(items){
|
|
1932
|
+
var added = 0;
|
|
1933
|
+
(items || []).forEach(function(it){
|
|
1934
|
+
var ct = it.contentType || '';
|
|
1935
|
+
var isImg = ct.indexOf('image/') === 0 || /\.(png|jpe?g|gif|webp|svg|avif)$/i.test(it.name || '');
|
|
1936
|
+
if(it.viewUrl && isImg && refs.indexOf(it.viewUrl) === -1){ refs.push(it.viewUrl); added++; }
|
|
1937
|
+
});
|
|
1938
|
+
renderRefs();
|
|
1939
|
+
if(added){ status(plural(added, 'reference') + ' attached.', 'success'); closePanel(); }
|
|
1940
|
+
else status('Pick an image file to use as a reference.', 'error');
|
|
1941
|
+
}
|
|
1942
|
+
});
|
|
1943
|
+
} else {
|
|
1944
|
+
mountEl.innerHTML = '<p style="padding:16px;color:var(--rk-text-muted);font-size:13px">File browser is unavailable right now.</p>';
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
// ---- wiring ------------------------------------------------------------
|
|
1949
|
+
if(ta){
|
|
1950
|
+
ta.addEventListener('input', function(){ autoGrow(); syncSend(); });
|
|
1951
|
+
ta.addEventListener('keydown', function(e){
|
|
1952
|
+
if(e.key !== 'Enter' || e.shiftKey) return;
|
|
1953
|
+
e.preventDefault();
|
|
1954
|
+
// Enter is ignored mid-turn so an impatient double-send cannot queue a
|
|
1955
|
+
// second job; the draft stays put.
|
|
1956
|
+
if(busy) return;
|
|
1957
|
+
if(cfg.onSend) cfg.onSend();
|
|
1958
|
+
});
|
|
1959
|
+
ta.addEventListener('paste', function(e){
|
|
1960
|
+
var imgs = imagesFromClipboard(e);
|
|
1961
|
+
if(imgs.length){ e.preventDefault(); addFiles(imgs); }
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1964
|
+
if(clipBtn) clipBtn.addEventListener('click', openPanel);
|
|
1965
|
+
if(sendBtn) sendBtn.addEventListener('click', function(){
|
|
1966
|
+
if(busy){ if(cfg.onStop) cfg.onStop(); return; }
|
|
1967
|
+
if(cfg.onSend) cfg.onSend();
|
|
1968
|
+
});
|
|
1969
|
+
if(root){
|
|
1970
|
+
['dragenter', 'dragover'].forEach(function(ev){
|
|
1971
|
+
root.addEventListener(ev, function(e){
|
|
1972
|
+
if(dtHasFiles(e)){ e.preventDefault(); e.stopPropagation(); root.classList.add('is-dragref'); }
|
|
1973
|
+
});
|
|
1974
|
+
});
|
|
1975
|
+
['dragleave', 'dragend'].forEach(function(ev){
|
|
1976
|
+
root.addEventListener(ev, function(){ root.classList.remove('is-dragref'); });
|
|
1977
|
+
});
|
|
1978
|
+
root.addEventListener('drop', function(e){
|
|
1979
|
+
if(!dtHasFiles(e)) return;
|
|
1980
|
+
e.preventDefault(); e.stopPropagation();
|
|
1981
|
+
root.classList.remove('is-dragref');
|
|
1982
|
+
addFiles(e.dataTransfer.files);
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
autoGrow();
|
|
1987
|
+
syncSend();
|
|
1988
|
+
|
|
1989
|
+
return {
|
|
1990
|
+
refs: function(){ return refs.slice(); },
|
|
1991
|
+
setRefs: function(list){ refs = (list || []).slice(); renderRefs(); },
|
|
1992
|
+
clearRefs: function(){ refs = []; renderRefs(); },
|
|
1993
|
+
addFiles: addFiles,
|
|
1994
|
+
renderRefs: renderRefs,
|
|
1995
|
+
autoGrow: autoGrow,
|
|
1996
|
+
resetHeight: resetHeight,
|
|
1997
|
+
setBusy: setBusy,
|
|
1998
|
+
isBusy: function(){ return busy; },
|
|
1999
|
+
syncSend: syncSend,
|
|
2000
|
+
closePanel: closePanel,
|
|
2001
|
+
setPlaceholder: function(s){ if(ta) ta.placeholder = s; }
|
|
2002
|
+
};
|
|
2003
|
+
};
|
|
2004
|
+
})();
|
|
2005
|
+
|
|
1721
2006
|
(function(){
|
|
1722
2007
|
const uploadButton = document.querySelector("[data-custom-inpaint-upload]");
|
|
1723
2008
|
const startNewButton = document.querySelector("[data-custom-inpaint-start-new]");
|
|
@@ -1771,7 +2056,6 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
1771
2056
|
const chatFeed = document.querySelector("[data-ip-feed]");
|
|
1772
2057
|
const canvasHolder = document.querySelector("[data-ip-canvas-holder]");
|
|
1773
2058
|
const composerEl = document.querySelector("[data-ip-composer]");
|
|
1774
|
-
const modeSelect = document.querySelector("[data-ip-mode]");
|
|
1775
2059
|
const maskBlock = document.querySelector("[data-ip-mask-block]");
|
|
1776
2060
|
const maskChip = document.querySelector("[data-ip-comp-mask]");
|
|
1777
2061
|
const paintStack = document.querySelector("[data-ip-paint-stack]");
|
|
@@ -1818,7 +2102,6 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
1818
2102
|
|| !(chatFeed instanceof HTMLElement)
|
|
1819
2103
|
|| !(canvasHolder instanceof HTMLElement)
|
|
1820
2104
|
|| !(composerEl instanceof HTMLElement)
|
|
1821
|
-
|| !(modeSelect instanceof HTMLSelectElement)
|
|
1822
2105
|
|| !(maskBlock instanceof HTMLElement)
|
|
1823
2106
|
|| !(maskChip instanceof HTMLElement)
|
|
1824
2107
|
|| !(paintStack instanceof HTMLElement)) {
|
|
@@ -3074,7 +3357,7 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
3074
3357
|
return;
|
|
3075
3358
|
}
|
|
3076
3359
|
// Never stomp on a turn that is mid-flight locally.
|
|
3077
|
-
if (
|
|
3360
|
+
if (isGenBusy() || activeTurnId) { syncSessionAssets(); return; }
|
|
3078
3361
|
historyRefresh.disabled = true;
|
|
3079
3362
|
try {
|
|
3080
3363
|
const endpoint = new URL("/job-runs/history", window.location.origin);
|
|
@@ -3711,41 +3994,36 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
3711
3994
|
var genSend = document.getElementById('rk-ip-gensend');
|
|
3712
3995
|
var genRefsEl = document.getElementById('rk-ip-genrefs');
|
|
3713
3996
|
var genStatusEl = document.getElementById('rk-ip-genstatus');
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
var
|
|
3997
|
+
// The composer itself — auto-grow, the drag-to-resize grip, reference chips,
|
|
3998
|
+
// paste/drop, and the attach pop panel — is the shared implementation from
|
|
3999
|
+
// chat-composer.ts, byte-identical to the one on /tools/video.
|
|
4000
|
+
var composer = window.__rkChatComposer({
|
|
4001
|
+
root: composerEl,
|
|
4002
|
+
textarea: genPrompt,
|
|
4003
|
+
refsEl: genRefsEl,
|
|
4004
|
+
sendBtn: genSend,
|
|
4005
|
+
clipBtn: genClip,
|
|
4006
|
+
attachTitle: "Attach reference images",
|
|
4007
|
+
uploadBlob: function(f, name, type){ return uploadTemporaryBlob(f, name, type); },
|
|
4008
|
+
onStatus: function(m, t){ setStatus(m, t); },
|
|
4009
|
+
canSend: function(){ return maskHasInstruction(); },
|
|
4010
|
+
onSend: function(){ sendChatMessage(); },
|
|
4011
|
+
onStop: function(){ stopActiveTurn(); }
|
|
4012
|
+
});
|
|
4013
|
+
// Aliases so the rest of the page keeps its original call sites.
|
|
4014
|
+
function isGenBusy(){ return Boolean(composer && composer.isBusy()); }
|
|
4015
|
+
function renderGenRefs(){ composer.renderRefs(); }
|
|
4016
|
+
function autoGrowComposer(){ composer.autoGrow(); }
|
|
4017
|
+
function addGenReferenceFiles(fl){ return composer.addFiles(fl); }
|
|
4018
|
+
function closeGenFilePanel(){ composer.closePanel(); }
|
|
4019
|
+
function syncGenSend(){ composer.syncSend(); }
|
|
4020
|
+
function setGenBusy(v){ composer.setBusy(v); }
|
|
3718
4021
|
|
|
3719
4022
|
// One status line for the whole surface — the composer's.
|
|
3720
4023
|
function setGenStatus(m, t){ setStatus(m, t); }
|
|
3721
4024
|
function maskHasInstruction(){
|
|
3722
4025
|
return inpaintStrokes.length > 0 && inpaintMaskColors.some(function(entry){ return String(entry.prompt || '').trim(); });
|
|
3723
4026
|
}
|
|
3724
|
-
function canGenNew(){ return !genBusy && (Boolean((genPrompt && genPrompt.value || '').trim()) || maskHasInstruction()); }
|
|
3725
|
-
// While a turn is in flight the composer stays fully usable — you can keep
|
|
3726
|
-
// typing the next message, attach references, and scroll the history. The send
|
|
3727
|
-
// button turns into Stop, which cancels the running job.
|
|
3728
|
-
function syncGenSend(){
|
|
3729
|
-
if(!genSend) return;
|
|
3730
|
-
if(genBusy){
|
|
3731
|
-
genSend.disabled = false;
|
|
3732
|
-
genSend.dataset.mode = 'stop';
|
|
3733
|
-
genSend.setAttribute('aria-label', 'Stop generating');
|
|
3734
|
-
genSend.setAttribute('title', 'Stop generating');
|
|
3735
|
-
genSend.innerHTML = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\"><rect x=\"5\" y=\"5\" width=\"10\" height=\"10\" rx=\"2\"/></svg>";
|
|
3736
|
-
return;
|
|
3737
|
-
}
|
|
3738
|
-
genSend.dataset.mode = 'send';
|
|
3739
|
-
genSend.setAttribute('aria-label', 'Send');
|
|
3740
|
-
genSend.setAttribute('title', 'Send');
|
|
3741
|
-
genSend.disabled = !canGenNew();
|
|
3742
|
-
genSend.innerHTML = GEN_SEND_HTML;
|
|
3743
|
-
}
|
|
3744
|
-
function setGenBusy(value){
|
|
3745
|
-
genBusy = Boolean(value);
|
|
3746
|
-
composerEl.classList.toggle('is-busy', genBusy);
|
|
3747
|
-
syncGenSend();
|
|
3748
|
-
}
|
|
3749
4027
|
|
|
3750
4028
|
// ---- stop --------------------------------------------------------------
|
|
3751
4029
|
// Stop aborts the client poll AND best-effort cancels the job server-side so a
|
|
@@ -3755,7 +4033,7 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
3755
4033
|
function stopError(){ var e = new Error('__vidfarm_stopped__'); e.stopped = true; return e; }
|
|
3756
4034
|
function throwIfStopped(){ if(stopRequested) throw stopError(); }
|
|
3757
4035
|
function stopActiveTurn(){
|
|
3758
|
-
if(!
|
|
4036
|
+
if(!isGenBusy() || stopRequested) return;
|
|
3759
4037
|
stopRequested = true;
|
|
3760
4038
|
setStatus('Stopping…', '');
|
|
3761
4039
|
if(activeTurnId) updatePendingLabel(activeTurnId, 'Stopping…');
|
|
@@ -3767,129 +4045,11 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
3767
4045
|
}
|
|
3768
4046
|
function composerPlaceholder(){
|
|
3769
4047
|
if(!genPrompt) return;
|
|
3770
|
-
genPrompt.placeholder =
|
|
3771
|
-
? (
|
|
3772
|
-
:
|
|
3773
|
-
}
|
|
3774
|
-
// Grows with the message up to 10 rows, then scrolls inside itself.
|
|
3775
|
-
var COMPOSER_MAX_ROWS = 10;
|
|
3776
|
-
function composerMaxHeight(){
|
|
3777
|
-
if(!genPrompt) return 0;
|
|
3778
|
-
var styles = window.getComputedStyle(genPrompt);
|
|
3779
|
-
var line = parseFloat(styles.lineHeight) || 20;
|
|
3780
|
-
var pad = (parseFloat(styles.paddingTop) || 0) + (parseFloat(styles.paddingBottom) || 0)
|
|
3781
|
-
+ (parseFloat(styles.borderTopWidth) || 0) + (parseFloat(styles.borderBottomWidth) || 0);
|
|
3782
|
-
return Math.round(line * COMPOSER_MAX_ROWS + pad);
|
|
3783
|
-
}
|
|
3784
|
-
function autoGrowComposer(){
|
|
3785
|
-
if(!genPrompt) return;
|
|
3786
|
-
var max = composerMaxHeight();
|
|
3787
|
-
genPrompt.style.height = 'auto';
|
|
3788
|
-
genPrompt.style.height = Math.min(max, Math.max(56, genPrompt.scrollHeight)) + 'px';
|
|
3789
|
-
genPrompt.style.overflowY = genPrompt.scrollHeight > max ? 'auto' : 'hidden';
|
|
3790
|
-
}
|
|
3791
|
-
function renderGenRefs(){
|
|
3792
|
-
if(!genRefsEl) return;
|
|
3793
|
-
genRefsEl.hidden = genRefs.length === 0;
|
|
3794
|
-
genRefsEl.innerHTML = '';
|
|
3795
|
-
genRefs.forEach(function(url, idx){
|
|
3796
|
-
var chip = document.createElement('span'); chip.className = 'rk-ip-gen-ref';
|
|
3797
|
-
var im = document.createElement('img'); im.src = url; im.alt = ''; im.loading = 'lazy'; chip.appendChild(im);
|
|
3798
|
-
var lbl = document.createElement('span'); var nm = url.split('/').pop() || 'reference'; lbl.textContent = decodeURIComponent(nm).slice(0, 28); chip.appendChild(lbl);
|
|
3799
|
-
var x = document.createElement('button'); x.type = 'button'; x.setAttribute('aria-label', 'Remove reference'); x.innerHTML = '×';
|
|
3800
|
-
x.addEventListener('click', function(){ genRefs.splice(idx, 1); renderGenRefs(); }); chip.appendChild(x);
|
|
3801
|
-
genRefsEl.appendChild(chip);
|
|
3802
|
-
});
|
|
4048
|
+
genPrompt.placeholder = sourceImageUrl
|
|
4049
|
+
? (inpaintStrokes.length ? 'Describe the masked edit (whole-image guidance)\u2026' : 'Describe the next edit to the newest image\u2026')
|
|
4050
|
+
: 'Describe the image you want\u2026';
|
|
3803
4051
|
}
|
|
3804
4052
|
|
|
3805
|
-
// Paperclip -> shared file-directory explorer (pop panel) to attach image refs.
|
|
3806
|
-
var genFileModal = null;
|
|
3807
|
-
function closeGenFilePanel(){ if(genFileModal){ try{ genFileModal._handle && genFileModal._handle.destroy && genFileModal._handle.destroy(); }catch(_){} genFileModal.remove(); genFileModal = null; document.removeEventListener('keydown', onGenFileEsc, true); } }
|
|
3808
|
-
function onGenFileEsc(e){ if(e.key === 'Escape') closeGenFilePanel(); }
|
|
3809
|
-
function openGenFilePanel(){
|
|
3810
|
-
if(genFileModal) return;
|
|
3811
|
-
var overlay = document.createElement('div'); overlay.className = 'rk-ip-filemodal';
|
|
3812
|
-
var panel = document.createElement('div'); panel.className = 'rk-ip-filemodal-panel';
|
|
3813
|
-
var head = document.createElement('div'); head.className = 'rk-ip-filemodal-head';
|
|
3814
|
-
var title = document.createElement('span'); title.className = 'rk-ip-filemodal-title'; title.textContent = 'Attach reference images';
|
|
3815
|
-
var headActions = document.createElement('div'); headActions.className = 'rk-ip-filemodal-actions';
|
|
3816
|
-
var uploadBtn = document.createElement('button'); uploadBtn.type = 'button'; uploadBtn.className = 'rk-ip-filemodal-upload'; uploadBtn.textContent = '\u2191 Upload from computer';
|
|
3817
|
-
var uploadInput = document.createElement('input'); uploadInput.type = 'file'; uploadInput.accept = 'image/png,image/jpeg,image/webp,image/gif,image/avif'; uploadInput.multiple = true; uploadInput.hidden = true;
|
|
3818
|
-
var close = document.createElement('button'); close.type = 'button'; close.className = 'rk-ip-filemodal-close'; close.setAttribute('aria-label', 'Close'); close.innerHTML = '×';
|
|
3819
|
-
headActions.appendChild(uploadBtn); headActions.appendChild(close);
|
|
3820
|
-
head.appendChild(title); head.appendChild(headActions); head.appendChild(uploadInput);
|
|
3821
|
-
uploadBtn.addEventListener('click', function(){ if(!uploadBtn.disabled) uploadInput.click(); });
|
|
3822
|
-
uploadInput.addEventListener('change', function(){
|
|
3823
|
-
var files = Array.prototype.slice.call(uploadInput.files || []).filter(function(f){ return /^image\//i.test(f.type || '') || /\.(png|jpe?g|gif|webp|avif)$/i.test(f.name || ''); });
|
|
3824
|
-
uploadInput.value = '';
|
|
3825
|
-
if(!files.length){ setGenStatus('Pick an image file to upload as a reference.', 'error'); return; }
|
|
3826
|
-
uploadBtn.disabled = true; var original = uploadBtn.textContent; uploadBtn.textContent = 'Uploading\u2026';
|
|
3827
|
-
setGenStatus('Uploading ' + files.length + ' reference' + (files.length > 1 ? 's' : '') + '\u2026', '');
|
|
3828
|
-
var added = 0;
|
|
3829
|
-
files.reduce(function(chain, f){
|
|
3830
|
-
return chain.then(function(){
|
|
3831
|
-
return uploadTemporaryBlob(f, f.name || 'reference.png', f.type || 'image/png').then(function(url){
|
|
3832
|
-
if(url && genRefs.indexOf(url) === -1){ genRefs.push(url); added++; renderGenRefs(); }
|
|
3833
|
-
});
|
|
3834
|
-
});
|
|
3835
|
-
}, Promise.resolve()).then(function(){
|
|
3836
|
-
uploadBtn.disabled = false; uploadBtn.textContent = original;
|
|
3837
|
-
if(added){ setGenStatus(added + ' reference' + (added > 1 ? 's' : '') + ' uploaded.', 'success'); closeGenFilePanel(); }
|
|
3838
|
-
else setGenStatus('Those files were already attached.', '');
|
|
3839
|
-
}).catch(function(err){
|
|
3840
|
-
uploadBtn.disabled = false; uploadBtn.textContent = original;
|
|
3841
|
-
setGenStatus((err && err.message) ? err.message : 'Upload failed.', 'error');
|
|
3842
|
-
});
|
|
3843
|
-
});
|
|
3844
|
-
var mountEl = document.createElement('div'); mountEl.className = 'rk-ip-filemodal-mount';
|
|
3845
|
-
panel.appendChild(head); panel.appendChild(mountEl); overlay.appendChild(panel);
|
|
3846
|
-
document.body.appendChild(overlay); genFileModal = overlay;
|
|
3847
|
-
close.addEventListener('click', closeGenFilePanel);
|
|
3848
|
-
overlay.addEventListener('click', function(e){ if(e.target === overlay) closeGenFilePanel(); });
|
|
3849
|
-
document.addEventListener('keydown', onGenFileEsc, true);
|
|
3850
|
-
if(window.__vidfarmDirectory && window.__vidfarmDirectory.mount){
|
|
3851
|
-
genFileModal._handle = window.__vidfarmDirectory.mount(mountEl, {
|
|
3852
|
-
mode: 'attach', showMultiSelect: true, primaryClick: 'attach',
|
|
3853
|
-
onAttach: function(items){
|
|
3854
|
-
var added = 0;
|
|
3855
|
-
(items || []).forEach(function(it){
|
|
3856
|
-
var ct = (it.contentType || ''); var isImg = ct.indexOf('image/') === 0 || /\.(png|jpe?g|gif|webp|svg|avif)$/i.test(it.name || '');
|
|
3857
|
-
if(it.viewUrl && isImg && genRefs.indexOf(it.viewUrl) === -1){ genRefs.push(it.viewUrl); added++; }
|
|
3858
|
-
});
|
|
3859
|
-
renderGenRefs();
|
|
3860
|
-
if(added){ setGenStatus(added + ' reference' + (added > 1 ? 's' : '') + ' attached.', 'success'); closeGenFilePanel(); }
|
|
3861
|
-
else setGenStatus('Pick an image file to use as a reference.', 'error');
|
|
3862
|
-
}
|
|
3863
|
-
});
|
|
3864
|
-
} else {
|
|
3865
|
-
mountEl.innerHTML = '<p style="padding:16px;color:var(--rk-text-muted);font-size:13px">File browser is unavailable right now.</p>';
|
|
3866
|
-
}
|
|
3867
|
-
}
|
|
3868
|
-
|
|
3869
|
-
// Paste or drop image files straight into the composer -> temp upload -> reference.
|
|
3870
|
-
function addGenReferenceFiles(fileList){
|
|
3871
|
-
var files = Array.prototype.slice.call(fileList || []).filter(function(f){ return /^image\//i.test(f.type || '') || /\.(png|jpe?g|gif|webp|avif)$/i.test(f.name || ''); });
|
|
3872
|
-
if(!files.length) return Promise.resolve(0);
|
|
3873
|
-
setGenStatus('Uploading ' + files.length + ' reference' + (files.length > 1 ? 's' : '') + '\u2026', '');
|
|
3874
|
-
var added = 0;
|
|
3875
|
-
return files.reduce(function(chain, f){
|
|
3876
|
-
return chain.then(function(){
|
|
3877
|
-
return uploadTemporaryBlob(f, f.name || 'pasted-image.png', f.type || 'image/png').then(function(url){
|
|
3878
|
-
if(url && genRefs.indexOf(url) === -1){ genRefs.push(url); added++; renderGenRefs(); }
|
|
3879
|
-
});
|
|
3880
|
-
});
|
|
3881
|
-
}, Promise.resolve()).then(function(){
|
|
3882
|
-
if(added) setGenStatus(added + ' reference' + (added > 1 ? 's' : '') + ' attached.', 'success');
|
|
3883
|
-
return added;
|
|
3884
|
-
}).catch(function(err){ setGenStatus((err && err.message) ? err.message : 'Upload failed.', 'error'); return added; });
|
|
3885
|
-
}
|
|
3886
|
-
function imagesFromClipboard(e){
|
|
3887
|
-
var items = (e.clipboardData && e.clipboardData.items) || []; var out = [];
|
|
3888
|
-
for(var i = 0; i < items.length; i++){ if(items[i].kind === 'file'){ var f = items[i].getAsFile(); if(f && /^image\//i.test(f.type || '')) out.push(f); } }
|
|
3889
|
-
return out;
|
|
3890
|
-
}
|
|
3891
|
-
function dtHasFiles(e){ var t = (e.dataTransfer && e.dataTransfer.types) || []; return Array.prototype.indexOf.call(t, 'Files') !== -1; }
|
|
3892
|
-
|
|
3893
4053
|
// "character card" / "storyboard" shorthand — spell out the full layout
|
|
3894
4054
|
// so the image model produces a reference sheet / numbered shot board
|
|
3895
4055
|
// instead of a single picture. Snippets are shared with the AI chat +
|
|
@@ -4235,61 +4395,31 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
4235
4395
|
});
|
|
4236
4396
|
}
|
|
4237
4397
|
|
|
4238
|
-
function runVideoTurn(text){
|
|
4239
|
-
return Promise.resolve().then(function(){
|
|
4240
|
-
var pending = addTurn({ kind: 'pending', label: 'Generating video \u2014 this can take a few minutes\u2026', prompt: text });
|
|
4241
|
-
activeTurnId = pending.id;
|
|
4242
|
-
var payload = { prompt: text, output_format: 'mp4' };
|
|
4243
|
-
// The newest image is the only visual reference — it is the opening frame.
|
|
4244
|
-
if(sourceImageUrl) payload.input_references = [sourceImageUrl];
|
|
4245
|
-
if(inpaintProviderSelect.value) payload.provider = inpaintProviderSelect.value;
|
|
4246
|
-
var tracer = tracerAutoPending ? nameTracerFromSeed(text) : getTracerValue();
|
|
4247
|
-
return fetch('/api/v1/primitives/videos/generate', {
|
|
4248
|
-
method: 'POST', credentials: 'same-origin', headers: { 'content-type': 'application/json' },
|
|
4249
|
-
body: JSON.stringify({ tracer: tracer, payload: payload, chat_message: text, chat_mode: 'video' })
|
|
4250
|
-
}).then(parseJsonResponse).then(function(j){
|
|
4251
|
-
if(!j || !j.job_id) throw new Error((j && j.error) || 'Unable to start video generation.');
|
|
4252
|
-
activeJob = { kind: 'video_generate', jobId: j.job_id };
|
|
4253
|
-
return pollPrimitiveJob('video_generate', j.job_id, 'Generating video', true).then(function(out){
|
|
4254
|
-
if(!out){ updatePendingLabel(activeTurnId, 'Still rendering \u2014 check the Video tool\u2019s history shortly.'); activeTurnId = ''; return; }
|
|
4255
|
-
resolveActiveTurn({ kind: 'media', mediaKind: 'video', url: out, jobId: j.job_id, note: 'video' });
|
|
4256
|
-
setStatus('Video ready.', 'success');
|
|
4257
|
-
void loadTracerSessions();
|
|
4258
|
-
});
|
|
4259
|
-
});
|
|
4260
|
-
});
|
|
4261
|
-
}
|
|
4262
|
-
|
|
4263
4398
|
// ---- send ----------------------------------------------------------------
|
|
4264
4399
|
function sendChatMessage(){
|
|
4265
|
-
if(
|
|
4266
|
-
var
|
|
4267
|
-
var
|
|
4268
|
-
var text = mode === 'image' ? expandShorthand(raw) : raw;
|
|
4269
|
-
var masked = mode === 'image' && inpaintStrokes.length > 0;
|
|
4400
|
+
if(isGenBusy()) return;
|
|
4401
|
+
var text = expandShorthand((genPrompt && genPrompt.value || '').trim());
|
|
4402
|
+
var masked = inpaintStrokes.length > 0;
|
|
4270
4403
|
if(!text && !(masked && maskHasInstruction())){
|
|
4271
|
-
setStatus(
|
|
4404
|
+
setStatus('Type what you want to create or change.', 'error');
|
|
4272
4405
|
if(genPrompt) genPrompt.focus();
|
|
4273
4406
|
return;
|
|
4274
4407
|
}
|
|
4275
|
-
|
|
4276
|
-
var refs = genRefs.slice();
|
|
4408
|
+
var refs = composer.refs();
|
|
4277
4409
|
addTurn({
|
|
4278
4410
|
role: 'user', kind: 'text',
|
|
4279
4411
|
text: text || 'Apply the painted mask instructions.',
|
|
4280
|
-
refs: refs, mode:
|
|
4281
|
-
isEdit:
|
|
4412
|
+
refs: refs, mode: 'image', masked: masked,
|
|
4413
|
+
isEdit: Boolean(sourceImageUrl)
|
|
4282
4414
|
});
|
|
4283
|
-
if(genPrompt){ genPrompt.value = '';
|
|
4284
|
-
|
|
4285
|
-
renderGenRefs();
|
|
4415
|
+
if(genPrompt){ genPrompt.value = ''; composer.resetHeight(); }
|
|
4416
|
+
composer.clearRefs();
|
|
4286
4417
|
stopRequested = false;
|
|
4287
4418
|
activeJob = { kind: '', jobId: '' };
|
|
4288
4419
|
setGenBusy(true);
|
|
4289
|
-
setStatus(
|
|
4420
|
+
setStatus('Working\u2026', '');
|
|
4290
4421
|
var work;
|
|
4291
|
-
if(
|
|
4292
|
-
else if(!sourceImageUrl) work = runGenerateTurn(text, refs);
|
|
4422
|
+
if(!sourceImageUrl) work = runGenerateTurn(text, refs);
|
|
4293
4423
|
else if(masked) work = runMaskedTurn(text, refs);
|
|
4294
4424
|
else work = runEditTurn(text, refs);
|
|
4295
4425
|
work.catch(function(err){
|
|
@@ -4312,25 +4442,8 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
4312
4442
|
});
|
|
4313
4443
|
}
|
|
4314
4444
|
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
genPrompt.addEventListener('keydown', function(e){
|
|
4318
|
-
if(e.key !== 'Enter' || e.shiftKey) return;
|
|
4319
|
-
e.preventDefault();
|
|
4320
|
-
// Keep typing freely while a turn runs; Enter just won't fire a second one.
|
|
4321
|
-
if(genBusy){ setStatus('Still generating — press Stop to cancel, or wait and send then.', ''); return; }
|
|
4322
|
-
sendChatMessage();
|
|
4323
|
-
});
|
|
4324
|
-
genPrompt.addEventListener('paste', function(e){ var imgs = imagesFromClipboard(e); if(imgs.length){ e.preventDefault(); addGenReferenceFiles(imgs); } });
|
|
4325
|
-
}
|
|
4326
|
-
if(genWrap){
|
|
4327
|
-
['dragenter', 'dragover'].forEach(function(ev){ genWrap.addEventListener(ev, function(e){ if(dtHasFiles(e)){ e.preventDefault(); e.stopPropagation(); genWrap.classList.add('is-dragref'); } }); });
|
|
4328
|
-
['dragleave', 'dragend'].forEach(function(ev){ genWrap.addEventListener(ev, function(){ genWrap.classList.remove('is-dragref'); }); });
|
|
4329
|
-
genWrap.addEventListener('drop', function(e){ if(dtHasFiles(e)){ e.preventDefault(); e.stopPropagation(); genWrap.classList.remove('is-dragref'); addGenReferenceFiles(e.dataTransfer.files); } });
|
|
4330
|
-
}
|
|
4331
|
-
if(genClip) genClip.addEventListener('click', openGenFilePanel);
|
|
4332
|
-
if(genSend) genSend.addEventListener('click', function(){ if(genBusy) stopActiveTurn(); else sendChatMessage(); });
|
|
4333
|
-
modeSelect.addEventListener('change', function(){ composerPlaceholder(); syncGenSend(); });
|
|
4445
|
+
// input / Enter-to-send / paste / drag-drop / paperclip / send-stop are all
|
|
4446
|
+
// wired by the shared composer runtime above.
|
|
4334
4447
|
// ⌘Z / ⇧⌘Z anywhere outside a text field undoes and redoes the paint.
|
|
4335
4448
|
window.addEventListener('keydown', function(e){
|
|
4336
4449
|
if(!(e.metaKey || e.ctrlKey) || String(e.key).toLowerCase() !== 'z') return;
|