@officexapp/vidfarm-devcli 0.21.26 → 0.21.27
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/vidfarm/SKILL.md +26 -6
- package/.agents/skills/vidfarm/references/assets-and-sourcing.md +26 -0
- package/.agents/skills/vidfarm/references/automation-and-local-dev.md +27 -6
- package/.agents/skills/vidfarm/references/core-workflows.md +1 -1
- package/.agents/skills/vidfarm/references/editor-workflows.md +44 -2
- package/.agents/skills/vidfarm/references/primitives.md +28 -0
- package/.agents/skills/vidfarm-media/SKILL.md +3 -1
- package/.agents/skills/vidfarm-media/references/tts.md +1 -1
- package/SKILL.director.md +152 -15
- package/SKILL.md +6 -2
- package/clipper.md +3 -2
- package/dist/src/cli.js +409 -19
- package/dist/src/devcli/cost-mode.js +46 -18
- package/dist/src/devcli/sequence.js +619 -0
- package/dist/src/services/sequence-prompts.js +463 -0
- package/package.json +3 -1
- package/public/assets/file-directory-app.js +26 -26
- package/public/assets/homepage-client-app.js +12 -12
- package/public/serve-shells/editor.html +66 -3
- package/public/serve-shells/library-files.html +60 -3
- package/public/serve-shells/library-raws.html +60 -3
- package/public/serve-shells/tools-clipper.html +60 -3
- package/public/serve-shells/tools-image.html +1069 -483
- package/public/serve-shells/tools-video.html +140 -14
|
@@ -576,6 +576,7 @@
|
|
|
576
576
|
background:#fff;border:1px solid var(--rk-border);border-radius:var(--rk-r-full);padding:4px 11px;text-decoration:none;line-height:1.4;
|
|
577
577
|
transition:background var(--rk-dur) var(--rk-ease),border-color var(--rk-dur) var(--rk-ease)}
|
|
578
578
|
.rk-aichat-preview-act:hover{background:var(--rk-gold-tint);border-color:var(--rk-gold-600)}
|
|
579
|
+
.rk-aichat-preview-act[hidden]{display:none}
|
|
579
580
|
|
|
580
581
|
/* ── markdown pipe tables (assistant messages + .md previews) ── */
|
|
581
582
|
.rk-md-tablewrap{max-width:100%;overflow-x:auto;margin:8px 0;border:1px solid var(--rk-border);border-radius:var(--rk-r-lg)}
|
|
@@ -607,6 +608,14 @@
|
|
|
607
608
|
.rk-aichat-file-thumb--markdown,.rk-aichat-file-thumb--text{background:var(--rk-n-50)}.rk-aichat-file-thumb--audio{background:var(--rk-gold-tint2)}
|
|
608
609
|
.rk-aichat-file-play{position:absolute;inset:0;display:grid;place-items:center;font-size:22px;color:#fff;text-shadow:0 1px 6px rgba(0,0,0,.55);pointer-events:none}
|
|
609
610
|
.rk-aichat-file-name{font-size:11px;font-weight:600;color:var(--rk-n-600);line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:112px}
|
|
611
|
+
/* "Edit in the tool" pencil — only on image/video cards; opens /tools/image or
|
|
612
|
+
/tools/video in a new tab with this file preloaded as the working file. */
|
|
613
|
+
.rk-aichat-file-open{position:absolute;top:5px;right:5px;width:24px;height:24px;border-radius:var(--rk-r-md);
|
|
614
|
+
display:grid;place-items:center;background:rgba(255,255,255,.94);border:1px solid var(--rk-border);color:var(--rk-ink);
|
|
615
|
+
opacity:0;transition:opacity var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease);z-index:2}
|
|
616
|
+
.rk-aichat-file-open svg{width:13px;height:13px}
|
|
617
|
+
.rk-aichat-file:hover .rk-aichat-file-open,.rk-aichat-file-open:focus-visible{opacity:1}
|
|
618
|
+
.rk-aichat-file-open:hover{background:var(--rk-gold-tint)}
|
|
610
619
|
|
|
611
620
|
/* create_video pipeline: live status row + clickable result links */
|
|
612
621
|
.rk-aichat-flow{display:flex;align-items:center;gap:9px;font-size:12.5px;color:var(--rk-text-muted);padding:2px 2px}
|
|
@@ -1095,6 +1104,12 @@ html,body{margin:0;background:#050604;color:#fffbe6}
|
|
|
1095
1104
|
.rk-aichat--editor .rk-aichat-toolnote.is-ok::before{background:#33d17a}
|
|
1096
1105
|
.rk-aichat--editor .rk-aichat-toolnote.is-err{color:#ff9b8f}
|
|
1097
1106
|
.rk-aichat--editor .rk-aichat-toolnote.is-err::before{background:#ff6a5a}
|
|
1107
|
+
/* "Edit in the tool" pencil + preview actions are hardcoded light in the shared
|
|
1108
|
+
sheet; dark them so they read on the editor dock's dark chrome. */
|
|
1109
|
+
.rk-aichat--editor .rk-aichat-file-open,
|
|
1110
|
+
.rk-aichat--editor .rk-aichat-preview-act{background:#141810;color:var(--rk-ink)}
|
|
1111
|
+
.rk-aichat--editor .rk-aichat-file-open:hover,
|
|
1112
|
+
.rk-aichat--editor .rk-aichat-preview-act:hover{background:#1b2013}
|
|
1098
1113
|
|
|
1099
1114
|
/* Hide the SPA's own embedded chat (replaced by the farmville dock). */
|
|
1100
1115
|
.chat-dock,.chat-dock-fab{display:none !important}
|
|
@@ -1822,6 +1837,34 @@ html,body{margin:0;background:#050604;color:#fffbe6}
|
|
|
1822
1837
|
}
|
|
1823
1838
|
function fileIcon(kind) { return kind === 'pdf' ? '📕' : kind === 'json' ? '{ }' : kind === 'markdown' ? '📝' : kind === 'audio' ? '🎵' : kind === 'text' ? '📄' : '📄'; }
|
|
1824
1839
|
|
|
1840
|
+
// ── "edit this media" hand-off: dock chat → the matching creative tool ──
|
|
1841
|
+
// Images open /tools/image, videos /tools/video, each with the file preloaded as
|
|
1842
|
+
// that tool's working file (its chat then edits THAT media). Deliberately a fresh
|
|
1843
|
+
// tool session — no ?tracer — so the tool names its own chat from the first prompt.
|
|
1844
|
+
var PENCIL_SVG = '<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="M13.6 3.6a1.9 1.9 0 0 1 2.8 2.8L7.7 15.1l-3.6.8.8-3.6z"/></svg>';
|
|
1845
|
+
function toolEditUrl(file) {
|
|
1846
|
+
if (!file || !file.viewUrl) return '';
|
|
1847
|
+
var k = fileKind(file.name, file.contentType);
|
|
1848
|
+
var name = file.name || '';
|
|
1849
|
+
if (k === 'image') return '/tools/image?source_image_url=' + encodeURIComponent(file.viewUrl) + (name ? '&source_image_name=' + encodeURIComponent(name) : '');
|
|
1850
|
+
if (k === 'video') return '/tools/video?source_video_url=' + encodeURIComponent(file.viewUrl) + (name ? '&source_video_name=' + encodeURIComponent(name) : '');
|
|
1851
|
+
return '';
|
|
1852
|
+
}
|
|
1853
|
+
function toolEditLink(file, className) {
|
|
1854
|
+
var href = toolEditUrl(file);
|
|
1855
|
+
if (!href) return null;
|
|
1856
|
+
var a = document.createElement('a');
|
|
1857
|
+
a.className = className;
|
|
1858
|
+
a.href = href;
|
|
1859
|
+
a.target = '_blank';
|
|
1860
|
+
a.rel = 'noopener noreferrer';
|
|
1861
|
+
a.title = 'Edit in the ' + (fileKind(file.name, file.contentType) === 'video' ? 'video' : 'image') + ' tool';
|
|
1862
|
+
a.setAttribute('aria-label', a.title);
|
|
1863
|
+
// The card itself opens the lightbox; the pencil must not also trigger it.
|
|
1864
|
+
a.addEventListener('click', function (e) { e.stopPropagation(); });
|
|
1865
|
+
return a;
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1825
1868
|
// Compact markdown → HTML for .md previews (headings, lists, quotes, rules,
|
|
1826
1869
|
// fenced + inline code, bold/italic/links). Escapes first, then injects tags
|
|
1827
1870
|
// (same order as linkify). Backticks are built via char codes to avoid
|
|
@@ -1882,7 +1925,9 @@ html,body{margin:0;background:#050604;color:#fffbe6}
|
|
|
1882
1925
|
var dlBtn = document.createElement('a'); dlBtn.className = 'rk-aichat-preview-act'; dlBtn.target = '_blank'; dlBtn.rel = 'noopener noreferrer'; dlBtn.textContent = 'Download';
|
|
1883
1926
|
var linkBtn = document.createElement('button'); linkBtn.type = 'button'; linkBtn.className = 'rk-aichat-preview-act'; linkBtn.textContent = 'Copy link';
|
|
1884
1927
|
var xBtn = document.createElement('button'); xBtn.type = 'button'; xBtn.className = 'rk-aichat-modal-x'; xBtn.setAttribute('aria-label', 'Close'); xBtn.innerHTML = '×';
|
|
1885
|
-
|
|
1928
|
+
// Same hand-off as the card pencil, spelled out as a labelled action here.
|
|
1929
|
+
var editBtn = document.createElement('a'); editBtn.className = 'rk-aichat-preview-act'; editBtn.target = '_blank'; editBtn.rel = 'noopener noreferrer'; editBtn.textContent = 'Edit';
|
|
1930
|
+
head.appendChild(nameEl); head.appendChild(editBtn); head.appendChild(copyBtn); head.appendChild(dlBtn); head.appendChild(linkBtn); head.appendChild(xBtn);
|
|
1886
1931
|
function flashAct(btn, label) { var o = btn.getAttribute('data-label') || btn.textContent; btn.setAttribute('data-label', o); btn.textContent = label; setTimeout(function () { btn.textContent = o; }, 1400); }
|
|
1887
1932
|
// Copy = file text (json/md/txt) or its URL for binaries. Copy link = the
|
|
1888
1933
|
// durable public S3 URL. Download opens in a new tab when the browser can't
|
|
@@ -1931,6 +1976,9 @@ html,body{margin:0;background:#050604;color:#fffbe6}
|
|
|
1931
1976
|
var it = items[idx];
|
|
1932
1977
|
nameEl.textContent = it.name || 'file'; nameEl.title = it.name || '';
|
|
1933
1978
|
dlBtn.href = it.viewUrl; dlBtn.setAttribute('download', it.name || '');
|
|
1979
|
+
var editHref = toolEditUrl(it);
|
|
1980
|
+
editBtn.hidden = !editHref;
|
|
1981
|
+
if (editHref) { editBtn.href = editHref; editBtn.title = 'Edit in the ' + (fileKind(it.name, it.contentType) === 'video' ? 'video' : 'image') + ' tool'; }
|
|
1934
1982
|
linkBtn.style.display = it.publicUrl ? '' : 'none';
|
|
1935
1983
|
body.innerHTML = '';
|
|
1936
1984
|
var kind = fileKind(it.name, it.contentType);
|
|
@@ -1985,7 +2033,15 @@ html,body{margin:0;background:#050604;color:#fffbe6}
|
|
|
1985
2033
|
var files = [], seen = {};
|
|
1986
2034
|
var arts = (job && job.artifacts) || [];
|
|
1987
2035
|
for (var i = 0; i < arts.length; i++) { var f = rkFileFromArtifact(arts[i]); if (f && !seen[f.viewUrl]) { seen[f.viewUrl] = true; files.push(f); } }
|
|
1988
|
-
|
|
2036
|
+
// Name the direct result URL after the FILE, not the tracer — fileKind() reads the
|
|
2037
|
+
// extension, and a tracer-named entry ("my-poster") classifies as 'other', losing
|
|
2038
|
+
// the image/video thumbnail and the edit-in-tool hand-off.
|
|
2039
|
+
var mu = rkResultMediaUrl(job);
|
|
2040
|
+
if (mu && !seen[mu]) {
|
|
2041
|
+
seen[mu] = true;
|
|
2042
|
+
var seg = String(mu).split('?')[0].split('/').pop() || '';
|
|
2043
|
+
files.push({ name: (/.[a-z0-9]{2,5}$/i.test(seg) ? seg : (job.tracer || 'output')), contentType: '', viewUrl: mu, publicUrl: mu });
|
|
2044
|
+
}
|
|
1989
2045
|
var res = job && job.result;
|
|
1990
2046
|
if (res && typeof res === 'object' && !Array.isArray(res) && Object.keys(res).length) {
|
|
1991
2047
|
try { var blob = new Blob([JSON.stringify(res, null, 2)], { type: 'application/json' }); files.push({ name: (job.tracer || 'result') + '.json', contentType: 'application/json', viewUrl: URL.createObjectURL(blob), publicUrl: null }); } catch (e) {}
|
|
@@ -2038,14 +2094,21 @@ html,body{margin:0;background:#050604;color:#fffbe6}
|
|
|
2038
2094
|
var grid = document.createElement('div'); grid.className = 'rk-aichat-files-grid';
|
|
2039
2095
|
list.forEach(function (f, ix) {
|
|
2040
2096
|
var k = fileKind(f.name, f.contentType);
|
|
2041
|
-
|
|
2097
|
+
// A div, not a button: media cards carry a nested "edit in tool" link, and
|
|
2098
|
+
// an anchor inside a button is invalid (and unclickable in some browsers).
|
|
2099
|
+
var card = document.createElement('div'); card.className = 'rk-aichat-file'; card.tabIndex = 0; card.setAttribute('role', 'button');
|
|
2042
2100
|
var thumb = document.createElement('div'); thumb.className = 'rk-aichat-file-thumb rk-aichat-file-thumb--' + k;
|
|
2043
2101
|
if (k === 'image') { var im = document.createElement('img'); im.src = f.viewUrl; im.alt = ''; im.loading = 'lazy'; thumb.appendChild(im); }
|
|
2044
2102
|
else if (k === 'video') { var v = document.createElement('video'); v.src = f.viewUrl; v.muted = true; v.preload = 'metadata'; v.playsInline = true; thumb.appendChild(v); var pl = document.createElement('span'); pl.className = 'rk-aichat-file-play'; pl.textContent = '▶'; thumb.appendChild(pl); }
|
|
2045
2103
|
else { var ic = document.createElement('span'); ic.className = 'rk-aichat-file-ic'; ic.textContent = fileIcon(k); thumb.appendChild(ic); }
|
|
2104
|
+
var edit = toolEditLink(f, 'rk-aichat-file-open');
|
|
2105
|
+
if (edit) { edit.innerHTML = PENCIL_SVG; thumb.appendChild(edit); }
|
|
2046
2106
|
var nm = document.createElement('div'); nm.className = 'rk-aichat-file-name'; nm.textContent = f.name || 'file'; nm.title = f.name || '';
|
|
2047
2107
|
card.appendChild(thumb); card.appendChild(nm);
|
|
2048
2108
|
card.addEventListener('click', function () { openFilePreview(list, ix); });
|
|
2109
|
+
card.addEventListener('keydown', function (e) {
|
|
2110
|
+
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openFilePreview(list, ix); }
|
|
2111
|
+
});
|
|
2049
2112
|
grid.appendChild(card);
|
|
2050
2113
|
});
|
|
2051
2114
|
wrap.appendChild(grid); turn.appendChild(wrap); logBottom();
|
|
@@ -591,6 +591,7 @@
|
|
|
591
591
|
background:#fff;border:1px solid var(--rk-border);border-radius:var(--rk-r-full);padding:4px 11px;text-decoration:none;line-height:1.4;
|
|
592
592
|
transition:background var(--rk-dur) var(--rk-ease),border-color var(--rk-dur) var(--rk-ease)}
|
|
593
593
|
.rk-aichat-preview-act:hover{background:var(--rk-gold-tint);border-color:var(--rk-gold-600)}
|
|
594
|
+
.rk-aichat-preview-act[hidden]{display:none}
|
|
594
595
|
|
|
595
596
|
/* ── markdown pipe tables (assistant messages + .md previews) ── */
|
|
596
597
|
.rk-md-tablewrap{max-width:100%;overflow-x:auto;margin:8px 0;border:1px solid var(--rk-border);border-radius:var(--rk-r-lg)}
|
|
@@ -622,6 +623,14 @@
|
|
|
622
623
|
.rk-aichat-file-thumb--markdown,.rk-aichat-file-thumb--text{background:var(--rk-n-50)}.rk-aichat-file-thumb--audio{background:var(--rk-gold-tint2)}
|
|
623
624
|
.rk-aichat-file-play{position:absolute;inset:0;display:grid;place-items:center;font-size:22px;color:#fff;text-shadow:0 1px 6px rgba(0,0,0,.55);pointer-events:none}
|
|
624
625
|
.rk-aichat-file-name{font-size:11px;font-weight:600;color:var(--rk-n-600);line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:112px}
|
|
626
|
+
/* "Edit in the tool" pencil — only on image/video cards; opens /tools/image or
|
|
627
|
+
/tools/video in a new tab with this file preloaded as the working file. */
|
|
628
|
+
.rk-aichat-file-open{position:absolute;top:5px;right:5px;width:24px;height:24px;border-radius:var(--rk-r-md);
|
|
629
|
+
display:grid;place-items:center;background:rgba(255,255,255,.94);border:1px solid var(--rk-border);color:var(--rk-ink);
|
|
630
|
+
opacity:0;transition:opacity var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease);z-index:2}
|
|
631
|
+
.rk-aichat-file-open svg{width:13px;height:13px}
|
|
632
|
+
.rk-aichat-file:hover .rk-aichat-file-open,.rk-aichat-file-open:focus-visible{opacity:1}
|
|
633
|
+
.rk-aichat-file-open:hover{background:var(--rk-gold-tint)}
|
|
625
634
|
|
|
626
635
|
/* create_video pipeline: live status row + clickable result links */
|
|
627
636
|
.rk-aichat-flow{display:flex;align-items:center;gap:9px;font-size:12.5px;color:var(--rk-text-muted);padding:2px 2px}
|
|
@@ -2148,6 +2157,34 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
2148
2157
|
}
|
|
2149
2158
|
function fileIcon(kind) { return kind === 'pdf' ? '📕' : kind === 'json' ? '{ }' : kind === 'markdown' ? '📝' : kind === 'audio' ? '🎵' : kind === 'text' ? '📄' : '📄'; }
|
|
2150
2159
|
|
|
2160
|
+
// ── "edit this media" hand-off: dock chat → the matching creative tool ──
|
|
2161
|
+
// Images open /tools/image, videos /tools/video, each with the file preloaded as
|
|
2162
|
+
// that tool's working file (its chat then edits THAT media). Deliberately a fresh
|
|
2163
|
+
// tool session — no ?tracer — so the tool names its own chat from the first prompt.
|
|
2164
|
+
var PENCIL_SVG = '<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="M13.6 3.6a1.9 1.9 0 0 1 2.8 2.8L7.7 15.1l-3.6.8.8-3.6z"/></svg>';
|
|
2165
|
+
function toolEditUrl(file) {
|
|
2166
|
+
if (!file || !file.viewUrl) return '';
|
|
2167
|
+
var k = fileKind(file.name, file.contentType);
|
|
2168
|
+
var name = file.name || '';
|
|
2169
|
+
if (k === 'image') return '/tools/image?source_image_url=' + encodeURIComponent(file.viewUrl) + (name ? '&source_image_name=' + encodeURIComponent(name) : '');
|
|
2170
|
+
if (k === 'video') return '/tools/video?source_video_url=' + encodeURIComponent(file.viewUrl) + (name ? '&source_video_name=' + encodeURIComponent(name) : '');
|
|
2171
|
+
return '';
|
|
2172
|
+
}
|
|
2173
|
+
function toolEditLink(file, className) {
|
|
2174
|
+
var href = toolEditUrl(file);
|
|
2175
|
+
if (!href) return null;
|
|
2176
|
+
var a = document.createElement('a');
|
|
2177
|
+
a.className = className;
|
|
2178
|
+
a.href = href;
|
|
2179
|
+
a.target = '_blank';
|
|
2180
|
+
a.rel = 'noopener noreferrer';
|
|
2181
|
+
a.title = 'Edit in the ' + (fileKind(file.name, file.contentType) === 'video' ? 'video' : 'image') + ' tool';
|
|
2182
|
+
a.setAttribute('aria-label', a.title);
|
|
2183
|
+
// The card itself opens the lightbox; the pencil must not also trigger it.
|
|
2184
|
+
a.addEventListener('click', function (e) { e.stopPropagation(); });
|
|
2185
|
+
return a;
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2151
2188
|
// Compact markdown → HTML for .md previews (headings, lists, quotes, rules,
|
|
2152
2189
|
// fenced + inline code, bold/italic/links). Escapes first, then injects tags
|
|
2153
2190
|
// (same order as linkify). Backticks are built via char codes to avoid
|
|
@@ -2208,7 +2245,9 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
2208
2245
|
var dlBtn = document.createElement('a'); dlBtn.className = 'rk-aichat-preview-act'; dlBtn.target = '_blank'; dlBtn.rel = 'noopener noreferrer'; dlBtn.textContent = 'Download';
|
|
2209
2246
|
var linkBtn = document.createElement('button'); linkBtn.type = 'button'; linkBtn.className = 'rk-aichat-preview-act'; linkBtn.textContent = 'Copy link';
|
|
2210
2247
|
var xBtn = document.createElement('button'); xBtn.type = 'button'; xBtn.className = 'rk-aichat-modal-x'; xBtn.setAttribute('aria-label', 'Close'); xBtn.innerHTML = '×';
|
|
2211
|
-
|
|
2248
|
+
// Same hand-off as the card pencil, spelled out as a labelled action here.
|
|
2249
|
+
var editBtn = document.createElement('a'); editBtn.className = 'rk-aichat-preview-act'; editBtn.target = '_blank'; editBtn.rel = 'noopener noreferrer'; editBtn.textContent = 'Edit';
|
|
2250
|
+
head.appendChild(nameEl); head.appendChild(editBtn); head.appendChild(copyBtn); head.appendChild(dlBtn); head.appendChild(linkBtn); head.appendChild(xBtn);
|
|
2212
2251
|
function flashAct(btn, label) { var o = btn.getAttribute('data-label') || btn.textContent; btn.setAttribute('data-label', o); btn.textContent = label; setTimeout(function () { btn.textContent = o; }, 1400); }
|
|
2213
2252
|
// Copy = file text (json/md/txt) or its URL for binaries. Copy link = the
|
|
2214
2253
|
// durable public S3 URL. Download opens in a new tab when the browser can't
|
|
@@ -2257,6 +2296,9 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
2257
2296
|
var it = items[idx];
|
|
2258
2297
|
nameEl.textContent = it.name || 'file'; nameEl.title = it.name || '';
|
|
2259
2298
|
dlBtn.href = it.viewUrl; dlBtn.setAttribute('download', it.name || '');
|
|
2299
|
+
var editHref = toolEditUrl(it);
|
|
2300
|
+
editBtn.hidden = !editHref;
|
|
2301
|
+
if (editHref) { editBtn.href = editHref; editBtn.title = 'Edit in the ' + (fileKind(it.name, it.contentType) === 'video' ? 'video' : 'image') + ' tool'; }
|
|
2260
2302
|
linkBtn.style.display = it.publicUrl ? '' : 'none';
|
|
2261
2303
|
body.innerHTML = '';
|
|
2262
2304
|
var kind = fileKind(it.name, it.contentType);
|
|
@@ -2311,7 +2353,15 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
2311
2353
|
var files = [], seen = {};
|
|
2312
2354
|
var arts = (job && job.artifacts) || [];
|
|
2313
2355
|
for (var i = 0; i < arts.length; i++) { var f = rkFileFromArtifact(arts[i]); if (f && !seen[f.viewUrl]) { seen[f.viewUrl] = true; files.push(f); } }
|
|
2314
|
-
|
|
2356
|
+
// Name the direct result URL after the FILE, not the tracer — fileKind() reads the
|
|
2357
|
+
// extension, and a tracer-named entry ("my-poster") classifies as 'other', losing
|
|
2358
|
+
// the image/video thumbnail and the edit-in-tool hand-off.
|
|
2359
|
+
var mu = rkResultMediaUrl(job);
|
|
2360
|
+
if (mu && !seen[mu]) {
|
|
2361
|
+
seen[mu] = true;
|
|
2362
|
+
var seg = String(mu).split('?')[0].split('/').pop() || '';
|
|
2363
|
+
files.push({ name: (/.[a-z0-9]{2,5}$/i.test(seg) ? seg : (job.tracer || 'output')), contentType: '', viewUrl: mu, publicUrl: mu });
|
|
2364
|
+
}
|
|
2315
2365
|
var res = job && job.result;
|
|
2316
2366
|
if (res && typeof res === 'object' && !Array.isArray(res) && Object.keys(res).length) {
|
|
2317
2367
|
try { var blob = new Blob([JSON.stringify(res, null, 2)], { type: 'application/json' }); files.push({ name: (job.tracer || 'result') + '.json', contentType: 'application/json', viewUrl: URL.createObjectURL(blob), publicUrl: null }); } catch (e) {}
|
|
@@ -2364,14 +2414,21 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
2364
2414
|
var grid = document.createElement('div'); grid.className = 'rk-aichat-files-grid';
|
|
2365
2415
|
list.forEach(function (f, ix) {
|
|
2366
2416
|
var k = fileKind(f.name, f.contentType);
|
|
2367
|
-
|
|
2417
|
+
// A div, not a button: media cards carry a nested "edit in tool" link, and
|
|
2418
|
+
// an anchor inside a button is invalid (and unclickable in some browsers).
|
|
2419
|
+
var card = document.createElement('div'); card.className = 'rk-aichat-file'; card.tabIndex = 0; card.setAttribute('role', 'button');
|
|
2368
2420
|
var thumb = document.createElement('div'); thumb.className = 'rk-aichat-file-thumb rk-aichat-file-thumb--' + k;
|
|
2369
2421
|
if (k === 'image') { var im = document.createElement('img'); im.src = f.viewUrl; im.alt = ''; im.loading = 'lazy'; thumb.appendChild(im); }
|
|
2370
2422
|
else if (k === 'video') { var v = document.createElement('video'); v.src = f.viewUrl; v.muted = true; v.preload = 'metadata'; v.playsInline = true; thumb.appendChild(v); var pl = document.createElement('span'); pl.className = 'rk-aichat-file-play'; pl.textContent = '▶'; thumb.appendChild(pl); }
|
|
2371
2423
|
else { var ic = document.createElement('span'); ic.className = 'rk-aichat-file-ic'; ic.textContent = fileIcon(k); thumb.appendChild(ic); }
|
|
2424
|
+
var edit = toolEditLink(f, 'rk-aichat-file-open');
|
|
2425
|
+
if (edit) { edit.innerHTML = PENCIL_SVG; thumb.appendChild(edit); }
|
|
2372
2426
|
var nm = document.createElement('div'); nm.className = 'rk-aichat-file-name'; nm.textContent = f.name || 'file'; nm.title = f.name || '';
|
|
2373
2427
|
card.appendChild(thumb); card.appendChild(nm);
|
|
2374
2428
|
card.addEventListener('click', function () { openFilePreview(list, ix); });
|
|
2429
|
+
card.addEventListener('keydown', function (e) {
|
|
2430
|
+
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openFilePreview(list, ix); }
|
|
2431
|
+
});
|
|
2375
2432
|
grid.appendChild(card);
|
|
2376
2433
|
});
|
|
2377
2434
|
wrap.appendChild(grid); turn.appendChild(wrap); logBottom();
|
|
@@ -591,6 +591,7 @@
|
|
|
591
591
|
background:#fff;border:1px solid var(--rk-border);border-radius:var(--rk-r-full);padding:4px 11px;text-decoration:none;line-height:1.4;
|
|
592
592
|
transition:background var(--rk-dur) var(--rk-ease),border-color var(--rk-dur) var(--rk-ease)}
|
|
593
593
|
.rk-aichat-preview-act:hover{background:var(--rk-gold-tint);border-color:var(--rk-gold-600)}
|
|
594
|
+
.rk-aichat-preview-act[hidden]{display:none}
|
|
594
595
|
|
|
595
596
|
/* ── markdown pipe tables (assistant messages + .md previews) ── */
|
|
596
597
|
.rk-md-tablewrap{max-width:100%;overflow-x:auto;margin:8px 0;border:1px solid var(--rk-border);border-radius:var(--rk-r-lg)}
|
|
@@ -622,6 +623,14 @@
|
|
|
622
623
|
.rk-aichat-file-thumb--markdown,.rk-aichat-file-thumb--text{background:var(--rk-n-50)}.rk-aichat-file-thumb--audio{background:var(--rk-gold-tint2)}
|
|
623
624
|
.rk-aichat-file-play{position:absolute;inset:0;display:grid;place-items:center;font-size:22px;color:#fff;text-shadow:0 1px 6px rgba(0,0,0,.55);pointer-events:none}
|
|
624
625
|
.rk-aichat-file-name{font-size:11px;font-weight:600;color:var(--rk-n-600);line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:112px}
|
|
626
|
+
/* "Edit in the tool" pencil — only on image/video cards; opens /tools/image or
|
|
627
|
+
/tools/video in a new tab with this file preloaded as the working file. */
|
|
628
|
+
.rk-aichat-file-open{position:absolute;top:5px;right:5px;width:24px;height:24px;border-radius:var(--rk-r-md);
|
|
629
|
+
display:grid;place-items:center;background:rgba(255,255,255,.94);border:1px solid var(--rk-border);color:var(--rk-ink);
|
|
630
|
+
opacity:0;transition:opacity var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease);z-index:2}
|
|
631
|
+
.rk-aichat-file-open svg{width:13px;height:13px}
|
|
632
|
+
.rk-aichat-file:hover .rk-aichat-file-open,.rk-aichat-file-open:focus-visible{opacity:1}
|
|
633
|
+
.rk-aichat-file-open:hover{background:var(--rk-gold-tint)}
|
|
625
634
|
|
|
626
635
|
/* create_video pipeline: live status row + clickable result links */
|
|
627
636
|
.rk-aichat-flow{display:flex;align-items:center;gap:9px;font-size:12.5px;color:var(--rk-text-muted);padding:2px 2px}
|
|
@@ -3224,6 +3233,34 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
3224
3233
|
}
|
|
3225
3234
|
function fileIcon(kind) { return kind === 'pdf' ? '📕' : kind === 'json' ? '{ }' : kind === 'markdown' ? '📝' : kind === 'audio' ? '🎵' : kind === 'text' ? '📄' : '📄'; }
|
|
3226
3235
|
|
|
3236
|
+
// ── "edit this media" hand-off: dock chat → the matching creative tool ──
|
|
3237
|
+
// Images open /tools/image, videos /tools/video, each with the file preloaded as
|
|
3238
|
+
// that tool's working file (its chat then edits THAT media). Deliberately a fresh
|
|
3239
|
+
// tool session — no ?tracer — so the tool names its own chat from the first prompt.
|
|
3240
|
+
var PENCIL_SVG = '<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="M13.6 3.6a1.9 1.9 0 0 1 2.8 2.8L7.7 15.1l-3.6.8.8-3.6z"/></svg>';
|
|
3241
|
+
function toolEditUrl(file) {
|
|
3242
|
+
if (!file || !file.viewUrl) return '';
|
|
3243
|
+
var k = fileKind(file.name, file.contentType);
|
|
3244
|
+
var name = file.name || '';
|
|
3245
|
+
if (k === 'image') return '/tools/image?source_image_url=' + encodeURIComponent(file.viewUrl) + (name ? '&source_image_name=' + encodeURIComponent(name) : '');
|
|
3246
|
+
if (k === 'video') return '/tools/video?source_video_url=' + encodeURIComponent(file.viewUrl) + (name ? '&source_video_name=' + encodeURIComponent(name) : '');
|
|
3247
|
+
return '';
|
|
3248
|
+
}
|
|
3249
|
+
function toolEditLink(file, className) {
|
|
3250
|
+
var href = toolEditUrl(file);
|
|
3251
|
+
if (!href) return null;
|
|
3252
|
+
var a = document.createElement('a');
|
|
3253
|
+
a.className = className;
|
|
3254
|
+
a.href = href;
|
|
3255
|
+
a.target = '_blank';
|
|
3256
|
+
a.rel = 'noopener noreferrer';
|
|
3257
|
+
a.title = 'Edit in the ' + (fileKind(file.name, file.contentType) === 'video' ? 'video' : 'image') + ' tool';
|
|
3258
|
+
a.setAttribute('aria-label', a.title);
|
|
3259
|
+
// The card itself opens the lightbox; the pencil must not also trigger it.
|
|
3260
|
+
a.addEventListener('click', function (e) { e.stopPropagation(); });
|
|
3261
|
+
return a;
|
|
3262
|
+
}
|
|
3263
|
+
|
|
3227
3264
|
// Compact markdown → HTML for .md previews (headings, lists, quotes, rules,
|
|
3228
3265
|
// fenced + inline code, bold/italic/links). Escapes first, then injects tags
|
|
3229
3266
|
// (same order as linkify). Backticks are built via char codes to avoid
|
|
@@ -3284,7 +3321,9 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
3284
3321
|
var dlBtn = document.createElement('a'); dlBtn.className = 'rk-aichat-preview-act'; dlBtn.target = '_blank'; dlBtn.rel = 'noopener noreferrer'; dlBtn.textContent = 'Download';
|
|
3285
3322
|
var linkBtn = document.createElement('button'); linkBtn.type = 'button'; linkBtn.className = 'rk-aichat-preview-act'; linkBtn.textContent = 'Copy link';
|
|
3286
3323
|
var xBtn = document.createElement('button'); xBtn.type = 'button'; xBtn.className = 'rk-aichat-modal-x'; xBtn.setAttribute('aria-label', 'Close'); xBtn.innerHTML = '×';
|
|
3287
|
-
|
|
3324
|
+
// Same hand-off as the card pencil, spelled out as a labelled action here.
|
|
3325
|
+
var editBtn = document.createElement('a'); editBtn.className = 'rk-aichat-preview-act'; editBtn.target = '_blank'; editBtn.rel = 'noopener noreferrer'; editBtn.textContent = 'Edit';
|
|
3326
|
+
head.appendChild(nameEl); head.appendChild(editBtn); head.appendChild(copyBtn); head.appendChild(dlBtn); head.appendChild(linkBtn); head.appendChild(xBtn);
|
|
3288
3327
|
function flashAct(btn, label) { var o = btn.getAttribute('data-label') || btn.textContent; btn.setAttribute('data-label', o); btn.textContent = label; setTimeout(function () { btn.textContent = o; }, 1400); }
|
|
3289
3328
|
// Copy = file text (json/md/txt) or its URL for binaries. Copy link = the
|
|
3290
3329
|
// durable public S3 URL. Download opens in a new tab when the browser can't
|
|
@@ -3333,6 +3372,9 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
3333
3372
|
var it = items[idx];
|
|
3334
3373
|
nameEl.textContent = it.name || 'file'; nameEl.title = it.name || '';
|
|
3335
3374
|
dlBtn.href = it.viewUrl; dlBtn.setAttribute('download', it.name || '');
|
|
3375
|
+
var editHref = toolEditUrl(it);
|
|
3376
|
+
editBtn.hidden = !editHref;
|
|
3377
|
+
if (editHref) { editBtn.href = editHref; editBtn.title = 'Edit in the ' + (fileKind(it.name, it.contentType) === 'video' ? 'video' : 'image') + ' tool'; }
|
|
3336
3378
|
linkBtn.style.display = it.publicUrl ? '' : 'none';
|
|
3337
3379
|
body.innerHTML = '';
|
|
3338
3380
|
var kind = fileKind(it.name, it.contentType);
|
|
@@ -3387,7 +3429,15 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
3387
3429
|
var files = [], seen = {};
|
|
3388
3430
|
var arts = (job && job.artifacts) || [];
|
|
3389
3431
|
for (var i = 0; i < arts.length; i++) { var f = rkFileFromArtifact(arts[i]); if (f && !seen[f.viewUrl]) { seen[f.viewUrl] = true; files.push(f); } }
|
|
3390
|
-
|
|
3432
|
+
// Name the direct result URL after the FILE, not the tracer — fileKind() reads the
|
|
3433
|
+
// extension, and a tracer-named entry ("my-poster") classifies as 'other', losing
|
|
3434
|
+
// the image/video thumbnail and the edit-in-tool hand-off.
|
|
3435
|
+
var mu = rkResultMediaUrl(job);
|
|
3436
|
+
if (mu && !seen[mu]) {
|
|
3437
|
+
seen[mu] = true;
|
|
3438
|
+
var seg = String(mu).split('?')[0].split('/').pop() || '';
|
|
3439
|
+
files.push({ name: (/.[a-z0-9]{2,5}$/i.test(seg) ? seg : (job.tracer || 'output')), contentType: '', viewUrl: mu, publicUrl: mu });
|
|
3440
|
+
}
|
|
3391
3441
|
var res = job && job.result;
|
|
3392
3442
|
if (res && typeof res === 'object' && !Array.isArray(res) && Object.keys(res).length) {
|
|
3393
3443
|
try { var blob = new Blob([JSON.stringify(res, null, 2)], { type: 'application/json' }); files.push({ name: (job.tracer || 'result') + '.json', contentType: 'application/json', viewUrl: URL.createObjectURL(blob), publicUrl: null }); } catch (e) {}
|
|
@@ -3440,14 +3490,21 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
3440
3490
|
var grid = document.createElement('div'); grid.className = 'rk-aichat-files-grid';
|
|
3441
3491
|
list.forEach(function (f, ix) {
|
|
3442
3492
|
var k = fileKind(f.name, f.contentType);
|
|
3443
|
-
|
|
3493
|
+
// A div, not a button: media cards carry a nested "edit in tool" link, and
|
|
3494
|
+
// an anchor inside a button is invalid (and unclickable in some browsers).
|
|
3495
|
+
var card = document.createElement('div'); card.className = 'rk-aichat-file'; card.tabIndex = 0; card.setAttribute('role', 'button');
|
|
3444
3496
|
var thumb = document.createElement('div'); thumb.className = 'rk-aichat-file-thumb rk-aichat-file-thumb--' + k;
|
|
3445
3497
|
if (k === 'image') { var im = document.createElement('img'); im.src = f.viewUrl; im.alt = ''; im.loading = 'lazy'; thumb.appendChild(im); }
|
|
3446
3498
|
else if (k === 'video') { var v = document.createElement('video'); v.src = f.viewUrl; v.muted = true; v.preload = 'metadata'; v.playsInline = true; thumb.appendChild(v); var pl = document.createElement('span'); pl.className = 'rk-aichat-file-play'; pl.textContent = '▶'; thumb.appendChild(pl); }
|
|
3447
3499
|
else { var ic = document.createElement('span'); ic.className = 'rk-aichat-file-ic'; ic.textContent = fileIcon(k); thumb.appendChild(ic); }
|
|
3500
|
+
var edit = toolEditLink(f, 'rk-aichat-file-open');
|
|
3501
|
+
if (edit) { edit.innerHTML = PENCIL_SVG; thumb.appendChild(edit); }
|
|
3448
3502
|
var nm = document.createElement('div'); nm.className = 'rk-aichat-file-name'; nm.textContent = f.name || 'file'; nm.title = f.name || '';
|
|
3449
3503
|
card.appendChild(thumb); card.appendChild(nm);
|
|
3450
3504
|
card.addEventListener('click', function () { openFilePreview(list, ix); });
|
|
3505
|
+
card.addEventListener('keydown', function (e) {
|
|
3506
|
+
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openFilePreview(list, ix); }
|
|
3507
|
+
});
|
|
3451
3508
|
grid.appendChild(card);
|
|
3452
3509
|
});
|
|
3453
3510
|
wrap.appendChild(grid); turn.appendChild(wrap); logBottom();
|
|
@@ -591,6 +591,7 @@
|
|
|
591
591
|
background:#fff;border:1px solid var(--rk-border);border-radius:var(--rk-r-full);padding:4px 11px;text-decoration:none;line-height:1.4;
|
|
592
592
|
transition:background var(--rk-dur) var(--rk-ease),border-color var(--rk-dur) var(--rk-ease)}
|
|
593
593
|
.rk-aichat-preview-act:hover{background:var(--rk-gold-tint);border-color:var(--rk-gold-600)}
|
|
594
|
+
.rk-aichat-preview-act[hidden]{display:none}
|
|
594
595
|
|
|
595
596
|
/* ── markdown pipe tables (assistant messages + .md previews) ── */
|
|
596
597
|
.rk-md-tablewrap{max-width:100%;overflow-x:auto;margin:8px 0;border:1px solid var(--rk-border);border-radius:var(--rk-r-lg)}
|
|
@@ -622,6 +623,14 @@
|
|
|
622
623
|
.rk-aichat-file-thumb--markdown,.rk-aichat-file-thumb--text{background:var(--rk-n-50)}.rk-aichat-file-thumb--audio{background:var(--rk-gold-tint2)}
|
|
623
624
|
.rk-aichat-file-play{position:absolute;inset:0;display:grid;place-items:center;font-size:22px;color:#fff;text-shadow:0 1px 6px rgba(0,0,0,.55);pointer-events:none}
|
|
624
625
|
.rk-aichat-file-name{font-size:11px;font-weight:600;color:var(--rk-n-600);line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:112px}
|
|
626
|
+
/* "Edit in the tool" pencil — only on image/video cards; opens /tools/image or
|
|
627
|
+
/tools/video in a new tab with this file preloaded as the working file. */
|
|
628
|
+
.rk-aichat-file-open{position:absolute;top:5px;right:5px;width:24px;height:24px;border-radius:var(--rk-r-md);
|
|
629
|
+
display:grid;place-items:center;background:rgba(255,255,255,.94);border:1px solid var(--rk-border);color:var(--rk-ink);
|
|
630
|
+
opacity:0;transition:opacity var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease);z-index:2}
|
|
631
|
+
.rk-aichat-file-open svg{width:13px;height:13px}
|
|
632
|
+
.rk-aichat-file:hover .rk-aichat-file-open,.rk-aichat-file-open:focus-visible{opacity:1}
|
|
633
|
+
.rk-aichat-file-open:hover{background:var(--rk-gold-tint)}
|
|
625
634
|
|
|
626
635
|
/* create_video pipeline: live status row + clickable result links */
|
|
627
636
|
.rk-aichat-flow{display:flex;align-items:center;gap:9px;font-size:12.5px;color:var(--rk-text-muted);padding:2px 2px}
|
|
@@ -2674,6 +2683,34 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
2674
2683
|
}
|
|
2675
2684
|
function fileIcon(kind) { return kind === 'pdf' ? '📕' : kind === 'json' ? '{ }' : kind === 'markdown' ? '📝' : kind === 'audio' ? '🎵' : kind === 'text' ? '📄' : '📄'; }
|
|
2676
2685
|
|
|
2686
|
+
// ── "edit this media" hand-off: dock chat → the matching creative tool ──
|
|
2687
|
+
// Images open /tools/image, videos /tools/video, each with the file preloaded as
|
|
2688
|
+
// that tool's working file (its chat then edits THAT media). Deliberately a fresh
|
|
2689
|
+
// tool session — no ?tracer — so the tool names its own chat from the first prompt.
|
|
2690
|
+
var PENCIL_SVG = '<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="M13.6 3.6a1.9 1.9 0 0 1 2.8 2.8L7.7 15.1l-3.6.8.8-3.6z"/></svg>';
|
|
2691
|
+
function toolEditUrl(file) {
|
|
2692
|
+
if (!file || !file.viewUrl) return '';
|
|
2693
|
+
var k = fileKind(file.name, file.contentType);
|
|
2694
|
+
var name = file.name || '';
|
|
2695
|
+
if (k === 'image') return '/tools/image?source_image_url=' + encodeURIComponent(file.viewUrl) + (name ? '&source_image_name=' + encodeURIComponent(name) : '');
|
|
2696
|
+
if (k === 'video') return '/tools/video?source_video_url=' + encodeURIComponent(file.viewUrl) + (name ? '&source_video_name=' + encodeURIComponent(name) : '');
|
|
2697
|
+
return '';
|
|
2698
|
+
}
|
|
2699
|
+
function toolEditLink(file, className) {
|
|
2700
|
+
var href = toolEditUrl(file);
|
|
2701
|
+
if (!href) return null;
|
|
2702
|
+
var a = document.createElement('a');
|
|
2703
|
+
a.className = className;
|
|
2704
|
+
a.href = href;
|
|
2705
|
+
a.target = '_blank';
|
|
2706
|
+
a.rel = 'noopener noreferrer';
|
|
2707
|
+
a.title = 'Edit in the ' + (fileKind(file.name, file.contentType) === 'video' ? 'video' : 'image') + ' tool';
|
|
2708
|
+
a.setAttribute('aria-label', a.title);
|
|
2709
|
+
// The card itself opens the lightbox; the pencil must not also trigger it.
|
|
2710
|
+
a.addEventListener('click', function (e) { e.stopPropagation(); });
|
|
2711
|
+
return a;
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2677
2714
|
// Compact markdown → HTML for .md previews (headings, lists, quotes, rules,
|
|
2678
2715
|
// fenced + inline code, bold/italic/links). Escapes first, then injects tags
|
|
2679
2716
|
// (same order as linkify). Backticks are built via char codes to avoid
|
|
@@ -2734,7 +2771,9 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
2734
2771
|
var dlBtn = document.createElement('a'); dlBtn.className = 'rk-aichat-preview-act'; dlBtn.target = '_blank'; dlBtn.rel = 'noopener noreferrer'; dlBtn.textContent = 'Download';
|
|
2735
2772
|
var linkBtn = document.createElement('button'); linkBtn.type = 'button'; linkBtn.className = 'rk-aichat-preview-act'; linkBtn.textContent = 'Copy link';
|
|
2736
2773
|
var xBtn = document.createElement('button'); xBtn.type = 'button'; xBtn.className = 'rk-aichat-modal-x'; xBtn.setAttribute('aria-label', 'Close'); xBtn.innerHTML = '×';
|
|
2737
|
-
|
|
2774
|
+
// Same hand-off as the card pencil, spelled out as a labelled action here.
|
|
2775
|
+
var editBtn = document.createElement('a'); editBtn.className = 'rk-aichat-preview-act'; editBtn.target = '_blank'; editBtn.rel = 'noopener noreferrer'; editBtn.textContent = 'Edit';
|
|
2776
|
+
head.appendChild(nameEl); head.appendChild(editBtn); head.appendChild(copyBtn); head.appendChild(dlBtn); head.appendChild(linkBtn); head.appendChild(xBtn);
|
|
2738
2777
|
function flashAct(btn, label) { var o = btn.getAttribute('data-label') || btn.textContent; btn.setAttribute('data-label', o); btn.textContent = label; setTimeout(function () { btn.textContent = o; }, 1400); }
|
|
2739
2778
|
// Copy = file text (json/md/txt) or its URL for binaries. Copy link = the
|
|
2740
2779
|
// durable public S3 URL. Download opens in a new tab when the browser can't
|
|
@@ -2783,6 +2822,9 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
2783
2822
|
var it = items[idx];
|
|
2784
2823
|
nameEl.textContent = it.name || 'file'; nameEl.title = it.name || '';
|
|
2785
2824
|
dlBtn.href = it.viewUrl; dlBtn.setAttribute('download', it.name || '');
|
|
2825
|
+
var editHref = toolEditUrl(it);
|
|
2826
|
+
editBtn.hidden = !editHref;
|
|
2827
|
+
if (editHref) { editBtn.href = editHref; editBtn.title = 'Edit in the ' + (fileKind(it.name, it.contentType) === 'video' ? 'video' : 'image') + ' tool'; }
|
|
2786
2828
|
linkBtn.style.display = it.publicUrl ? '' : 'none';
|
|
2787
2829
|
body.innerHTML = '';
|
|
2788
2830
|
var kind = fileKind(it.name, it.contentType);
|
|
@@ -2837,7 +2879,15 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
2837
2879
|
var files = [], seen = {};
|
|
2838
2880
|
var arts = (job && job.artifacts) || [];
|
|
2839
2881
|
for (var i = 0; i < arts.length; i++) { var f = rkFileFromArtifact(arts[i]); if (f && !seen[f.viewUrl]) { seen[f.viewUrl] = true; files.push(f); } }
|
|
2840
|
-
|
|
2882
|
+
// Name the direct result URL after the FILE, not the tracer — fileKind() reads the
|
|
2883
|
+
// extension, and a tracer-named entry ("my-poster") classifies as 'other', losing
|
|
2884
|
+
// the image/video thumbnail and the edit-in-tool hand-off.
|
|
2885
|
+
var mu = rkResultMediaUrl(job);
|
|
2886
|
+
if (mu && !seen[mu]) {
|
|
2887
|
+
seen[mu] = true;
|
|
2888
|
+
var seg = String(mu).split('?')[0].split('/').pop() || '';
|
|
2889
|
+
files.push({ name: (/.[a-z0-9]{2,5}$/i.test(seg) ? seg : (job.tracer || 'output')), contentType: '', viewUrl: mu, publicUrl: mu });
|
|
2890
|
+
}
|
|
2841
2891
|
var res = job && job.result;
|
|
2842
2892
|
if (res && typeof res === 'object' && !Array.isArray(res) && Object.keys(res).length) {
|
|
2843
2893
|
try { var blob = new Blob([JSON.stringify(res, null, 2)], { type: 'application/json' }); files.push({ name: (job.tracer || 'result') + '.json', contentType: 'application/json', viewUrl: URL.createObjectURL(blob), publicUrl: null }); } catch (e) {}
|
|
@@ -2890,14 +2940,21 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
2890
2940
|
var grid = document.createElement('div'); grid.className = 'rk-aichat-files-grid';
|
|
2891
2941
|
list.forEach(function (f, ix) {
|
|
2892
2942
|
var k = fileKind(f.name, f.contentType);
|
|
2893
|
-
|
|
2943
|
+
// A div, not a button: media cards carry a nested "edit in tool" link, and
|
|
2944
|
+
// an anchor inside a button is invalid (and unclickable in some browsers).
|
|
2945
|
+
var card = document.createElement('div'); card.className = 'rk-aichat-file'; card.tabIndex = 0; card.setAttribute('role', 'button');
|
|
2894
2946
|
var thumb = document.createElement('div'); thumb.className = 'rk-aichat-file-thumb rk-aichat-file-thumb--' + k;
|
|
2895
2947
|
if (k === 'image') { var im = document.createElement('img'); im.src = f.viewUrl; im.alt = ''; im.loading = 'lazy'; thumb.appendChild(im); }
|
|
2896
2948
|
else if (k === 'video') { var v = document.createElement('video'); v.src = f.viewUrl; v.muted = true; v.preload = 'metadata'; v.playsInline = true; thumb.appendChild(v); var pl = document.createElement('span'); pl.className = 'rk-aichat-file-play'; pl.textContent = '▶'; thumb.appendChild(pl); }
|
|
2897
2949
|
else { var ic = document.createElement('span'); ic.className = 'rk-aichat-file-ic'; ic.textContent = fileIcon(k); thumb.appendChild(ic); }
|
|
2950
|
+
var edit = toolEditLink(f, 'rk-aichat-file-open');
|
|
2951
|
+
if (edit) { edit.innerHTML = PENCIL_SVG; thumb.appendChild(edit); }
|
|
2898
2952
|
var nm = document.createElement('div'); nm.className = 'rk-aichat-file-name'; nm.textContent = f.name || 'file'; nm.title = f.name || '';
|
|
2899
2953
|
card.appendChild(thumb); card.appendChild(nm);
|
|
2900
2954
|
card.addEventListener('click', function () { openFilePreview(list, ix); });
|
|
2955
|
+
card.addEventListener('keydown', function (e) {
|
|
2956
|
+
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openFilePreview(list, ix); }
|
|
2957
|
+
});
|
|
2901
2958
|
grid.appendChild(card);
|
|
2902
2959
|
});
|
|
2903
2960
|
wrap.appendChild(grid); turn.appendChild(wrap); logBottom();
|