@officexapp/vidfarm-devcli 0.21.14 → 0.21.16
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-director/SKILL.md +19 -4
- package/.agents/skills/vidfarm-director/references/assets-and-sourcing.md +6 -0
- package/.agents/skills/vidfarm-director/references/automation-and-local-dev.md +19 -4
- package/.agents/skills/vidfarm-director/references/core-workflows.md +2 -2
- package/.agents/skills/vidfarm-director/references/editor-workflows.md +29 -2
- package/SKILL.director.md +75 -12
- package/demo/dist/app.js +207 -207
- package/dist/src/cli.js +217 -10
- package/dist/src/devcli/cost-mode.js +22 -7
- package/dist/src/hyperframes/composition.js +2 -2
- package/dist/src/services/clip-curation/hunt.js +5 -4
- package/dist/src/services/clip-curation/media-select.js +76 -30
- package/package.json +1 -1
- package/public/serve-shells/editor.html +15 -5
- package/public/serve-shells/library-files.html +87 -3
- package/public/serve-shells/library-raws.html +222 -16
- package/public/serve-shells/tools-clipper.html +73 -4
- package/public/serve-shells/tools-image.html +172 -5
- package/public/serve-shells/tools-video.html +102 -4
|
@@ -382,7 +382,10 @@
|
|
|
382
382
|
/* chat column */
|
|
383
383
|
.rk-aichat-main{flex:1;min-width:0;display:flex;flex-direction:column}
|
|
384
384
|
.rk-aichat-head{display:flex;align-items:center;gap:8px;padding:14px 14px;border-bottom:1px solid var(--rk-border);flex:none}
|
|
385
|
-
.rk-aichat-head-main{flex:1;min-width:0;display:
|
|
385
|
+
.rk-aichat-head-main{flex:1;min-width:0;display:flex;align-items:center;gap:10px}
|
|
386
|
+
.rk-aichat-head-titles{display:grid;gap:1px;min-width:0}
|
|
387
|
+
.rk-aichat-watch{display:inline-flex;align-items:center;gap:5px;border:0;background:transparent;color:var(--rk-text-muted);font-size:12px;font-weight:400;font-family:inherit;cursor:pointer;padding:3px 8px;border-radius:8px;white-space:nowrap;transition:color .15s,background .15s}
|
|
388
|
+
.rk-aichat-watch:hover{color:var(--rk-ink);background:var(--rk-n-100)}
|
|
386
389
|
.rk-aichat-title{font-family:var(--rk-font-display);font-weight:800;font-size:15px;color:var(--rk-ink);letter-spacing:-.01em}
|
|
387
390
|
.rk-aichat-sub{font-size:11.5px;color:var(--rk-text-muted)}
|
|
388
391
|
.rk-aichat-back{flex:1;min-width:0;display:inline-flex;align-items:center;gap:8px;padding:7px 12px 7px 9px;
|
|
@@ -1367,6 +1370,25 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
1367
1370
|
.rk-clips-dialog-note[data-tone="error"]{background:#fdecea;color:#8d3b2f}
|
|
1368
1371
|
.rk-clips-dialog-note[data-tone="success"]{background:#e8f7ee;color:#1f7a44}
|
|
1369
1372
|
.rk-clips-dialog-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:4px}
|
|
1373
|
+
/* Import Raws: file-upload dropzone (mirrors discover) + folder picker trigger. */
|
|
1374
|
+
.rk-add-optional{font-weight:500;color:var(--rk-text-faint)}
|
|
1375
|
+
.rk-add-or{display:flex;align-items:center;gap:12px;color:var(--rk-text-faint);font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.08em}
|
|
1376
|
+
.rk-add-or::before,.rk-add-or::after{content:"";height:1px;flex:1;background:var(--rk-border)}
|
|
1377
|
+
.rk-add-file{display:flex;align-items:center;gap:12px;border:1px dashed var(--rk-border-strong);border-radius:var(--rk-r-lg);padding:13px 14px;cursor:pointer;color:var(--rk-n-700);transition:border-color var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease)}
|
|
1378
|
+
.rk-add-file:hover{border-color:var(--rk-gold-600);background:var(--rk-gold-tint)}
|
|
1379
|
+
.rk-add-file input[type=file]{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
|
|
1380
|
+
.rk-add-file-ic{flex:none;color:var(--rk-gold-700);display:grid;place-items:center}
|
|
1381
|
+
.rk-add-file-text{font-size:13.5px;font-weight:600;display:grid;gap:2px;min-width:0}
|
|
1382
|
+
.rk-add-file-text .rk-add-optional{font-size:11.5px;font-weight:500}
|
|
1383
|
+
.rk-add-file.is-picked{border-style:solid;border-color:var(--rk-gold-600);background:var(--rk-gold-tint)}
|
|
1384
|
+
.rk-folderpick{display:flex;align-items:stretch;gap:8px}
|
|
1385
|
+
.rk-folderpick-btn{flex:1;min-width:0;display:flex;align-items:center;gap:9px;text-align:left;cursor:pointer;color:var(--rk-text-faint);font-weight:500}
|
|
1386
|
+
.rk-folderpick-btn:hover{border-color:var(--rk-gold-600)}
|
|
1387
|
+
.rk-folderpick-btn.is-picked{color:var(--rk-ink);font-weight:600}
|
|
1388
|
+
.rk-folderpick-ic{flex:none;color:var(--rk-gold-700);display:grid;place-items:center}
|
|
1389
|
+
.rk-folderpick-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1390
|
+
.rk-folderpick-clear{flex:none;width:38px;border:1px solid var(--rk-border);border-radius:var(--rk-r-lg);background:var(--rk-n-50);color:var(--rk-n-700);cursor:pointer;font-size:13px;line-height:1;transition:border-color var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease)}
|
|
1391
|
+
.rk-folderpick-clear:hover{border-color:var(--rk-gold-600);background:var(--rk-gold-tint)}
|
|
1370
1392
|
/* ── files tab: full-page mount of the shared directory-explorer component ──
|
|
1371
1393
|
The component (.rk-dir + .rk-aichat-* rows) is tuned for the narrow chat
|
|
1372
1394
|
drawer; on a full page we wrap it in a card and scale the search, crumbs and
|
|
@@ -1537,8 +1559,13 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
1537
1559
|
<header class="rk-aichat-head">
|
|
1538
1560
|
<span class="rk-brand-mark">V</span>
|
|
1539
1561
|
<div class="rk-aichat-head-main">
|
|
1540
|
-
<div class="rk-aichat-
|
|
1541
|
-
|
|
1562
|
+
<div class="rk-aichat-head-titles">
|
|
1563
|
+
<div class="rk-aichat-title">VidFarm AI</div>
|
|
1564
|
+
<div class="rk-aichat-sub">on your own AI keys</div>
|
|
1565
|
+
</div>
|
|
1566
|
+
<button class="rk-aichat-watch" id="rkWatchPoppanel" type="button" title="Watch Tutorial" aria-label="Watch Tutorial">
|
|
1567
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="M10 8.5l6 3.5-6 3.5z" fill="currentColor" stroke="none"></path></svg><span>Watch Tutorial</span>
|
|
1568
|
+
</button>
|
|
1542
1569
|
</div>
|
|
1543
1570
|
<button class="rk-aichat-tool" id="rkHistoryToggle" type="button" title="Chat history" aria-label="Toggle chat history" aria-pressed="false">
|
|
1544
1571
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l3 2"/></svg>
|
|
@@ -3507,6 +3534,63 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
3507
3534
|
document.querySelectorAll("[data-skill-menu]").forEach((panel) => { panel.hidden = true; });
|
|
3508
3535
|
});
|
|
3509
3536
|
})();</script>
|
|
3537
|
+
<script>window.__vidfarmTutorials=[["/discover/templates/categories","/assets/blog/tutorial-videos/page-categories.mp4"],["/discover/templates/swipe","/assets/blog/tutorial-videos/page-swipe.mp4"],["/discover","/assets/blog/tutorial-videos/page-discover.mp4"],["/tools/image","/assets/blog/tutorial-videos/tools-image.mp4"],["/tools/video","/assets/blog/tutorial-videos/tools-video.mp4"],["/tools/clipper","/assets/blog/tutorial-videos/tools-clipper.mp4"],["/chat","/assets/blog/tutorial-videos/page-chat.mp4"],["/calendar","/assets/blog/tutorial-videos/page-calendar.mp4"],["/settings/profile","/assets/blog/tutorial-videos/page-profile.mp4"],["/settings/wallet","/assets/blog/tutorial-videos/page-wallet.mp4"],["/settings/channels","/assets/blog/tutorial-videos/page-channels.mp4"],["/settings/developer","/assets/blog/tutorial-videos/page-developer.mp4"],["/library","/assets/blog/tutorial-videos/page-library.mp4"]];window.__vfPoppanelVideo="/assets/blog/tutorial-videos/page-poppanel.mp4";</script>
|
|
3538
|
+
<script>(() => {
|
|
3539
|
+
try {
|
|
3540
|
+
function findTitle() {
|
|
3541
|
+
var hs = document.querySelectorAll('.rk-content h1');
|
|
3542
|
+
for (var i = 0; i < hs.length; i++) { var t = (hs[i].textContent || '').trim(); if (t && t.toLowerCase() !== 'vidfarm') return hs[i]; }
|
|
3543
|
+
return null;
|
|
3544
|
+
}
|
|
3545
|
+
function openModal(url) {
|
|
3546
|
+
if (document.getElementById('vf-tut-modal')) return;
|
|
3547
|
+
var ov = document.createElement('div');
|
|
3548
|
+
ov.id = 'vf-tut-modal';
|
|
3549
|
+
ov.style.cssText = 'position:fixed;inset:0;z-index:2147483000;background:rgba(0,0,0,.72);display:flex;align-items:center;justify-content:center;padding:24px;';
|
|
3550
|
+
var box = document.createElement('div');
|
|
3551
|
+
box.style.cssText = 'position:relative;width:min(900px,92vw);';
|
|
3552
|
+
var vid = document.createElement('video');
|
|
3553
|
+
vid.src = url; vid.controls = true; vid.autoplay = true; vid.playsInline = true;
|
|
3554
|
+
vid.style.cssText = 'width:100%;max-height:86vh;border-radius:14px;box-shadow:0 24px 80px rgba(0,0,0,.5);background:#000;display:block;';
|
|
3555
|
+
var x = document.createElement('button');
|
|
3556
|
+
x.type = 'button'; x.setAttribute('aria-label', 'Close'); x.innerHTML = '×';
|
|
3557
|
+
x.style.cssText = 'position:absolute;top:-16px;right:-16px;width:40px;height:40px;border-radius:50%;border:0;background:#fff;color:#111;font-size:26px;line-height:1;cursor:pointer;box-shadow:0 4px 16px rgba(0,0,0,.3);display:flex;align-items:center;justify-content:center;';
|
|
3558
|
+
function close() { try { vid.pause(); } catch (e) {} ov.remove(); document.removeEventListener('keydown', onKey); }
|
|
3559
|
+
function onKey(e) { if (e.key === 'Escape') close(); }
|
|
3560
|
+
x.onclick = close;
|
|
3561
|
+
ov.onclick = function (e) { if (e.target === ov) close(); };
|
|
3562
|
+
document.addEventListener('keydown', onKey);
|
|
3563
|
+
box.appendChild(vid); box.appendChild(x); ov.appendChild(box); document.body.appendChild(ov);
|
|
3564
|
+
}
|
|
3565
|
+
window.__vfOpenTutorial = openModal;
|
|
3566
|
+
// Pop-panel (agent dock) tutorial button — present on every full-chrome page,
|
|
3567
|
+
// next to the "VidFarm AI" title. Retry briefly in case the dock mounts late.
|
|
3568
|
+
var pt = 0;
|
|
3569
|
+
(function wirePop() {
|
|
3570
|
+
var pv = document.getElementById('rkWatchPoppanel');
|
|
3571
|
+
if (pv && window.__vfPoppanelVideo) { pv.onclick = function () { openModal(window.__vfPoppanelVideo); }; return; }
|
|
3572
|
+
if (pt++ < 20) setTimeout(wirePop, 300);
|
|
3573
|
+
})();
|
|
3574
|
+
// Page-title tutorial button — only when this route has a matching video.
|
|
3575
|
+
var routes = window.__vidfarmTutorials || [];
|
|
3576
|
+
var path = location.pathname, hit = null;
|
|
3577
|
+
for (var i = 0; i < routes.length; i++) { if (path === routes[i][0] || path.indexOf(routes[i][0]) === 0) { hit = routes[i][1]; break; } }
|
|
3578
|
+
function inject() {
|
|
3579
|
+
if (document.getElementById('vf-watch-tut')) return true;
|
|
3580
|
+
var h1 = findTitle(); if (!h1) return false;
|
|
3581
|
+
var btn = document.createElement('button');
|
|
3582
|
+
btn.id = 'vf-watch-tut'; btn.type = 'button'; btn.setAttribute('aria-label', 'Watch Tutorial');
|
|
3583
|
+
btn.style.cssText = 'display:inline-flex;align-items:center;gap:6px;margin-left:16px;padding:4px 10px;border:0;background:transparent;color:#9aa0a6;font-size:13px;font-weight:400;font-family:inherit;letter-spacing:0;cursor:pointer;vertical-align:middle;border-radius:8px;transition:color .15s,background .15s;';
|
|
3584
|
+
btn.innerHTML = '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="M10 8.5l6 3.5-6 3.5z" fill="currentColor" stroke="none"></path></svg><span>Watch Tutorial</span>';
|
|
3585
|
+
btn.onmouseenter = function () { btn.style.color = '#3c4043'; btn.style.background = 'rgba(0,0,0,.05)'; };
|
|
3586
|
+
btn.onmouseleave = function () { btn.style.color = '#9aa0a6'; btn.style.background = 'transparent'; };
|
|
3587
|
+
btn.onclick = function () { openModal(hit); };
|
|
3588
|
+
h1.appendChild(btn);
|
|
3589
|
+
return true;
|
|
3590
|
+
}
|
|
3591
|
+
if (hit) { var tries = 0; (function loop() { if (inject()) return; if (tries++ > 40) return; setTimeout(loop, 250); })(); }
|
|
3592
|
+
} catch (e) {}
|
|
3593
|
+
})();</script>
|
|
3510
3594
|
<script type="module" src="/assets/file-directory-app.js"></script>
|
|
3511
3595
|
</body>
|
|
3512
3596
|
</html>
|
|
@@ -382,7 +382,10 @@
|
|
|
382
382
|
/* chat column */
|
|
383
383
|
.rk-aichat-main{flex:1;min-width:0;display:flex;flex-direction:column}
|
|
384
384
|
.rk-aichat-head{display:flex;align-items:center;gap:8px;padding:14px 14px;border-bottom:1px solid var(--rk-border);flex:none}
|
|
385
|
-
.rk-aichat-head-main{flex:1;min-width:0;display:
|
|
385
|
+
.rk-aichat-head-main{flex:1;min-width:0;display:flex;align-items:center;gap:10px}
|
|
386
|
+
.rk-aichat-head-titles{display:grid;gap:1px;min-width:0}
|
|
387
|
+
.rk-aichat-watch{display:inline-flex;align-items:center;gap:5px;border:0;background:transparent;color:var(--rk-text-muted);font-size:12px;font-weight:400;font-family:inherit;cursor:pointer;padding:3px 8px;border-radius:8px;white-space:nowrap;transition:color .15s,background .15s}
|
|
388
|
+
.rk-aichat-watch:hover{color:var(--rk-ink);background:var(--rk-n-100)}
|
|
386
389
|
.rk-aichat-title{font-family:var(--rk-font-display);font-weight:800;font-size:15px;color:var(--rk-ink);letter-spacing:-.01em}
|
|
387
390
|
.rk-aichat-sub{font-size:11.5px;color:var(--rk-text-muted)}
|
|
388
391
|
.rk-aichat-back{flex:1;min-width:0;display:inline-flex;align-items:center;gap:8px;padding:7px 12px 7px 9px;
|
|
@@ -1367,6 +1370,25 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
1367
1370
|
.rk-clips-dialog-note[data-tone="error"]{background:#fdecea;color:#8d3b2f}
|
|
1368
1371
|
.rk-clips-dialog-note[data-tone="success"]{background:#e8f7ee;color:#1f7a44}
|
|
1369
1372
|
.rk-clips-dialog-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:4px}
|
|
1373
|
+
/* Import Raws: file-upload dropzone (mirrors discover) + folder picker trigger. */
|
|
1374
|
+
.rk-add-optional{font-weight:500;color:var(--rk-text-faint)}
|
|
1375
|
+
.rk-add-or{display:flex;align-items:center;gap:12px;color:var(--rk-text-faint);font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.08em}
|
|
1376
|
+
.rk-add-or::before,.rk-add-or::after{content:"";height:1px;flex:1;background:var(--rk-border)}
|
|
1377
|
+
.rk-add-file{display:flex;align-items:center;gap:12px;border:1px dashed var(--rk-border-strong);border-radius:var(--rk-r-lg);padding:13px 14px;cursor:pointer;color:var(--rk-n-700);transition:border-color var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease)}
|
|
1378
|
+
.rk-add-file:hover{border-color:var(--rk-gold-600);background:var(--rk-gold-tint)}
|
|
1379
|
+
.rk-add-file input[type=file]{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
|
|
1380
|
+
.rk-add-file-ic{flex:none;color:var(--rk-gold-700);display:grid;place-items:center}
|
|
1381
|
+
.rk-add-file-text{font-size:13.5px;font-weight:600;display:grid;gap:2px;min-width:0}
|
|
1382
|
+
.rk-add-file-text .rk-add-optional{font-size:11.5px;font-weight:500}
|
|
1383
|
+
.rk-add-file.is-picked{border-style:solid;border-color:var(--rk-gold-600);background:var(--rk-gold-tint)}
|
|
1384
|
+
.rk-folderpick{display:flex;align-items:stretch;gap:8px}
|
|
1385
|
+
.rk-folderpick-btn{flex:1;min-width:0;display:flex;align-items:center;gap:9px;text-align:left;cursor:pointer;color:var(--rk-text-faint);font-weight:500}
|
|
1386
|
+
.rk-folderpick-btn:hover{border-color:var(--rk-gold-600)}
|
|
1387
|
+
.rk-folderpick-btn.is-picked{color:var(--rk-ink);font-weight:600}
|
|
1388
|
+
.rk-folderpick-ic{flex:none;color:var(--rk-gold-700);display:grid;place-items:center}
|
|
1389
|
+
.rk-folderpick-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1390
|
+
.rk-folderpick-clear{flex:none;width:38px;border:1px solid var(--rk-border);border-radius:var(--rk-r-lg);background:var(--rk-n-50);color:var(--rk-n-700);cursor:pointer;font-size:13px;line-height:1;transition:border-color var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease)}
|
|
1391
|
+
.rk-folderpick-clear:hover{border-color:var(--rk-gold-600);background:var(--rk-gold-tint)}
|
|
1370
1392
|
/* ── files tab: full-page mount of the shared directory-explorer component ──
|
|
1371
1393
|
The component (.rk-dir + .rk-aichat-* rows) is tuned for the narrow chat
|
|
1372
1394
|
drawer; on a full page we wrap it in a card and scale the search, crumbs and
|
|
@@ -1532,12 +1554,30 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
1532
1554
|
<div class="rk-card-head">
|
|
1533
1555
|
<span class="rk-eyebrow">Import source</span>
|
|
1534
1556
|
<h2>Import Raws</h2>
|
|
1535
|
-
<p class="rk-muted">Paste a video URL — TikTok, YouTube, Instagram, X, or a direct link. We'll open the Clipper to
|
|
1557
|
+
<p class="rk-muted">Paste a video URL — TikTok, YouTube, Instagram, X, or a direct link — or upload a video file. We'll import it (or open the Clipper to trim a subrange) into your raws.</p>
|
|
1536
1558
|
</div>
|
|
1537
1559
|
<div class="rk-field">
|
|
1538
1560
|
<label class="rk-label" for="rkScanUrl">Video URL</label>
|
|
1539
1561
|
<input class="rk-input" id="rkScanUrl" name="url" type="url" placeholder="https://youtube.com/watch?v=… or https://…/video.mp4" autocomplete="off" />
|
|
1540
1562
|
</div>
|
|
1563
|
+
<div class="rk-add-or"><span>or</span></div>
|
|
1564
|
+
<label class="rk-add-file" data-rk-scan-filelabel>
|
|
1565
|
+
<input type="file" accept="video/*,.mp4,.mov,.webm,.m4v" data-rk-scan-file />
|
|
1566
|
+
<span class="rk-add-file-ic"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 16V4"/><path d="m7 9 5-5 5 5"/><path d="M4 15v3a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-3"/></svg></span>
|
|
1567
|
+
<span class="rk-add-file-text" data-rk-scan-filetext>Upload a video<span class="rk-add-optional">MP4 / MOV / WebM, up to 100 MB</span></span>
|
|
1568
|
+
</label>
|
|
1569
|
+
<div class="rk-field">
|
|
1570
|
+
<label class="rk-label">Save to folder <span class="rk-hint">optional</span></label>
|
|
1571
|
+
<div class="rk-folderpick">
|
|
1572
|
+
<button type="button" class="rk-input rk-folderpick-btn" id="rkScanFolderBtn">
|
|
1573
|
+
<span class="rk-folderpick-ic" aria-hidden="true"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2Z"/></svg></span>
|
|
1574
|
+
<span class="rk-folderpick-label" id="rkScanFolderLabel">Auto-named from the video</span>
|
|
1575
|
+
</button>
|
|
1576
|
+
<button type="button" class="rk-folderpick-clear" id="rkScanFolderClear" aria-label="Clear folder" hidden>✕</button>
|
|
1577
|
+
</div>
|
|
1578
|
+
<input type="hidden" id="rkScanFolder" name="folder_path" value="" />
|
|
1579
|
+
<span class="rk-hint">Choose an existing Raws folder or create one. Blank = auto-named from the video.</span>
|
|
1580
|
+
</div>
|
|
1541
1581
|
<div class="rk-field">
|
|
1542
1582
|
<label class="rk-label" for="rkScanTracer">Tracer <span class="rk-hint">optional</span></label>
|
|
1543
1583
|
<input class="rk-input" id="rkScanTracer" name="tracer" type="text" placeholder="my-campaign-001" autocomplete="off" />
|
|
@@ -1649,8 +1689,13 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
1649
1689
|
<header class="rk-aichat-head">
|
|
1650
1690
|
<span class="rk-brand-mark">V</span>
|
|
1651
1691
|
<div class="rk-aichat-head-main">
|
|
1652
|
-
<div class="rk-aichat-
|
|
1653
|
-
|
|
1692
|
+
<div class="rk-aichat-head-titles">
|
|
1693
|
+
<div class="rk-aichat-title">VidFarm AI</div>
|
|
1694
|
+
<div class="rk-aichat-sub">on your own AI keys</div>
|
|
1695
|
+
</div>
|
|
1696
|
+
<button class="rk-aichat-watch" id="rkWatchPoppanel" type="button" title="Watch Tutorial" aria-label="Watch Tutorial">
|
|
1697
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="M10 8.5l6 3.5-6 3.5z" fill="currentColor" stroke="none"></path></svg><span>Watch Tutorial</span>
|
|
1698
|
+
</button>
|
|
1654
1699
|
</div>
|
|
1655
1700
|
<button class="rk-aichat-tool" id="rkHistoryToggle" type="button" title="Chat history" aria-label="Toggle chat history" aria-pressed="false">
|
|
1656
1701
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l3 2"/></svg>
|
|
@@ -2201,6 +2246,19 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
2201
2246
|
});
|
|
2202
2247
|
// Copy a raw's canonical route path — handy to paste into the /editor Insert
|
|
2203
2248
|
// Media panel. Works from the visible chip AND the kebab "Copy path" item.
|
|
2249
|
+
// NOTE: this raws panel is its own IIFE — it can't see the files-panel copyText,
|
|
2250
|
+
// so define a local one here (falls back to execCommand if the async API rejects,
|
|
2251
|
+
// e.g. when the document isn't focused).
|
|
2252
|
+
function copyText(txt,ok){
|
|
2253
|
+
function fb(){ try{ var ta=document.createElement('textarea'); ta.value=txt; ta.style.position='fixed'; ta.style.opacity='0'; document.body.appendChild(ta); ta.focus(); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); }catch(e){} }
|
|
2254
|
+
try{
|
|
2255
|
+
if(navigator.clipboard&&navigator.clipboard.writeText){
|
|
2256
|
+
navigator.clipboard.writeText(txt).then(function(){ if(ok)ok(); },function(){ fb(); if(ok)ok(); });
|
|
2257
|
+
return;
|
|
2258
|
+
}
|
|
2259
|
+
}catch(e){}
|
|
2260
|
+
fb(); if(ok)ok();
|
|
2261
|
+
}
|
|
2204
2262
|
document.addEventListener('click',function(e){
|
|
2205
2263
|
var cp=e.target&&e.target.closest?e.target.closest('[data-raw-copy-path]'):null;
|
|
2206
2264
|
if(!cp) return;
|
|
@@ -2410,7 +2468,84 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
2410
2468
|
var scanPrompt=root.getElementById('rkScanPrompt');
|
|
2411
2469
|
var scanProvider=root.getElementById('rkScanProvider');
|
|
2412
2470
|
var scanTracer=root.getElementById('rkScanTracer');
|
|
2471
|
+
var scanFolder=root.getElementById('rkScanFolder');
|
|
2472
|
+
var scanFolderBtn=root.getElementById('rkScanFolderBtn');
|
|
2473
|
+
var scanFolderLabel=root.getElementById('rkScanFolderLabel');
|
|
2474
|
+
var scanFolderClear=root.getElementById('rkScanFolderClear');
|
|
2475
|
+
var scanFile=root.querySelector('[data-rk-scan-file]');
|
|
2476
|
+
var scanFileLabel=root.querySelector('[data-rk-scan-filelabel]');
|
|
2477
|
+
var scanFileText=root.querySelector('[data-rk-scan-filetext]');
|
|
2478
|
+
var defaultScanFileText=scanFileText?scanFileText.innerHTML:'';
|
|
2479
|
+
var MAX_TEMP_UPLOAD=100*1024*1024;
|
|
2413
2480
|
var scanNote=root.getElementById('rkScanNote');
|
|
2481
|
+
function selectedScanFile(){ return scanFile&&scanFile.files&&scanFile.files[0]?scanFile.files[0]:null; }
|
|
2482
|
+
function syncScanFileUi(){
|
|
2483
|
+
var file=selectedScanFile();
|
|
2484
|
+
if(scanFileLabel)scanFileLabel.classList.toggle('is-picked',!!file);
|
|
2485
|
+
if(scanFileText)scanFileText.innerHTML=file?esc(file.name):defaultScanFileText;
|
|
2486
|
+
}
|
|
2487
|
+
function resetScanUpload(){ if(scanFile)scanFile.value=''; syncScanFileUi(); }
|
|
2488
|
+
// Presign → upload → finalize a picked video into the user's temp store, then
|
|
2489
|
+
// hand /raws/scan its temp_file_id (which the route resolves to an S3 key).
|
|
2490
|
+
function uploadScanTempFile(file){
|
|
2491
|
+
return fetch('/api/v1/user/me/temporary-files/presign',{method:'POST',credentials:'same-origin',headers:{'content-type':'application/json',Accept:'application/json'},
|
|
2492
|
+
body:JSON.stringify({ file_name:file.name, content_type:file.type||null, size_bytes:file.size, folder_path:'raws/_inputs' })})
|
|
2493
|
+
.then(function(r){ return r.json().catch(function(){ return {}; }).then(function(d){ return {ok:r.ok,data:d}; }); })
|
|
2494
|
+
.then(function(res){
|
|
2495
|
+
if(!res.ok||!res.data||!res.data.transport) throw new Error((res.data&&res.data.error)||'Unable to prepare upload.');
|
|
2496
|
+
var p=res.data;
|
|
2497
|
+
if(p.transport==='server'){
|
|
2498
|
+
var form=new FormData();
|
|
2499
|
+
form.append((p.upload&&p.upload.form_field)||'file',file,file.name);
|
|
2500
|
+
form.append('folder_path','raws/_inputs');
|
|
2501
|
+
return fetch((p.upload&&p.upload.url)||'/api/v1/user/me/temporary-files/upload',{method:(p.upload&&p.upload.method)||'POST',credentials:'same-origin',body:form})
|
|
2502
|
+
.then(function(r){ return r.json().catch(function(){ return {}; }).then(function(d){ return {ok:r.ok,data:d}; }); })
|
|
2503
|
+
.then(function(done){ var tf=done&&done.data&&done.data.file; var id=tf&&(tf.id||tf.fileId||tf.file_id); if(!done.ok||!id) throw new Error((done.data&&done.data.error)||'Unable to upload video.'); return id; });
|
|
2504
|
+
}
|
|
2505
|
+
return fetch(p.upload.url,{method:p.upload.method,headers:p.upload.headers,body:file})
|
|
2506
|
+
.then(function(r){ if(!r.ok) throw new Error('Upload failed with status '+r.status+'.');
|
|
2507
|
+
return fetch('/api/v1/user/me/temporary-files',{method:'POST',credentials:'same-origin',headers:{'content-type':'application/json',Accept:'application/json'},
|
|
2508
|
+
body:JSON.stringify({ file_id:p.file_id, file_name:p.file_name, content_type:p.content_type, size_bytes:file.size, storage_key:p.storage_key, folder_path:p.folder_path||'raws/_inputs' })}); })
|
|
2509
|
+
.then(function(r){ return r.json().catch(function(){ return {}; }).then(function(d){ return {ok:r.ok,data:d}; }); })
|
|
2510
|
+
.then(function(done){ var tf=done&&done.data&&done.data.file; var id=tf&&(tf.id||tf.fileId||tf.file_id); if(!done.ok||!id) throw new Error((done.data&&done.data.error)||'Unable to save uploaded video.'); return id; });
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
// "Save to folder" uses the shared file-directory pop-panel (window.__vidfarmDirectory)
|
|
2514
|
+
// scoped to the Raws root — the same picker as "Add to folder…", with create-new-folder
|
|
2515
|
+
// inside. The chosen folder's relative folderPath is stashed on the hidden #rkScanFolder
|
|
2516
|
+
// input (name=folder_path); its full /raws/… path shows on the trigger button.
|
|
2517
|
+
function setScanFolder(folderPath, displayPath){
|
|
2518
|
+
if(scanFolder)scanFolder.value=folderPath||'';
|
|
2519
|
+
if(scanFolderLabel)scanFolderLabel.textContent=folderPath?(displayPath||('/raws/'+folderPath)):'Auto-named from the video';
|
|
2520
|
+
if(scanFolderBtn)scanFolderBtn.classList.toggle('is-picked',!!folderPath);
|
|
2521
|
+
if(scanFolderClear)scanFolderClear.hidden=!folderPath;
|
|
2522
|
+
}
|
|
2523
|
+
function openScanFolderPicker(){
|
|
2524
|
+
var dir=window.__vidfarmDirectory;
|
|
2525
|
+
if(!dir||typeof dir.pick!=='function'){
|
|
2526
|
+
// Explorer not mounted — degrade to a prompt so the field still works.
|
|
2527
|
+
var typed=window.prompt('Save these raws to which folder? (leave blank to auto-name)', (scanFolder&&scanFolder.value)||'');
|
|
2528
|
+
if(typed!=null)setScanFolder((typed||'').trim().replace(/^\/+|\/+$/g,''));
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2531
|
+
// The Import dialog is a top-layer <dialog> (showModal); the picker overlay is
|
|
2532
|
+
// z-index only, so it would render BEHIND the dialog. Close the dialog while
|
|
2533
|
+
// picking, then reopen it — the form's inputs persist across close/reopen.
|
|
2534
|
+
var reopen=function(){ try{ if(dialog&&typeof dialog.showModal==='function'&&!dialog.open)dialog.showModal(); }catch(e){} };
|
|
2535
|
+
try{ if(dialog&&dialog.open)dialog.close(); }catch(e){}
|
|
2536
|
+
dir.pick({
|
|
2537
|
+
select:'folder',
|
|
2538
|
+
roots:['raws'],
|
|
2539
|
+
initialPath:(scanFolder&&scanFolder.value)?('/raws/'+scanFolder.value):'/raws',
|
|
2540
|
+
title:'Save raws to folder',
|
|
2541
|
+
hint:'Pick a Raws folder (or create a new one), then choose “Save here”.',
|
|
2542
|
+
confirmLabel:'Save here',
|
|
2543
|
+
onPick:function(folder){ setScanFolder((folder&&folder.folderPath)||'', folder&&folder.path); reopen(); },
|
|
2544
|
+
onCancel:reopen
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2547
|
+
if(scanFolderBtn)scanFolderBtn.addEventListener('click',openScanFolderPicker);
|
|
2548
|
+
if(scanFolderClear)scanFolderClear.addEventListener('click',function(){ setScanFolder(''); });
|
|
2414
2549
|
var scanSubmit=root.getElementById('rkClipsScanSubmit');
|
|
2415
2550
|
var scanClip=root.getElementById('rkScanClip');
|
|
2416
2551
|
var scanAudioOnly=root.getElementById('rkScanAudioOnly');
|
|
@@ -2455,18 +2590,21 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
2455
2590
|
.then(function(r){ return r.ok?r.json():null; })
|
|
2456
2591
|
.then(function(d){ if(d){ SCAN_OPTS=d; applyScanOpts(); } }).catch(function(){});
|
|
2457
2592
|
}
|
|
2593
|
+
var FILE_IMPORT_NOTE='Uploaded videos import straight into your raws library. Turn on Clip raws with AI to auto-mine matching moments instead.';
|
|
2458
2594
|
function syncClipMode(){
|
|
2459
2595
|
var on=!!(scanClip&&scanClip.checked);
|
|
2596
|
+
var file=selectedScanFile();
|
|
2460
2597
|
if(scanAdvanced)scanAdvanced.hidden=!on;
|
|
2461
|
-
if(scanSubmit)scanSubmit.innerHTML=(on?'Start scan':'Open clipper')+' <span class="rk-arrow">→</span>';
|
|
2598
|
+
if(scanSubmit)scanSubmit.innerHTML=(on?'Start scan':(file?'Import video':'Open clipper'))+' <span class="rk-arrow">→</span>';
|
|
2462
2599
|
if(on&&!clipReady()){
|
|
2463
2600
|
setNote('No AI provider available — add a Gemini, OpenAI, or OpenRouter key in Settings to clip raws (or uncheck to import the whole video).','error');
|
|
2464
2601
|
} else {
|
|
2465
|
-
setNote(on?CLIP_NOTE:IMPORT_NOTE);
|
|
2602
|
+
setNote(on?CLIP_NOTE:(file?FILE_IMPORT_NOTE:IMPORT_NOTE));
|
|
2466
2603
|
}
|
|
2467
2604
|
}
|
|
2468
2605
|
if(scanProvider)scanProvider.addEventListener('change',function(){ scanProviderTouched=true; });
|
|
2469
2606
|
if(scanClip)scanClip.addEventListener('change',syncClipMode);
|
|
2607
|
+
if(scanFile)scanFile.addEventListener('change',function(){ syncScanFileUi(); syncClipMode(); });
|
|
2470
2608
|
if(dialog){ openBtns.forEach(function(openBtn){ openBtn.addEventListener('click',function(){ if(!SCAN_OPTS)loadScanOpts(); syncClipMode(); if(typeof dialog.showModal==='function'&&!dialog.open)dialog.showModal(); }); }); }
|
|
2471
2609
|
if(cancelBtn&&dialog){ cancelBtn.addEventListener('click',function(){ if(dialog.open)dialog.close(); }); }
|
|
2472
2610
|
if(dialog){
|
|
@@ -2479,33 +2617,43 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
2479
2617
|
scanForm.addEventListener('submit',function(e){
|
|
2480
2618
|
e.preventDefault();
|
|
2481
2619
|
var url=(scanUrl&&scanUrl.value||'').trim();
|
|
2620
|
+
var file=selectedScanFile();
|
|
2482
2621
|
var tracer=(scanTracer&&scanTracer.value||'').trim();
|
|
2622
|
+
var folder=(scanFolder&&scanFolder.value||'').trim();
|
|
2483
2623
|
var clip=!!(scanClip&&scanClip.checked);
|
|
2484
2624
|
var prompt=clip?(scanPrompt&&scanPrompt.value||'').trim():'';
|
|
2485
2625
|
var provider=clip?(scanProvider&&scanProvider.value||'').trim():'';
|
|
2486
2626
|
var audioOnly=clip&&!!(scanAudioOnly&&scanAudioOnly.checked);
|
|
2487
|
-
if(!url){ setNote('Enter a video URL.','error'); return; }
|
|
2488
|
-
|
|
2489
|
-
//
|
|
2490
|
-
|
|
2627
|
+
if(!file&&!url){ setNote('Enter a video URL or upload a video file.','error'); return; }
|
|
2628
|
+
if(file&&file.size>MAX_TEMP_UPLOAD){ setNote('Video uploads can be at most 100 MB.','error'); return; }
|
|
2629
|
+
// Whole-video (non-AI) URL path opens the Clipper so the user can pick an
|
|
2630
|
+
// exact subrange (or save the whole thing) with a live video preview. An
|
|
2631
|
+
// uploaded FILE has no previewable URL, so it imports directly instead.
|
|
2632
|
+
if(url&&!file&&!clip){
|
|
2491
2633
|
var clp=new URL('/tools/clipper', window.location.origin);
|
|
2492
2634
|
clp.searchParams.set('source_url', url);
|
|
2493
2635
|
if(tracer) clp.searchParams.set('tracer', tracer);
|
|
2636
|
+
if(folder) clp.searchParams.set('folder_path', folder);
|
|
2494
2637
|
window.location.href=clp.pathname+clp.search;
|
|
2495
2638
|
return;
|
|
2496
2639
|
}
|
|
2497
|
-
setNote('Starting scan…');
|
|
2640
|
+
setNote(file?'Uploading…':'Starting scan…');
|
|
2498
2641
|
if(scanSubmit){ scanSubmit.disabled=true; scanSubmit.classList.add('is-loading'); }
|
|
2499
2642
|
|
|
2500
|
-
var payload={
|
|
2643
|
+
var payload={};
|
|
2644
|
+
if(url&&!file)payload.source_url=url;
|
|
2501
2645
|
if(clip){ payload.prompt=prompt; if(provider)payload.provider=provider; if(audioOnly)payload.audio_only=true; } else { payload.import_only=true; }
|
|
2502
2646
|
if(tracer)payload.tracer=tracer;
|
|
2647
|
+
if(folder)payload.folder_path=folder;
|
|
2503
2648
|
|
|
2504
2649
|
// Fire the request now; hold it and only wire the result AFTER the modal
|
|
2505
2650
|
// closes and the optimistic card lands (so a fast reply can't race ahead
|
|
2506
|
-
// of its own placeholder).
|
|
2651
|
+
// of its own placeholder). An uploaded file is presigned+staged first, then
|
|
2652
|
+
// its temp_file_id rides the same /raws/scan body.
|
|
2507
2653
|
var id='imp'+Date.now()+'-'+Math.floor(Math.random()*1e6);
|
|
2508
|
-
var
|
|
2654
|
+
var prep=file?uploadScanTempFile(file).then(function(tempFileId){ payload.temp_file_id=tempFileId; }):Promise.resolve();
|
|
2655
|
+
var req=prep
|
|
2656
|
+
.then(function(){ return fetch('/raws/scan',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json',Accept:'application/json'},body:JSON.stringify(payload)}); })
|
|
2509
2657
|
.then(function(r){ return r.json().then(function(d){ return {ok:r.ok,data:d}; }); });
|
|
2510
2658
|
|
|
2511
2659
|
// brief spinner beat, then close and optimistically drop an "importing…" card
|
|
@@ -2513,12 +2661,13 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
2513
2661
|
if(scanSubmit){ scanSubmit.disabled=false; scanSubmit.classList.remove('is-loading'); }
|
|
2514
2662
|
if(dialog&&dialog.open)dialog.close();
|
|
2515
2663
|
if(scanForm.reset)scanForm.reset();
|
|
2664
|
+
resetScanUpload();
|
|
2516
2665
|
syncClipMode();
|
|
2517
|
-
PENDING.unshift({id:id,url:url,tracer:tracer,clip:clip,label:clip?'Starting scan…':'Importing…'});
|
|
2666
|
+
PENDING.unshift({id:id,url:url||(file&&file.name)||'',tracer:tracer,clip:clip,label:clip?'Starting scan…':(file?'Uploading…':'Importing…')});
|
|
2518
2667
|
if(query)query.value='';
|
|
2519
2668
|
paintRoot(); // surface the optimistic "importing…" card at the directory root
|
|
2520
2669
|
if(statusEl)statusEl.textContent=(clip?'Mining a source…':'Importing a video…')+(tracer?' (tracer '+tracer+')':'');
|
|
2521
|
-
req.then(function(res){ onScanResult(id,clip,res); }).catch(function(){ failImport(id,'
|
|
2670
|
+
req.then(function(res){ onScanResult(id,clip,res); }).catch(function(){ failImport(id,'Upload or scan failed — please try again.'); });
|
|
2522
2671
|
},650);
|
|
2523
2672
|
});
|
|
2524
2673
|
}
|
|
@@ -4461,6 +4610,63 @@ button.rk-clips-thumb{appearance:none;-webkit-appearance:none;border:0;margin:0;
|
|
|
4461
4610
|
document.querySelectorAll("[data-skill-menu]").forEach((panel) => { panel.hidden = true; });
|
|
4462
4611
|
});
|
|
4463
4612
|
})();</script>
|
|
4613
|
+
<script>window.__vidfarmTutorials=[["/discover/templates/categories","/assets/blog/tutorial-videos/page-categories.mp4"],["/discover/templates/swipe","/assets/blog/tutorial-videos/page-swipe.mp4"],["/discover","/assets/blog/tutorial-videos/page-discover.mp4"],["/tools/image","/assets/blog/tutorial-videos/tools-image.mp4"],["/tools/video","/assets/blog/tutorial-videos/tools-video.mp4"],["/tools/clipper","/assets/blog/tutorial-videos/tools-clipper.mp4"],["/chat","/assets/blog/tutorial-videos/page-chat.mp4"],["/calendar","/assets/blog/tutorial-videos/page-calendar.mp4"],["/settings/profile","/assets/blog/tutorial-videos/page-profile.mp4"],["/settings/wallet","/assets/blog/tutorial-videos/page-wallet.mp4"],["/settings/channels","/assets/blog/tutorial-videos/page-channels.mp4"],["/settings/developer","/assets/blog/tutorial-videos/page-developer.mp4"],["/library","/assets/blog/tutorial-videos/page-library.mp4"]];window.__vfPoppanelVideo="/assets/blog/tutorial-videos/page-poppanel.mp4";</script>
|
|
4614
|
+
<script>(() => {
|
|
4615
|
+
try {
|
|
4616
|
+
function findTitle() {
|
|
4617
|
+
var hs = document.querySelectorAll('.rk-content h1');
|
|
4618
|
+
for (var i = 0; i < hs.length; i++) { var t = (hs[i].textContent || '').trim(); if (t && t.toLowerCase() !== 'vidfarm') return hs[i]; }
|
|
4619
|
+
return null;
|
|
4620
|
+
}
|
|
4621
|
+
function openModal(url) {
|
|
4622
|
+
if (document.getElementById('vf-tut-modal')) return;
|
|
4623
|
+
var ov = document.createElement('div');
|
|
4624
|
+
ov.id = 'vf-tut-modal';
|
|
4625
|
+
ov.style.cssText = 'position:fixed;inset:0;z-index:2147483000;background:rgba(0,0,0,.72);display:flex;align-items:center;justify-content:center;padding:24px;';
|
|
4626
|
+
var box = document.createElement('div');
|
|
4627
|
+
box.style.cssText = 'position:relative;width:min(900px,92vw);';
|
|
4628
|
+
var vid = document.createElement('video');
|
|
4629
|
+
vid.src = url; vid.controls = true; vid.autoplay = true; vid.playsInline = true;
|
|
4630
|
+
vid.style.cssText = 'width:100%;max-height:86vh;border-radius:14px;box-shadow:0 24px 80px rgba(0,0,0,.5);background:#000;display:block;';
|
|
4631
|
+
var x = document.createElement('button');
|
|
4632
|
+
x.type = 'button'; x.setAttribute('aria-label', 'Close'); x.innerHTML = '×';
|
|
4633
|
+
x.style.cssText = 'position:absolute;top:-16px;right:-16px;width:40px;height:40px;border-radius:50%;border:0;background:#fff;color:#111;font-size:26px;line-height:1;cursor:pointer;box-shadow:0 4px 16px rgba(0,0,0,.3);display:flex;align-items:center;justify-content:center;';
|
|
4634
|
+
function close() { try { vid.pause(); } catch (e) {} ov.remove(); document.removeEventListener('keydown', onKey); }
|
|
4635
|
+
function onKey(e) { if (e.key === 'Escape') close(); }
|
|
4636
|
+
x.onclick = close;
|
|
4637
|
+
ov.onclick = function (e) { if (e.target === ov) close(); };
|
|
4638
|
+
document.addEventListener('keydown', onKey);
|
|
4639
|
+
box.appendChild(vid); box.appendChild(x); ov.appendChild(box); document.body.appendChild(ov);
|
|
4640
|
+
}
|
|
4641
|
+
window.__vfOpenTutorial = openModal;
|
|
4642
|
+
// Pop-panel (agent dock) tutorial button — present on every full-chrome page,
|
|
4643
|
+
// next to the "VidFarm AI" title. Retry briefly in case the dock mounts late.
|
|
4644
|
+
var pt = 0;
|
|
4645
|
+
(function wirePop() {
|
|
4646
|
+
var pv = document.getElementById('rkWatchPoppanel');
|
|
4647
|
+
if (pv && window.__vfPoppanelVideo) { pv.onclick = function () { openModal(window.__vfPoppanelVideo); }; return; }
|
|
4648
|
+
if (pt++ < 20) setTimeout(wirePop, 300);
|
|
4649
|
+
})();
|
|
4650
|
+
// Page-title tutorial button — only when this route has a matching video.
|
|
4651
|
+
var routes = window.__vidfarmTutorials || [];
|
|
4652
|
+
var path = location.pathname, hit = null;
|
|
4653
|
+
for (var i = 0; i < routes.length; i++) { if (path === routes[i][0] || path.indexOf(routes[i][0]) === 0) { hit = routes[i][1]; break; } }
|
|
4654
|
+
function inject() {
|
|
4655
|
+
if (document.getElementById('vf-watch-tut')) return true;
|
|
4656
|
+
var h1 = findTitle(); if (!h1) return false;
|
|
4657
|
+
var btn = document.createElement('button');
|
|
4658
|
+
btn.id = 'vf-watch-tut'; btn.type = 'button'; btn.setAttribute('aria-label', 'Watch Tutorial');
|
|
4659
|
+
btn.style.cssText = 'display:inline-flex;align-items:center;gap:6px;margin-left:16px;padding:4px 10px;border:0;background:transparent;color:#9aa0a6;font-size:13px;font-weight:400;font-family:inherit;letter-spacing:0;cursor:pointer;vertical-align:middle;border-radius:8px;transition:color .15s,background .15s;';
|
|
4660
|
+
btn.innerHTML = '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="M10 8.5l6 3.5-6 3.5z" fill="currentColor" stroke="none"></path></svg><span>Watch Tutorial</span>';
|
|
4661
|
+
btn.onmouseenter = function () { btn.style.color = '#3c4043'; btn.style.background = 'rgba(0,0,0,.05)'; };
|
|
4662
|
+
btn.onmouseleave = function () { btn.style.color = '#9aa0a6'; btn.style.background = 'transparent'; };
|
|
4663
|
+
btn.onclick = function () { openModal(hit); };
|
|
4664
|
+
h1.appendChild(btn);
|
|
4665
|
+
return true;
|
|
4666
|
+
}
|
|
4667
|
+
if (hit) { var tries = 0; (function loop() { if (inject()) return; if (tries++ > 40) return; setTimeout(loop, 250); })(); }
|
|
4668
|
+
} catch (e) {}
|
|
4669
|
+
})();</script>
|
|
4464
4670
|
<script type="module" src="/assets/file-directory-app.js"></script>
|
|
4465
4671
|
</body>
|
|
4466
4672
|
</html>
|
|
@@ -382,7 +382,10 @@
|
|
|
382
382
|
/* chat column */
|
|
383
383
|
.rk-aichat-main{flex:1;min-width:0;display:flex;flex-direction:column}
|
|
384
384
|
.rk-aichat-head{display:flex;align-items:center;gap:8px;padding:14px 14px;border-bottom:1px solid var(--rk-border);flex:none}
|
|
385
|
-
.rk-aichat-head-main{flex:1;min-width:0;display:
|
|
385
|
+
.rk-aichat-head-main{flex:1;min-width:0;display:flex;align-items:center;gap:10px}
|
|
386
|
+
.rk-aichat-head-titles{display:grid;gap:1px;min-width:0}
|
|
387
|
+
.rk-aichat-watch{display:inline-flex;align-items:center;gap:5px;border:0;background:transparent;color:var(--rk-text-muted);font-size:12px;font-weight:400;font-family:inherit;cursor:pointer;padding:3px 8px;border-radius:8px;white-space:nowrap;transition:color .15s,background .15s}
|
|
388
|
+
.rk-aichat-watch:hover{color:var(--rk-ink);background:var(--rk-n-100)}
|
|
386
389
|
.rk-aichat-title{font-family:var(--rk-font-display);font-weight:800;font-size:15px;color:var(--rk-ink);letter-spacing:-.01em}
|
|
387
390
|
.rk-aichat-sub{font-size:11.5px;color:var(--rk-text-muted)}
|
|
388
391
|
.rk-aichat-back{flex:1;min-width:0;display:inline-flex;align-items:center;gap:8px;padding:7px 12px 7px 9px;
|
|
@@ -1521,8 +1524,13 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
1521
1524
|
<header class="rk-aichat-head">
|
|
1522
1525
|
<span class="rk-brand-mark">V</span>
|
|
1523
1526
|
<div class="rk-aichat-head-main">
|
|
1524
|
-
<div class="rk-aichat-
|
|
1525
|
-
|
|
1527
|
+
<div class="rk-aichat-head-titles">
|
|
1528
|
+
<div class="rk-aichat-title">VidFarm AI</div>
|
|
1529
|
+
<div class="rk-aichat-sub">on your own AI keys</div>
|
|
1530
|
+
</div>
|
|
1531
|
+
<button class="rk-aichat-watch" id="rkWatchPoppanel" type="button" title="Watch Tutorial" aria-label="Watch Tutorial">
|
|
1532
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="M10 8.5l6 3.5-6 3.5z" fill="currentColor" stroke="none"></path></svg><span>Watch Tutorial</span>
|
|
1533
|
+
</button>
|
|
1526
1534
|
</div>
|
|
1527
1535
|
<button class="rk-aichat-tool" id="rkHistoryToggle" type="button" title="Chat history" aria-label="Toggle chat history" aria-pressed="false">
|
|
1528
1536
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l3 2"/></svg>
|
|
@@ -2099,10 +2107,14 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
2099
2107
|
|
|
2100
2108
|
// ── URL param preload ──
|
|
2101
2109
|
function firstParam(names){ var p=new URLSearchParams(window.location.search); for(var i=0;i<names.length;i++){ var v=p.get(names[i]); if(v && v.trim()) return v.trim(); } return ""; }
|
|
2102
|
-
function clearUrlParams(){ var u=new URL(window.location.href); ["source_url","source_video_url","video_url","url","source_video_name","video_name","file_name","tracer","tracer_id","trace"].forEach(function(k){ u.searchParams.delete(k); }); window.history.replaceState({}, "", u.toString()); }
|
|
2110
|
+
function clearUrlParams(){ var u=new URL(window.location.href); ["source_url","source_video_url","video_url","url","source_video_name","video_name","file_name","tracer","tracer_id","trace","folder_path","folder"].forEach(function(k){ u.searchParams.delete(k); }); window.history.replaceState({}, "", u.toString()); }
|
|
2103
2111
|
function initFromUrl(){
|
|
2104
2112
|
var t = firstParam(["tracer","tracer_id","trace"]);
|
|
2105
2113
|
if(t){ sessionTracer=t; tracerAutoPending=false; if(tracerInput) tracerInput.value=t; if(folderInput && !folderInput.value) folderInput.value=slug(t); }
|
|
2114
|
+
// An explicit target folder (forwarded from the Import Raws modal) wins over
|
|
2115
|
+
// the tracer-derived default so the whole-video import lands where asked.
|
|
2116
|
+
var f = firstParam(["folder_path","folder"]);
|
|
2117
|
+
if(f && folderInput){ folderInput.value=slug(f); }
|
|
2106
2118
|
var src = firstParam(["source_url","source_video_url","video_url","url"]);
|
|
2107
2119
|
if(src){ if(urlInput) urlInput.value = src; loadSourceUrl(src, firstParam(["source_video_name","video_name","file_name"])); }
|
|
2108
2120
|
if(t) loadSession();
|
|
@@ -4048,6 +4060,63 @@ body.vf-has-topbanner.rk-has-sidebar .rk-content{padding-top:var(--vf-topbanner-
|
|
|
4048
4060
|
document.querySelectorAll("[data-skill-menu]").forEach((panel) => { panel.hidden = true; });
|
|
4049
4061
|
});
|
|
4050
4062
|
})();</script>
|
|
4063
|
+
<script>window.__vidfarmTutorials=[["/discover/templates/categories","/assets/blog/tutorial-videos/page-categories.mp4"],["/discover/templates/swipe","/assets/blog/tutorial-videos/page-swipe.mp4"],["/discover","/assets/blog/tutorial-videos/page-discover.mp4"],["/tools/image","/assets/blog/tutorial-videos/tools-image.mp4"],["/tools/video","/assets/blog/tutorial-videos/tools-video.mp4"],["/tools/clipper","/assets/blog/tutorial-videos/tools-clipper.mp4"],["/chat","/assets/blog/tutorial-videos/page-chat.mp4"],["/calendar","/assets/blog/tutorial-videos/page-calendar.mp4"],["/settings/profile","/assets/blog/tutorial-videos/page-profile.mp4"],["/settings/wallet","/assets/blog/tutorial-videos/page-wallet.mp4"],["/settings/channels","/assets/blog/tutorial-videos/page-channels.mp4"],["/settings/developer","/assets/blog/tutorial-videos/page-developer.mp4"],["/library","/assets/blog/tutorial-videos/page-library.mp4"]];window.__vfPoppanelVideo="/assets/blog/tutorial-videos/page-poppanel.mp4";</script>
|
|
4064
|
+
<script>(() => {
|
|
4065
|
+
try {
|
|
4066
|
+
function findTitle() {
|
|
4067
|
+
var hs = document.querySelectorAll('.rk-content h1');
|
|
4068
|
+
for (var i = 0; i < hs.length; i++) { var t = (hs[i].textContent || '').trim(); if (t && t.toLowerCase() !== 'vidfarm') return hs[i]; }
|
|
4069
|
+
return null;
|
|
4070
|
+
}
|
|
4071
|
+
function openModal(url) {
|
|
4072
|
+
if (document.getElementById('vf-tut-modal')) return;
|
|
4073
|
+
var ov = document.createElement('div');
|
|
4074
|
+
ov.id = 'vf-tut-modal';
|
|
4075
|
+
ov.style.cssText = 'position:fixed;inset:0;z-index:2147483000;background:rgba(0,0,0,.72);display:flex;align-items:center;justify-content:center;padding:24px;';
|
|
4076
|
+
var box = document.createElement('div');
|
|
4077
|
+
box.style.cssText = 'position:relative;width:min(900px,92vw);';
|
|
4078
|
+
var vid = document.createElement('video');
|
|
4079
|
+
vid.src = url; vid.controls = true; vid.autoplay = true; vid.playsInline = true;
|
|
4080
|
+
vid.style.cssText = 'width:100%;max-height:86vh;border-radius:14px;box-shadow:0 24px 80px rgba(0,0,0,.5);background:#000;display:block;';
|
|
4081
|
+
var x = document.createElement('button');
|
|
4082
|
+
x.type = 'button'; x.setAttribute('aria-label', 'Close'); x.innerHTML = '×';
|
|
4083
|
+
x.style.cssText = 'position:absolute;top:-16px;right:-16px;width:40px;height:40px;border-radius:50%;border:0;background:#fff;color:#111;font-size:26px;line-height:1;cursor:pointer;box-shadow:0 4px 16px rgba(0,0,0,.3);display:flex;align-items:center;justify-content:center;';
|
|
4084
|
+
function close() { try { vid.pause(); } catch (e) {} ov.remove(); document.removeEventListener('keydown', onKey); }
|
|
4085
|
+
function onKey(e) { if (e.key === 'Escape') close(); }
|
|
4086
|
+
x.onclick = close;
|
|
4087
|
+
ov.onclick = function (e) { if (e.target === ov) close(); };
|
|
4088
|
+
document.addEventListener('keydown', onKey);
|
|
4089
|
+
box.appendChild(vid); box.appendChild(x); ov.appendChild(box); document.body.appendChild(ov);
|
|
4090
|
+
}
|
|
4091
|
+
window.__vfOpenTutorial = openModal;
|
|
4092
|
+
// Pop-panel (agent dock) tutorial button — present on every full-chrome page,
|
|
4093
|
+
// next to the "VidFarm AI" title. Retry briefly in case the dock mounts late.
|
|
4094
|
+
var pt = 0;
|
|
4095
|
+
(function wirePop() {
|
|
4096
|
+
var pv = document.getElementById('rkWatchPoppanel');
|
|
4097
|
+
if (pv && window.__vfPoppanelVideo) { pv.onclick = function () { openModal(window.__vfPoppanelVideo); }; return; }
|
|
4098
|
+
if (pt++ < 20) setTimeout(wirePop, 300);
|
|
4099
|
+
})();
|
|
4100
|
+
// Page-title tutorial button — only when this route has a matching video.
|
|
4101
|
+
var routes = window.__vidfarmTutorials || [];
|
|
4102
|
+
var path = location.pathname, hit = null;
|
|
4103
|
+
for (var i = 0; i < routes.length; i++) { if (path === routes[i][0] || path.indexOf(routes[i][0]) === 0) { hit = routes[i][1]; break; } }
|
|
4104
|
+
function inject() {
|
|
4105
|
+
if (document.getElementById('vf-watch-tut')) return true;
|
|
4106
|
+
var h1 = findTitle(); if (!h1) return false;
|
|
4107
|
+
var btn = document.createElement('button');
|
|
4108
|
+
btn.id = 'vf-watch-tut'; btn.type = 'button'; btn.setAttribute('aria-label', 'Watch Tutorial');
|
|
4109
|
+
btn.style.cssText = 'display:inline-flex;align-items:center;gap:6px;margin-left:16px;padding:4px 10px;border:0;background:transparent;color:#9aa0a6;font-size:13px;font-weight:400;font-family:inherit;letter-spacing:0;cursor:pointer;vertical-align:middle;border-radius:8px;transition:color .15s,background .15s;';
|
|
4110
|
+
btn.innerHTML = '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="M10 8.5l6 3.5-6 3.5z" fill="currentColor" stroke="none"></path></svg><span>Watch Tutorial</span>';
|
|
4111
|
+
btn.onmouseenter = function () { btn.style.color = '#3c4043'; btn.style.background = 'rgba(0,0,0,.05)'; };
|
|
4112
|
+
btn.onmouseleave = function () { btn.style.color = '#9aa0a6'; btn.style.background = 'transparent'; };
|
|
4113
|
+
btn.onclick = function () { openModal(hit); };
|
|
4114
|
+
h1.appendChild(btn);
|
|
4115
|
+
return true;
|
|
4116
|
+
}
|
|
4117
|
+
if (hit) { var tries = 0; (function loop() { if (inject()) return; if (tries++ > 40) return; setTimeout(loop, 250); })(); }
|
|
4118
|
+
} catch (e) {}
|
|
4119
|
+
})();</script>
|
|
4051
4120
|
<script type="module" src="/assets/file-directory-app.js"></script>
|
|
4052
4121
|
</body>
|
|
4053
4122
|
</html>
|