@mevdragon/vidfarm-devcli 0.18.1 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/editor-capabilities/SKILL.md +166 -0
- package/.agents/skills/editor-capabilities/references/re-theme-walkthrough.md +58 -0
- package/.agents/skills/vidfarm-media/SKILL.md +2 -0
- package/SKILL.director.md +174 -16
- package/SKILL.platform.md +7 -3
- package/demo/dist/app.css +1 -1
- package/demo/dist/app.js +79 -75
- package/dist/src/account-pages-legacy.js +1 -1
- package/dist/src/app.js +1694 -217
- package/dist/src/cli.js +1365 -105
- package/dist/src/config.js +8 -0
- package/dist/src/devcli/clips.js +3 -0
- package/dist/src/devcli/composition-edit.js +401 -10
- package/dist/src/devcli/local-backend.js +123 -0
- package/dist/src/devcli/migrate-local.js +140 -0
- package/dist/src/devcli/sync.js +311 -0
- package/dist/src/devcli/timeline-edit.js +208 -1
- package/dist/src/editor-chat.js +37 -18
- package/dist/src/frontend/file-directory.js +271 -20
- package/dist/src/frontend/homepage-view.js +3 -3
- package/dist/src/frontend/template-editor-chat.js +6 -3
- package/dist/src/page-shell.js +1 -1
- package/dist/src/primitive-context.js +31 -2
- package/dist/src/primitive-registry.js +409 -4
- package/dist/src/reskin/chat-page.js +103 -15
- package/dist/src/reskin/discover-page.js +247 -10
- package/dist/src/reskin/document.js +147 -10
- package/dist/src/reskin/inpaint-clipper-page.js +649 -0
- package/dist/src/reskin/inpaint-page.js +2459 -452
- package/dist/src/reskin/inpaint-video-page.js +1339 -0
- package/dist/src/reskin/library-page.js +324 -82
- package/dist/src/reskin/portfolio-page.js +687 -0
- package/dist/src/reskin/theme.js +36 -11
- package/dist/src/services/billing.js +4 -0
- package/dist/src/services/clip-curation/hunt.js +2 -0
- package/dist/src/services/clip-records.js +28 -0
- package/dist/src/services/file-directory.js +6 -3
- package/dist/src/services/hyperframes.js +535 -86
- package/dist/src/services/serverless-jobs.js +3 -1
- package/dist/src/services/serverless-records.js +43 -0
- package/dist/src/services/storage.js +24 -2
- package/dist/src/template-editor-pages.js +1 -1
- package/package.json +1 -1
- package/public/assets/file-directory-app.js +2 -2
- package/public/assets/homepage-client-app.js +1 -1
- package/public/assets/page-runtime-client-app.js +2 -2
- package/public/assets/placeholders/scene-placeholder.png +0 -0
|
@@ -152,7 +152,12 @@ function projectCard(post, accountId, index) {
|
|
|
152
152
|
</div>
|
|
153
153
|
</div>
|
|
154
154
|
</div>`;
|
|
155
|
-
|
|
155
|
+
// Folder identity for the client-side folder view: the real folder_path if the
|
|
156
|
+
// post has one, else its tracer (a synthetic group that materializes a folder
|
|
157
|
+
// when renamed). data-poster fuels the folder-tile mosaic.
|
|
158
|
+
const folder = (post.folder_path || "").replace(/^\/+|\/+$/g, "");
|
|
159
|
+
const posterUrl = (post.media.find((a) => isVideoAsset(a)) || post.media.find((a) => isImageAsset(a)) || primaryAsset(post))?.url || "";
|
|
160
|
+
return `<article class="rk-library-card rk-card-hover" data-status="${rkEscape(post.status)}" data-title="${rkEscape(searchHay)}" data-post-id="${rkEscape(post.post_id)}" data-folder="${rkEscape(folder)}" data-tracer="${rkEscape(post.tracer || "")}" data-poster="${rkEscape(posterUrl)}">
|
|
156
161
|
<button type="button" class="rk-library-poster" data-rk-view="${index}" aria-label="Play ${rkEscape(title)}">
|
|
157
162
|
${posterMedia(post)}
|
|
158
163
|
<span class="rk-pill ${s.pill} rk-library-pstatus"><span class="rk-library-dot"></span>${rkEscape(s.short)}</span>
|
|
@@ -216,6 +221,10 @@ function approvedPanel(input, tabs) {
|
|
|
216
221
|
${LOGS_BTN}
|
|
217
222
|
</div>
|
|
218
223
|
|
|
224
|
+
<div class="rk-raws-bar rk-library-foldbar" data-rk-approved-bar data-rk-account="${rkEscape(accountId)}" hidden>
|
|
225
|
+
<nav class="rk-raws-crumbs" data-rk-approved-crumbs aria-label="Breadcrumb"></nav>
|
|
226
|
+
</div>
|
|
227
|
+
|
|
219
228
|
<div class="rk-library-grid" data-rk-grid>
|
|
220
229
|
${cards}
|
|
221
230
|
</div>
|
|
@@ -278,10 +287,6 @@ function clipsPanel(tabs) {
|
|
|
278
287
|
<p class="rk-muted">Click <b>Import source</b> to mine a long video into tagged, searchable short clips.</p>
|
|
279
288
|
</div>
|
|
280
289
|
|
|
281
|
-
<div class="rk-raws-hover" id="rkRawsHover" hidden aria-hidden="true">
|
|
282
|
-
<video class="rk-raws-hover-vid" muted loop playsinline preload="none"></video>
|
|
283
|
-
<div class="rk-raws-hover-cap" id="rkRawsHoverCap"></div>
|
|
284
|
-
</div>
|
|
285
290
|
${LIBRARY_VIEWER_HTML}`;
|
|
286
291
|
}
|
|
287
292
|
/* ──────────────────────────────── Files tab ───────────────────────────────── */
|
|
@@ -293,7 +298,13 @@ function clipsPanel(tabs) {
|
|
|
293
298
|
// (/assets/file-directory-app.js) already ships on every full-chrome reskin
|
|
294
299
|
// page and auto-mounts any `[data-rk-directory]`, so no page script is needed;
|
|
295
300
|
// preview routes through the chrome's `window.__rkDirectoryHost.onPreview`.
|
|
296
|
-
function filesPanel(tabs) {
|
|
301
|
+
function filesPanel(tabs, cloudAvailable = false, initialPath) {
|
|
302
|
+
// `?path=` deep-links straight into a folder (e.g. /library/files?path=/approved/swipes).
|
|
303
|
+
// The client's parseDirectoryPath only accepts the known roots, so any junk or
|
|
304
|
+
// traversal falls back to the virtual root "/" — safe to reflect verbatim.
|
|
305
|
+
const initialAttr = initialPath && initialPath.trim()
|
|
306
|
+
? ` data-initial-path="${rkEscape(initialPath.trim())}"`
|
|
307
|
+
: "";
|
|
297
308
|
return `
|
|
298
309
|
<div class="rk-library-topbar">
|
|
299
310
|
${tabs}
|
|
@@ -301,7 +312,7 @@ function filesPanel(tabs) {
|
|
|
301
312
|
</div>
|
|
302
313
|
|
|
303
314
|
<div class="rk-files-explorer">
|
|
304
|
-
<div data-rk-directory data-mode="standalone" data-show-upload="true" data-show-multiselect="true" data-primary-click="preview"></div>
|
|
315
|
+
<div data-rk-directory data-mode="standalone" data-show-upload="true" data-show-multiselect="true" data-primary-click="preview"${cloudAvailable ? ` data-cloud-available="true"` : ""}${initialAttr}></div>
|
|
305
316
|
</div>`;
|
|
306
317
|
}
|
|
307
318
|
const CLIPS_DIALOG = `
|
|
@@ -310,7 +321,7 @@ const CLIPS_DIALOG = `
|
|
|
310
321
|
<div class="rk-card-head">
|
|
311
322
|
<span class="rk-eyebrow">Import source</span>
|
|
312
323
|
<h2>Import a video</h2>
|
|
313
|
-
<p class="rk-muted">Paste a video URL — TikTok, YouTube, Instagram, X, or a direct link. We'll
|
|
324
|
+
<p class="rk-muted">Paste a video URL — TikTok, YouTube, Instagram, X, or a direct link. We'll open the Clipper to preview it and save an exact subrange to your raws.</p>
|
|
314
325
|
</div>
|
|
315
326
|
<div class="rk-field">
|
|
316
327
|
<label class="rk-label" for="rkScanUrl">Video URL</label>
|
|
@@ -322,7 +333,7 @@ const CLIPS_DIALOG = `
|
|
|
322
333
|
</div>
|
|
323
334
|
<label class="rk-clips-toggle" for="rkScanClip">
|
|
324
335
|
<input type="checkbox" id="rkScanClip" name="clip" />
|
|
325
|
-
<span><b>Clip raws</b> — mine this video into matching moments
|
|
336
|
+
<span><b>Clip raws with AI</b> — auto-mine this video into matching moments, instead of hand-picking a subrange in the Clipper.</span>
|
|
326
337
|
</label>
|
|
327
338
|
<div class="rk-clips-advanced" id="rkClipsAdvanced" hidden>
|
|
328
339
|
<div class="rk-field">
|
|
@@ -343,7 +354,7 @@ const CLIPS_DIALOG = `
|
|
|
343
354
|
<p class="rk-hint rk-clips-dialog-note" id="rkScanNote">We'll pull the whole video into your raws library.</p>
|
|
344
355
|
<div class="rk-clips-dialog-actions">
|
|
345
356
|
<button type="button" class="rk-btn rk-btn-ghost" id="rkClipsScanCancel">Cancel</button>
|
|
346
|
-
<button type="submit" class="rk-btn rk-btn-gold" id="rkClipsScanSubmit">
|
|
357
|
+
<button type="submit" class="rk-btn rk-btn-gold" id="rkClipsScanSubmit">Open clipper <span class="rk-arrow">→</span></button>
|
|
347
358
|
</div>
|
|
348
359
|
</form>
|
|
349
360
|
</dialog>`;
|
|
@@ -354,24 +365,15 @@ const APPROVED_SCRIPT = `
|
|
|
354
365
|
var empty=root.querySelector('[data-rk-empty]');
|
|
355
366
|
var search=root.querySelector('[data-rk-search]');
|
|
356
367
|
var grid=root.querySelector('[data-rk-grid]');
|
|
368
|
+
var foldBar=root.querySelector('[data-rk-approved-bar]');
|
|
369
|
+
var crumbsEl=root.querySelector('[data-rk-approved-crumbs]');
|
|
370
|
+
var account=(foldBar&&foldBar.getAttribute('data-rk-account'))||'';
|
|
357
371
|
var cards=Array.prototype.slice.call(root.querySelectorAll('.rk-library-card'));
|
|
358
372
|
var total=cards.length;
|
|
359
373
|
function syncPlaceholder(){
|
|
360
374
|
if(!search)return;
|
|
361
375
|
search.setAttribute('placeholder','Search '+total+' approved post'+(total===1?'':'s')+String.fromCharCode(8230));
|
|
362
376
|
}
|
|
363
|
-
function apply(){
|
|
364
|
-
var q=(search&&search.value||'').trim().toLowerCase();
|
|
365
|
-
var shown=0;
|
|
366
|
-
cards.forEach(function(card){
|
|
367
|
-
if(card.getAttribute('data-removed'))return;
|
|
368
|
-
var visible=!q||(card.getAttribute('data-title')||'').indexOf(q)!==-1;
|
|
369
|
-
card.hidden=!visible;
|
|
370
|
-
if(visible)shown++;
|
|
371
|
-
});
|
|
372
|
-
if(empty)empty.classList.toggle('is-visible',shown===0);
|
|
373
|
-
}
|
|
374
|
-
if(search)search.addEventListener('input',apply);
|
|
375
377
|
// hover-to-play the real MP4 previews on the grid cards
|
|
376
378
|
root.querySelectorAll('video.rk-library-video').forEach(function(v){
|
|
377
379
|
var card=v.closest('.rk-library-card');
|
|
@@ -379,6 +381,146 @@ const APPROVED_SCRIPT = `
|
|
|
379
381
|
card.addEventListener('mouseenter',function(){ if(v.play){var p=v.play(); if(p&&p.catch)p.catch(function(){});} });
|
|
380
382
|
card.addEventListener('mouseleave',function(){ if(v.pause)v.pause(); });
|
|
381
383
|
});
|
|
384
|
+
|
|
385
|
+
/* ── folder view (mirrors the Raws tab): group cards by folder_path, fall back
|
|
386
|
+
to tracer for legacy posts; drill into a folder; search flattens. ─────── */
|
|
387
|
+
function cssUrl(u){ return "'"+String(u||'').replace(/['")\\\\]/g,'').replace(/\\s/g,'%20')+"'"; }
|
|
388
|
+
function activeCards(){ return cards.filter(function(c){ return !c.getAttribute('data-removed'); }); }
|
|
389
|
+
var FOLDERS=[], LOOSE=[], MODE='root', CUR='';
|
|
390
|
+
function metaOf(card){
|
|
391
|
+
var fp=(card.getAttribute('data-folder')||'').replace(/^\\/+|\\/+$/g,'');
|
|
392
|
+
var tr=card.getAttribute('data-tracer')||'';
|
|
393
|
+
var key = fp ? ('fp:'+fp) : (tr ? ('tr:'+tr) : '');
|
|
394
|
+
return { card:card, fp:fp, tr:tr, key:key, poster:card.getAttribute('data-poster')||'', postId:card.getAttribute('data-post-id')||'' };
|
|
395
|
+
}
|
|
396
|
+
function buildFolders(){
|
|
397
|
+
var map={}; FOLDERS=[]; LOOSE=[];
|
|
398
|
+
activeCards().forEach(function(card){
|
|
399
|
+
var m=metaOf(card);
|
|
400
|
+
if(!m.key){ LOOSE.push(m); return; }
|
|
401
|
+
var f=map[m.key];
|
|
402
|
+
if(!f){
|
|
403
|
+
f={ id:m.key, folderPath:m.fp||null, tracer:m.tr, name:(m.fp||m.tr||'Untitled'), items:[] };
|
|
404
|
+
map[m.key]=f; FOLDERS.push(f);
|
|
405
|
+
}
|
|
406
|
+
f.items.push(m);
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
function folderById(id){ for(var i=0;i<FOLDERS.length;i++){ if(FOLDERS[i].id===id)return FOLDERS[i]; } return null; }
|
|
410
|
+
function clearTiles(){ Array.prototype.slice.call(grid.querySelectorAll('[data-approved-tile]')).forEach(function(t){ t.parentNode&&t.parentNode.removeChild(t); }); }
|
|
411
|
+
function folderTileEl(f){
|
|
412
|
+
var thumbs=f.items.filter(function(m){ return m.poster; }).slice(0,4);
|
|
413
|
+
var mosaic = thumbs.length
|
|
414
|
+
? '<span class="rk-raws-folder-mosaic'+(thumbs.length===1?' is-single':'')+'">'+thumbs.map(function(m){ return '<span class="rk-raws-folder-cell" style="background-image:url('+cssUrl(m.poster)+')"></span>'; }).join('')+'</span>'
|
|
415
|
+
: '<span class="rk-raws-folder-mosaic is-empty">'+${JSON.stringify(ICON_FOLDER)}+'</span>';
|
|
416
|
+
var n=f.items.length;
|
|
417
|
+
var canonPath='/approved'+(f.folderPath?'/'+f.folderPath:(f.tracer?'/'+f.tracer:''));
|
|
418
|
+
var html='<div class="rk-raws-folder-wrap" data-approved-tile>'
|
|
419
|
+
+ '<button type="button" class="rk-raws-folder rk-card-hover" data-approved-folder="'+attr(f.id)+'">'
|
|
420
|
+
+ mosaic
|
|
421
|
+
+ '<span class="rk-raws-folder-meta"><span class="rk-raws-folder-ic">'+${JSON.stringify(ICON_FOLDER)}+'</span>'
|
|
422
|
+
+ '<span class="rk-raws-folder-txt"><span class="rk-raws-folder-name" title="'+attr(f.name)+'">'+esc(f.name)+'</span>'
|
|
423
|
+
+ '<span class="rk-raws-folder-count">'+n+' post'+(n===1?'':'s')+'</span></span></span>'
|
|
424
|
+
+ '</button>'
|
|
425
|
+
+ '<div class="rk-raws-folder-menu" data-folder-menu>'
|
|
426
|
+
+ '<button type="button" class="rk-raws-folder-kebab" data-approved-fkebab aria-label="Folder actions" aria-haspopup="true" aria-expanded="false">'+${JSON.stringify(ICON_MORE)}+'</button>'
|
|
427
|
+
+ '<div class="rk-raws-folder-pop" data-folder-pop hidden role="menu">'
|
|
428
|
+
+ '<button type="button" role="menuitem" data-approved-frename="'+attr(f.id)+'">Rename folder</button>'
|
|
429
|
+
+ '<button type="button" role="menuitem" data-folder-copy="'+attr(canonPath)+'">Copy path</button>'
|
|
430
|
+
+ '</div></div>'
|
|
431
|
+
+ '</div>';
|
|
432
|
+
var tmp=document.createElement('div'); tmp.innerHTML=html; return tmp.firstChild;
|
|
433
|
+
}
|
|
434
|
+
function renderCrumbs(){
|
|
435
|
+
if(!crumbsEl)return;
|
|
436
|
+
if(MODE==='root'){ crumbsEl.innerHTML='<span class="rk-raws-crumb is-current">All posts</span>'; return; }
|
|
437
|
+
var html='<button type="button" class="rk-raws-crumb rk-raws-crumb-link" data-approved-crumb-root>'+${JSON.stringify(ICON_CHEVLEFT)}+'<span>All posts</span></button>'
|
|
438
|
+
+ '<span class="rk-raws-crumb-sep" aria-hidden="true">/</span>';
|
|
439
|
+
if(MODE==='folder'){ var f=folderById(CUR); html+='<span class="rk-raws-crumb is-current">'+esc(f?f.name:'Folder')+' <span class="rk-raws-crumb-n">'+(f?f.items.length:0)+'</span></span>'; }
|
|
440
|
+
else { html+='<span class="rk-raws-crumb is-current">Search</span>'; }
|
|
441
|
+
crumbsEl.innerHTML=html;
|
|
442
|
+
}
|
|
443
|
+
function showBar(show){ if(foldBar)foldBar.hidden=!show; }
|
|
444
|
+
function setVisible(pred){ var shown=0; activeCards().forEach(function(c){ var v=pred(metaOf(c)); c.hidden=!v; if(v)shown++; }); return shown; }
|
|
445
|
+
function paintRoot(){
|
|
446
|
+
MODE='root'; CUR=''; clearTiles(); buildFolders();
|
|
447
|
+
// folder tiles first (in order, before the cards), then loose cards remain.
|
|
448
|
+
var frag=document.createDocumentFragment();
|
|
449
|
+
FOLDERS.forEach(function(f){ frag.appendChild(folderTileEl(f)); });
|
|
450
|
+
grid.insertBefore(frag,grid.firstChild);
|
|
451
|
+
var looseShown=setVisible(function(m){ return !m.key; });
|
|
452
|
+
showBar(FOLDERS.length>0);
|
|
453
|
+
renderCrumbs();
|
|
454
|
+
if(empty)empty.classList.toggle('is-visible',FOLDERS.length===0&&looseShown===0);
|
|
455
|
+
}
|
|
456
|
+
function paintFolder(id){
|
|
457
|
+
var f=folderById(id); if(!f){ paintRoot(); return; }
|
|
458
|
+
MODE='folder'; CUR=id; clearTiles(); showBar(true); renderCrumbs();
|
|
459
|
+
var shown=setVisible(function(m){ return m.key===id; });
|
|
460
|
+
if(empty)empty.classList.toggle('is-visible',shown===0);
|
|
461
|
+
}
|
|
462
|
+
function paintSearch(q){
|
|
463
|
+
MODE='search'; clearTiles(); showBar(false);
|
|
464
|
+
var shown=setVisible(function(m){ return (m.card.getAttribute('data-title')||'').indexOf(q)!==-1; });
|
|
465
|
+
if(empty)empty.classList.toggle('is-visible',shown===0);
|
|
466
|
+
}
|
|
467
|
+
function repaint(){ if(MODE==='folder'&&folderById(CUR))paintFolder(CUR); else paintRoot(); }
|
|
468
|
+
function apply(){
|
|
469
|
+
var q=(search&&search.value||'').trim().toLowerCase();
|
|
470
|
+
if(q)paintSearch(q); else repaint();
|
|
471
|
+
}
|
|
472
|
+
if(search)search.addEventListener('input',apply);
|
|
473
|
+
|
|
474
|
+
/* ── folder tile clicks: open / rename / copy-path + breadcrumb back ──────── */
|
|
475
|
+
function closeFolderMenus(except){
|
|
476
|
+
Array.prototype.slice.call(grid.querySelectorAll('[data-folder-menu]')).forEach(function(mn){
|
|
477
|
+
if(mn===except)return;
|
|
478
|
+
var pop=mn.querySelector('[data-folder-pop]'); if(pop)pop.hidden=true;
|
|
479
|
+
var kb=mn.querySelector('[data-approved-fkebab]'); if(kb)kb.setAttribute('aria-expanded','false');
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
function copyText(txt,ok){ try{ if(navigator.clipboard&&navigator.clipboard.writeText){ navigator.clipboard.writeText(txt); } else { var ta=document.createElement('textarea'); ta.value=txt; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); } if(ok)ok(); }catch(e){} }
|
|
483
|
+
function slugFolder(v){ return String(v||'').toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'').slice(0,60); }
|
|
484
|
+
function renameApprovedFolder(id){
|
|
485
|
+
var f=folderById(id); if(!f)return;
|
|
486
|
+
var current=f.folderPath?f.folderPath.split('/').pop():(f.tracer||f.name);
|
|
487
|
+
var next=(window.prompt('Rename folder',current||'')||'').trim();
|
|
488
|
+
if(!next||next===current)return;
|
|
489
|
+
if(f.folderPath){
|
|
490
|
+
fetch('/api/v1/user/me/directory/rename',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json',Accept:'application/json'},body:JSON.stringify({path:'/approved/'+f.folderPath,new_name:next})})
|
|
491
|
+
.then(function(r){ return r.ok?r.json():r.json().catch(function(){return{};}).then(function(d){ throw new Error((d&&d.error)||'Rename failed'); }); })
|
|
492
|
+
.then(function(){ window.location.reload(); })
|
|
493
|
+
.catch(function(err){ window.alert((err&&err.message)||'Rename failed'); });
|
|
494
|
+
} else {
|
|
495
|
+
// synthetic tracer group → materialize a real folder by moving each post.
|
|
496
|
+
if(!account){ window.alert('Cannot rename this group here.'); return; }
|
|
497
|
+
var target=slugFolder(next); if(!target){ window.alert('Pick a folder name with letters or numbers.'); return; }
|
|
498
|
+
Promise.all(f.items.map(function(m){
|
|
499
|
+
return fetch('/u/'+encodeURIComponent(account)+'/approved/posts/'+encodeURIComponent(m.postId),{method:'PATCH',credentials:'same-origin',headers:{'Content-Type':'application/json',Accept:'application/json'},body:JSON.stringify({folder_path:target})}).then(function(r){ return r.ok; });
|
|
500
|
+
})).then(function(){ window.location.reload(); }).catch(function(){ window.alert('Rename failed'); });
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
if(grid){
|
|
504
|
+
grid.addEventListener('click',function(e){
|
|
505
|
+
var t=e.target;
|
|
506
|
+
var kb=t&&t.closest?t.closest('[data-approved-fkebab]'):null;
|
|
507
|
+
if(kb){ e.preventDefault(); e.stopPropagation(); var mn=kb.closest('[data-folder-menu]'); var pop=mn&&mn.querySelector('[data-folder-pop]'); var willOpen=pop&&pop.hidden; closeFolderMenus(mn); if(pop){ pop.hidden=!willOpen; kb.setAttribute('aria-expanded',willOpen?'true':'false'); } return; }
|
|
508
|
+
var rn=t&&t.closest?t.closest('[data-approved-frename]'):null;
|
|
509
|
+
if(rn){ e.preventDefault(); closeFolderMenus(null); renameApprovedFolder(rn.getAttribute('data-approved-frename')); return; }
|
|
510
|
+
var cp=t&&t.closest?t.closest('[data-folder-copy]'):null;
|
|
511
|
+
if(cp){ e.preventDefault(); closeFolderMenus(null); copyText(cp.getAttribute('data-folder-copy')); return; }
|
|
512
|
+
var fo=t&&t.closest?t.closest('[data-approved-folder]'):null;
|
|
513
|
+
if(fo){ e.preventDefault(); closeFolderMenus(null); if(search)search.value=''; paintFolder(fo.getAttribute('data-approved-folder')); return; }
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
if(crumbsEl){
|
|
517
|
+
crumbsEl.addEventListener('click',function(e){
|
|
518
|
+
var r=e.target&&e.target.closest?e.target.closest('[data-approved-crumb-root]'):null;
|
|
519
|
+
if(!r)return; e.preventDefault(); if(search)search.value=''; paintRoot();
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
document.addEventListener('click',function(e){ var t=e.target; if(!(t&&t.closest&&t.closest('[data-folder-menu]')))closeFolderMenus(null); });
|
|
523
|
+
|
|
382
524
|
apply();
|
|
383
525
|
|
|
384
526
|
// ── per-card "⋯" overflow menu: Copy ID + Delete (popconfirm) ────────────
|
|
@@ -470,7 +612,7 @@ const APPROVED_SCRIPT = `
|
|
|
470
612
|
? '<video class="rk-library-viewer-media" data-vsrc="'+attr(t.url)+'"'+(t.poster?' poster="'+attr(t.poster)+'"':'')+' loop playsinline preload="none"></video>'
|
|
471
613
|
: '<img class="rk-library-viewer-media" src="'+attr(t.url||t.poster)+'" alt="'+attr(t.title||'Published cut')+'">';
|
|
472
614
|
var cta=t.editor_href
|
|
473
|
-
? '<a class="rk-btn rk-btn-gold rk-btn-sm rk-library-viewer-cta" href="'+attr(t.editor_href)+'">Open in editor</a>'
|
|
615
|
+
? '<a class="rk-btn rk-btn-gold rk-btn-sm rk-library-viewer-cta" href="'+attr(t.editor_href)+'" target="_blank" rel="noreferrer">Open in editor</a>'
|
|
474
616
|
: '';
|
|
475
617
|
var view=t.post_href
|
|
476
618
|
? '<a class="rk-btn rk-btn-ghost-light rk-btn-sm rk-library-viewer-cta" href="'+attr(t.post_href)+'" target="_blank" rel="noreferrer">View post</a>'
|
|
@@ -552,9 +694,6 @@ const CLIPS_SCRIPT = `
|
|
|
552
694
|
var empty=root.getElementById('rkClipsEmpty');
|
|
553
695
|
var form=root.getElementById('rkClipsSearch');
|
|
554
696
|
var query=root.getElementById('rkClipsQuery');
|
|
555
|
-
var hoverEl=root.getElementById('rkRawsHover');
|
|
556
|
-
var hoverVid=hoverEl&&hoverEl.querySelector('.rk-raws-hover-vid');
|
|
557
|
-
var hoverCap=root.getElementById('rkRawsHoverCap');
|
|
558
697
|
var canHover=!(window.matchMedia&&window.matchMedia('(hover:none)').matches);
|
|
559
698
|
|
|
560
699
|
// ── file-directory state ────────────────────────────────────────────────
|
|
@@ -586,16 +725,34 @@ const CLIPS_SCRIPT = `
|
|
|
586
725
|
query.setAttribute('placeholder','Search '+n+' raw clip'+(n===1?'':'s')+String.fromCharCode(8230));
|
|
587
726
|
}
|
|
588
727
|
|
|
589
|
-
|
|
590
|
-
|
|
728
|
+
function normFp(v){ return (v==null?'':String(v)).replace(/^\\/+|\\/+$/g,''); }
|
|
729
|
+
function slugFolder(v){ return String(v||'').toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'').slice(0,60); }
|
|
730
|
+
|
|
731
|
+
// Group raws into REAL directory folders keyed on each clip's folder_path
|
|
732
|
+
// (canonical /raws/<folder_path>) — the same folders the file directory and the
|
|
733
|
+
// AI agent see, so a folder can be renamed and clips moved between them. Legacy
|
|
734
|
+
// clips with no folder_path fall back to a synthetic source-derived group; those
|
|
735
|
+
// aren't backed by a stored folder yet, so renaming one MATERIALIZES a folder by
|
|
736
|
+
// moving its clips (see renameFolder). First-seen order is preserved.
|
|
591
737
|
function buildFolders(){
|
|
592
738
|
var map={}; FOLDERS=[];
|
|
593
739
|
ALL.forEach(function(c){
|
|
594
|
-
var
|
|
595
|
-
var f
|
|
596
|
-
if(
|
|
740
|
+
var fp=normFp(c.folder_path);
|
|
741
|
+
var key,f;
|
|
742
|
+
if(fp){ key='fp:'+fp; }
|
|
743
|
+
else { key='src:'+String(c.source_video_id||c.source_filename||'ungrouped'); }
|
|
744
|
+
f=map[key];
|
|
745
|
+
if(!f){
|
|
746
|
+
f={ id:key, folderPath:fp||null, name:fp||(c.source_filename||'Untitled source'), clips:[] };
|
|
747
|
+
map[key]=f; FOLDERS.push(f);
|
|
748
|
+
}
|
|
597
749
|
f.clips.push(c);
|
|
598
750
|
});
|
|
751
|
+
// Float the full-length original video (role:"source") to the top of its
|
|
752
|
+
// folder so it reads as the reference the mined clips were cut from.
|
|
753
|
+
FOLDERS.forEach(function(f){
|
|
754
|
+
f.clips.sort(function(a,b){ return (b.is_source_original?1:0)-(a.is_source_original?1:0); });
|
|
755
|
+
});
|
|
599
756
|
}
|
|
600
757
|
|
|
601
758
|
// Optimistic "importing…" placeholder card (spinner, or an error state with a
|
|
@@ -628,14 +785,31 @@ const CLIPS_SCRIPT = `
|
|
|
628
785
|
} else {
|
|
629
786
|
mosaic='<span class="rk-raws-folder-mosaic is-empty">'+${JSON.stringify(ICON_FOLDER)}+'</span>';
|
|
630
787
|
}
|
|
631
|
-
|
|
632
|
-
|
|
788
|
+
// Count MINED clips only — the full-length original is a reference raw, not a
|
|
789
|
+
// mined scene — and note the presence of a full video alongside the count.
|
|
790
|
+
var hasFull=folder.clips.some(function(c){ return c.is_source_original; });
|
|
791
|
+
var n=folder.clips.filter(function(c){ return !c.is_source_original; }).length;
|
|
792
|
+
var countTxt=n+' clip'+(n===1?'':'s')+(hasFull?' · full video':'');
|
|
793
|
+
var canonPath='/raws'+(folder.folderPath?'/'+folder.folderPath:'');
|
|
794
|
+
// Kebab: rename the folder + copy its canonical /raws path. Sits OUTSIDE the
|
|
795
|
+
// open-folder button (buttons can't nest); its own click is caught before the
|
|
796
|
+
// tile's navigation handler.
|
|
797
|
+
var menu='<div class="rk-raws-folder-menu" data-folder-menu>'
|
|
798
|
+
+ '<button type="button" class="rk-raws-folder-kebab" data-folder-kebab aria-label="Folder actions" aria-haspopup="true" aria-expanded="false">'+${JSON.stringify(ICON_MORE)}+'</button>'
|
|
799
|
+
+ '<div class="rk-raws-folder-pop" data-folder-pop hidden role="menu">'
|
|
800
|
+
+ '<button type="button" role="menuitem" data-folder-rename="'+attr(folder.id)+'">Rename folder</button>'
|
|
801
|
+
+ '<button type="button" role="menuitem" data-folder-copy="'+attr(canonPath)+'">Copy path</button>'
|
|
802
|
+
+ '</div></div>';
|
|
803
|
+
return '<div class="rk-raws-folder-wrap">'
|
|
804
|
+
+ '<button type="button" class="rk-raws-folder rk-card-hover" data-folder-id="'+attr(folder.id)+'">'
|
|
633
805
|
+ mosaic
|
|
634
806
|
+ '<span class="rk-raws-folder-meta">'
|
|
635
807
|
+ '<span class="rk-raws-folder-ic">'+${JSON.stringify(ICON_FOLDER)}+'</span>'
|
|
636
808
|
+ '<span class="rk-raws-folder-txt"><span class="rk-raws-folder-name" title="'+attr(folder.name)+'">'+esc(folder.name)+'</span>'
|
|
637
|
-
+ '<span class="rk-raws-folder-count">'+
|
|
638
|
-
+ '</span></button>'
|
|
809
|
+
+ '<span class="rk-raws-folder-count">'+countTxt+'</span></span>'
|
|
810
|
+
+ '</span></button>'
|
|
811
|
+
+ menu
|
|
812
|
+
+ '</div>';
|
|
639
813
|
}
|
|
640
814
|
|
|
641
815
|
function card(clip,i){
|
|
@@ -647,8 +821,12 @@ const CLIPS_SCRIPT = `
|
|
|
647
821
|
var tags=topTags(clip.tags).map(function(t){ return '<span class="rk-clips-tag">'+esc(t)+'</span>'; }).join('');
|
|
648
822
|
var desc=clip.description || (clip.tags&&clip.tags.transcript) || '(untagged clip)';
|
|
649
823
|
var dl='/raws/'+encodeURIComponent(clip.clip_id)+'/download';
|
|
650
|
-
|
|
824
|
+
// The full-length original a hunt was mined from gets a distinct badge.
|
|
825
|
+
var fullBadge=clip.is_source_original?'<span class="rk-clips-fullbadge">Full video</span>':'';
|
|
826
|
+
return '<article class="rk-clips-card rk-card-hover'+(clip.is_source_original?' is-source-original':'')+'">'
|
|
651
827
|
+ '<button type="button" class="rk-clips-thumb" data-rk-clip-view="'+i+'" aria-label="Open raw">'+media
|
|
828
|
+
+ '<video class="rk-clips-thumb-vid" muted loop playsinline preload="none"'+(clip.thumbnail_url?' poster="'+attr(clip.thumbnail_url)+'"':'')+'></video>'
|
|
829
|
+
+ fullBadge
|
|
652
830
|
+ '<span class="rk-clips-dur">'+esc(range+(dur?' · '+dur:''))+'</span>'
|
|
653
831
|
+ '<span class="rk-clips-play" aria-hidden="true">'+${JSON.stringify(PLAY)}+'</span>'
|
|
654
832
|
+ '</button>'
|
|
@@ -660,35 +838,29 @@ const CLIPS_SCRIPT = `
|
|
|
660
838
|
+ '</div></article>';
|
|
661
839
|
}
|
|
662
840
|
|
|
663
|
-
/* ── mouseover
|
|
841
|
+
/* ── mouseover: play the clip in place inside its own card ────────────────── */
|
|
664
842
|
var hoverTimer=null;
|
|
843
|
+
var playingThumb=null; // the thumb whose inline <video> is currently active
|
|
844
|
+
function stopInline(thumb){
|
|
845
|
+
if(!thumb)return;
|
|
846
|
+
var vid=thumb.querySelector('.rk-clips-thumb-vid');
|
|
847
|
+
if(vid){ try{ vid.pause(); }catch(e){} vid.removeAttribute('src'); vid.load&&vid.load(); }
|
|
848
|
+
thumb.classList.remove('is-playing');
|
|
849
|
+
}
|
|
665
850
|
function hideHover(){
|
|
666
851
|
if(hoverTimer){ clearTimeout(hoverTimer); hoverTimer=null; }
|
|
667
|
-
if(
|
|
668
|
-
hoverEl.hidden=true; hoverEl.classList.remove('is-visible');
|
|
669
|
-
if(hoverVid){ try{ hoverVid.pause(); }catch(e){} hoverVid.removeAttribute('src'); hoverVid.load&&hoverVid.load(); }
|
|
670
|
-
}
|
|
671
|
-
function positionHover(rect){
|
|
672
|
-
if(!hoverEl)return;
|
|
673
|
-
var gap=12, bw=hoverEl.offsetWidth||250, bh=hoverEl.offsetHeight||330;
|
|
674
|
-
var x=rect.right+gap;
|
|
675
|
-
if(x+bw>window.innerWidth-8) x=rect.left-gap-bw;
|
|
676
|
-
if(x<8) x=Math.max(8,Math.min(rect.left,window.innerWidth-bw-8));
|
|
677
|
-
var y=rect.top+rect.height/2-bh/2;
|
|
678
|
-
y=Math.max(8,Math.min(y,window.innerHeight-bh-8));
|
|
679
|
-
hoverEl.style.left=Math.round(x)+'px';
|
|
680
|
-
hoverEl.style.top=Math.round(y)+'px';
|
|
852
|
+
if(playingThumb){ stopInline(playingThumb); playingThumb=null; }
|
|
681
853
|
}
|
|
682
|
-
function
|
|
683
|
-
if(!
|
|
684
|
-
if(
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
854
|
+
function playInline(thumb,clip){
|
|
855
|
+
if(!thumb||!clip||!clip.view_url)return;
|
|
856
|
+
if(playingThumb&&playingThumb!==thumb){ stopInline(playingThumb); }
|
|
857
|
+
playingThumb=thumb;
|
|
858
|
+
var vid=thumb.querySelector('.rk-clips-thumb-vid');
|
|
859
|
+
if(!vid)return;
|
|
860
|
+
if(clip.thumbnail_url) vid.poster=clip.thumbnail_url;
|
|
861
|
+
vid.setAttribute('src',clip.view_url); vid.load&&vid.load();
|
|
862
|
+
thumb.classList.add('is-playing');
|
|
863
|
+
var p=vid.play&&vid.play(); if(p&&p.catch)p.catch(function(){});
|
|
692
864
|
}
|
|
693
865
|
function bindHover(){
|
|
694
866
|
if(!grid||!canHover)return;
|
|
@@ -696,17 +868,17 @@ const CLIPS_SCRIPT = `
|
|
|
696
868
|
var idx=Number(thumb.getAttribute('data-rk-clip-view'));
|
|
697
869
|
thumb.addEventListener('mouseenter',function(){
|
|
698
870
|
var clip=VIEW[idx]; if(!clip)return;
|
|
699
|
-
var rect=thumb.getBoundingClientRect();
|
|
700
871
|
if(hoverTimer)clearTimeout(hoverTimer);
|
|
701
|
-
hoverTimer=setTimeout(function(){
|
|
872
|
+
hoverTimer=setTimeout(function(){ playInline(thumb,clip); },90);
|
|
873
|
+
});
|
|
874
|
+
thumb.addEventListener('mouseleave',function(){
|
|
875
|
+
if(hoverTimer){ clearTimeout(hoverTimer); hoverTimer=null; }
|
|
876
|
+
stopInline(thumb);
|
|
877
|
+
if(playingThumb===thumb) playingThumb=null;
|
|
702
878
|
});
|
|
703
|
-
thumb.addEventListener('mouseleave',hideHover);
|
|
704
879
|
});
|
|
705
880
|
}
|
|
706
|
-
|
|
707
|
-
window.addEventListener('scroll',hideHover,true);
|
|
708
|
-
window.addEventListener('resize',hideHover);
|
|
709
|
-
}
|
|
881
|
+
window.addEventListener('scroll',hideHover,true);
|
|
710
882
|
|
|
711
883
|
/* ── breadcrumb + view rendering ──────────────────────────────────────────── */
|
|
712
884
|
function renderCrumbs(){
|
|
@@ -891,13 +1063,67 @@ const CLIPS_SCRIPT = `
|
|
|
891
1063
|
window.addEventListener('resize',function(){ if(vOpen&&vActive>=0)vTrack.scrollTop=vActive*vTrack.clientHeight; });
|
|
892
1064
|
}
|
|
893
1065
|
|
|
1066
|
+
/* ── folder rename / copy-path (kebab on each folder tile) ────────────────── */
|
|
1067
|
+
function closeFolderMenus(except){
|
|
1068
|
+
if(!grid)return;
|
|
1069
|
+
Array.prototype.slice.call(grid.querySelectorAll('[data-folder-menu]')).forEach(function(m){
|
|
1070
|
+
if(m===except)return;
|
|
1071
|
+
var pop=m.querySelector('[data-folder-pop]'); if(pop)pop.hidden=true;
|
|
1072
|
+
var kb=m.querySelector('[data-folder-kebab]'); if(kb)kb.setAttribute('aria-expanded','false');
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
function copyPath(path){
|
|
1076
|
+
try{
|
|
1077
|
+
if(navigator.clipboard&&navigator.clipboard.writeText){ navigator.clipboard.writeText(path); }
|
|
1078
|
+
else { var ta=document.createElement('textarea'); ta.value=path; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); }
|
|
1079
|
+
if(statusEl)statusEl.textContent='Copied '+path;
|
|
1080
|
+
}catch(e){ if(statusEl)statusEl.textContent=path; }
|
|
1081
|
+
}
|
|
1082
|
+
// Rename a raws folder. Real folders (folder_path set) go through the directory
|
|
1083
|
+
// rename endpoint (re-points every clip + nested subfolders server-side). Legacy
|
|
1084
|
+
// source-groups (no folder_path) have no stored folder to rename, so we
|
|
1085
|
+
// MATERIALIZE one by moving each clip into /raws/<slug> via PATCH /raws/:id.
|
|
1086
|
+
function renameFolder(folderId){
|
|
1087
|
+
var f=folderById(folderId); if(!f)return;
|
|
1088
|
+
var current=f.folderPath?(f.folderPath.split('/').pop()):f.name;
|
|
1089
|
+
var next=(window.prompt('Rename folder',current||'')||'').trim();
|
|
1090
|
+
if(!next||next===current)return;
|
|
1091
|
+
if(statusEl)statusEl.textContent='Renaming…';
|
|
1092
|
+
if(f.folderPath){
|
|
1093
|
+
fetch('/api/v1/user/me/directory/rename',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json',Accept:'application/json'},body:JSON.stringify({path:'/raws/'+f.folderPath,new_name:next})})
|
|
1094
|
+
.then(function(r){ return r.ok?r.json():r.json().catch(function(){return{};}).then(function(d){ throw new Error((d&&d.error)||'Rename failed'); }); })
|
|
1095
|
+
.then(function(){ loadFeed(); })
|
|
1096
|
+
.catch(function(err){ if(statusEl)statusEl.textContent=(err&&err.message)||'Rename failed'; });
|
|
1097
|
+
} else {
|
|
1098
|
+
var target=slugFolder(next); if(!target){ if(statusEl)statusEl.textContent='Pick a folder name with letters or numbers.'; return; }
|
|
1099
|
+
Promise.all(f.clips.map(function(c){
|
|
1100
|
+
return fetch('/raws/'+encodeURIComponent(c.clip_id),{method:'PATCH',credentials:'same-origin',headers:{'Content-Type':'application/json',Accept:'application/json'},body:JSON.stringify({folder_path:target})}).then(function(r){ return r.ok; });
|
|
1101
|
+
})).then(function(oks){
|
|
1102
|
+
if(oks.some(function(ok){ return !ok; })){ if(statusEl)statusEl.textContent='Some clips could not be moved — try again.'; }
|
|
1103
|
+
loadFeed();
|
|
1104
|
+
}).catch(function(){ if(statusEl)statusEl.textContent='Rename failed'; loadFeed(); });
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
|
|
894
1108
|
// Folder navigation (grid tile) + breadcrumb "All raws" back link.
|
|
895
1109
|
if(grid){
|
|
896
1110
|
grid.addEventListener('click',function(e){
|
|
897
|
-
var
|
|
898
|
-
|
|
1111
|
+
var tgt=e.target;
|
|
1112
|
+
// kebab toggle
|
|
1113
|
+
var kb=tgt&&tgt.closest?tgt.closest('[data-folder-kebab]'):null;
|
|
1114
|
+
if(kb){ e.preventDefault(); e.stopPropagation(); var menu=kb.closest('[data-folder-menu]'); var pop=menu&&menu.querySelector('[data-folder-pop]'); var willOpen=pop&&pop.hidden; closeFolderMenus(menu); if(pop){ pop.hidden=!willOpen; kb.setAttribute('aria-expanded',willOpen?'true':'false'); } return; }
|
|
1115
|
+
var rn=tgt&&tgt.closest?tgt.closest('[data-folder-rename]'):null;
|
|
1116
|
+
if(rn){ e.preventDefault(); closeFolderMenus(null); renameFolder(rn.getAttribute('data-folder-rename')); return; }
|
|
1117
|
+
var cp=tgt&&tgt.closest?tgt.closest('[data-folder-copy]'):null;
|
|
1118
|
+
if(cp){ e.preventDefault(); closeFolderMenus(null); copyPath(cp.getAttribute('data-folder-copy')); return; }
|
|
1119
|
+
var f=tgt&&tgt.closest?tgt.closest('[data-folder-id]'):null;
|
|
1120
|
+
if(!f)return; e.preventDefault(); closeFolderMenus(null); hideHover(); if(query)query.value=''; paintFolder(f.getAttribute('data-folder-id'));
|
|
899
1121
|
});
|
|
900
1122
|
}
|
|
1123
|
+
document.addEventListener('click',function(e){
|
|
1124
|
+
var t=e.target;
|
|
1125
|
+
if(!(t&&t.closest&&t.closest('[data-folder-menu]')))closeFolderMenus(null);
|
|
1126
|
+
});
|
|
901
1127
|
if(crumbsEl){
|
|
902
1128
|
crumbsEl.addEventListener('click',function(e){
|
|
903
1129
|
var r=e.target&&e.target.closest?e.target.closest('[data-crumb-root]'):null;
|
|
@@ -957,7 +1183,7 @@ const CLIPS_SCRIPT = `
|
|
|
957
1183
|
var scanSubmit=root.getElementById('rkClipsScanSubmit');
|
|
958
1184
|
var scanClip=root.getElementById('rkScanClip');
|
|
959
1185
|
var scanAdvanced=root.getElementById('rkClipsAdvanced');
|
|
960
|
-
var IMPORT_NOTE="We'll
|
|
1186
|
+
var IMPORT_NOTE="We'll open the Clipper so you can preview the video and save an exact subrange (or the whole thing) to your raws.";
|
|
961
1187
|
var CLIP_NOTE='AI tagging runs on your own key (BYOK) — vidfarm only bills the clip-scan compute.';
|
|
962
1188
|
var SCAN_OPTS=null; // GET /raws/scan-options: which providers/keys can run a hunt
|
|
963
1189
|
function setNote(msg,tone){ if(!scanNote)return; scanNote.textContent=msg; if(tone)scanNote.setAttribute('data-tone',tone); else scanNote.removeAttribute('data-tone'); }
|
|
@@ -989,7 +1215,7 @@ const CLIPS_SCRIPT = `
|
|
|
989
1215
|
function syncClipMode(){
|
|
990
1216
|
var on=!!(scanClip&&scanClip.checked);
|
|
991
1217
|
if(scanAdvanced)scanAdvanced.hidden=!on;
|
|
992
|
-
if(scanSubmit)scanSubmit.innerHTML=(on?'Start scan':'
|
|
1218
|
+
if(scanSubmit)scanSubmit.innerHTML=(on?'Start scan':'Open clipper')+' <span class="rk-arrow">→</span>';
|
|
993
1219
|
if(on&&!clipReady()){
|
|
994
1220
|
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');
|
|
995
1221
|
} else {
|
|
@@ -1014,7 +1240,16 @@ const CLIPS_SCRIPT = `
|
|
|
1014
1240
|
var prompt=clip?(scanPrompt&&scanPrompt.value||'').trim():'';
|
|
1015
1241
|
var provider=clip?(scanProvider&&scanProvider.value||'').trim():'';
|
|
1016
1242
|
if(!url){ setNote('Enter a video URL.','error'); return; }
|
|
1017
|
-
|
|
1243
|
+
// Whole-video (non-AI) path now opens the Clipper so the user can pick an
|
|
1244
|
+
// exact subrange (or save the whole thing) with a live video preview.
|
|
1245
|
+
if(!clip){
|
|
1246
|
+
var clp=new URL('/tools/clipper', window.location.origin);
|
|
1247
|
+
clp.searchParams.set('source_url', url);
|
|
1248
|
+
if(tracer) clp.searchParams.set('tracer', tracer);
|
|
1249
|
+
window.location.href=clp.pathname+clp.search;
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
setNote('Starting scan…');
|
|
1018
1253
|
if(scanSubmit){ scanSubmit.disabled=true; scanSubmit.classList.add('is-loading'); }
|
|
1019
1254
|
|
|
1020
1255
|
var payload={source_url:url};
|
|
@@ -1080,7 +1315,7 @@ export function renderReskinLibrary(tab = "approved", input = EMPTY_LIBRARY_INPU
|
|
|
1080
1315
|
const panel = isLogs
|
|
1081
1316
|
? `<div class="rk-library-topbar">${tabs}</div>\n ${jobRunsPanel(logsBoot)}`
|
|
1082
1317
|
: isFiles
|
|
1083
|
-
? filesPanel(tabs)
|
|
1318
|
+
? filesPanel(tabs, Boolean(input.cloudSpace), input.initialFilesPath)
|
|
1084
1319
|
: isRaws
|
|
1085
1320
|
? clipsPanel(tabs)
|
|
1086
1321
|
: approvedPanel(input, tabs);
|
|
@@ -1267,6 +1502,17 @@ video.rk-library-viewer-media{cursor:pointer}
|
|
|
1267
1502
|
.rk-raws-explorer.is-folders{grid-template-columns:repeat(auto-fill,minmax(228px,1fr))}
|
|
1268
1503
|
|
|
1269
1504
|
/* folder tile — 2×2 thumbnail mosaic + name + count */
|
|
1505
|
+
.rk-raws-folder-wrap{position:relative;display:flex}
|
|
1506
|
+
.rk-raws-folder-wrap>.rk-raws-folder{flex:1;min-width:0}
|
|
1507
|
+
.rk-raws-folder-menu{position:absolute;top:10px;right:10px;z-index:4}
|
|
1508
|
+
.rk-raws-folder-kebab{width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;border:0;border-radius:var(--rk-r-full);background:rgba(255,255,255,.92);color:var(--rk-ink);cursor:pointer;opacity:0;transform:scale(.85);box-shadow:var(--rk-shadow-sm);transition:opacity var(--rk-dur) var(--rk-ease),transform var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease)}
|
|
1509
|
+
.rk-raws-folder-wrap:hover .rk-raws-folder-kebab,.rk-raws-folder-menu [aria-expanded="true"]{opacity:1;transform:scale(1)}
|
|
1510
|
+
.rk-raws-folder-kebab:hover{background:#fff}
|
|
1511
|
+
@media(hover:none){.rk-raws-folder-kebab{opacity:1;transform:scale(1)}}
|
|
1512
|
+
.rk-raws-folder-pop{position:absolute;top:36px;right:0;min-width:168px;background:var(--rk-surface);border:1px solid var(--rk-border);border-radius:var(--rk-r-lg);box-shadow:var(--rk-shadow-lg);padding:6px;display:flex;flex-direction:column;gap:2px}
|
|
1513
|
+
.rk-raws-folder-pop[hidden]{display:none}
|
|
1514
|
+
.rk-raws-folder-pop button{appearance:none;border:0;background:none;font:inherit;text-align:left;padding:8px 10px;border-radius:var(--rk-r-md);color:var(--rk-ink);font-size:13px;font-weight:600;cursor:pointer}
|
|
1515
|
+
.rk-raws-folder-pop button:hover{background:var(--rk-n-100)}
|
|
1270
1516
|
.rk-raws-folder{display:flex;flex-direction:column;overflow:hidden;background:var(--rk-surface);border:1px solid var(--rk-border);border-radius:var(--rk-r-2xl);box-shadow:var(--rk-shadow-card);cursor:pointer;text-align:left;font:inherit;color:inherit;padding:0;transition:transform var(--rk-dur) var(--rk-ease),box-shadow var(--rk-dur) var(--rk-ease),border-color var(--rk-dur) var(--rk-ease)}
|
|
1271
1517
|
.rk-raws-folder:hover{transform:translateY(-3px);box-shadow:var(--rk-shadow-md);border-color:var(--rk-gold-600)}
|
|
1272
1518
|
.rk-raws-folder-mosaic{position:relative;display:grid;grid-template-columns:1fr 1fr;grid-auto-rows:1fr;gap:2px;aspect-ratio:4/3;background:var(--rk-n-100);overflow:hidden}
|
|
@@ -1280,21 +1526,17 @@ video.rk-library-viewer-media{cursor:pointer}
|
|
|
1280
1526
|
.rk-raws-folder-name{font-family:var(--rk-font-display);font-size:14px;font-weight:700;color:var(--rk-ink);letter-spacing:-.01em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1281
1527
|
.rk-raws-folder-count{font-size:12px;font-weight:600;color:var(--rk-text-muted)}
|
|
1282
1528
|
|
|
1283
|
-
/* mouseover preview popover (floating enlarged media) */
|
|
1284
|
-
.rk-raws-hover{position:fixed;z-index:90;width:250px;pointer-events:none;border-radius:var(--rk-r-2xl);overflow:hidden;background:#0d0d0d;border:1px solid rgba(255,255,255,.12);box-shadow:var(--rk-shadow-xl);opacity:0;transform:scale(.96);transition:opacity .12s var(--rk-ease),transform .12s var(--rk-ease)}
|
|
1285
|
-
.rk-raws-hover.is-visible{opacity:1;transform:scale(1)}
|
|
1286
|
-
.rk-raws-hover[hidden]{display:none}
|
|
1287
|
-
.rk-raws-hover-vid{display:block;width:100%;aspect-ratio:3/4;object-fit:cover;background:#0d0d0d}
|
|
1288
|
-
.rk-raws-hover-cap{padding:9px 12px 11px;font-size:12px;font-weight:600;line-height:1.35;color:rgba(255,255,255,.92);background:linear-gradient(180deg,#171717,#0d0d0d);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
|
|
1289
|
-
@media(hover:none){.rk-raws-hover{display:none!important}}
|
|
1290
1529
|
|
|
1291
1530
|
.rk-clips-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:20px}
|
|
1292
1531
|
.rk-clips-card{display:flex;flex-direction:column;overflow:hidden;background:var(--rk-surface);border:1px solid var(--rk-border);border-radius:var(--rk-r-2xl);box-shadow:var(--rk-shadow-card)}
|
|
1293
1532
|
.rk-clips-thumb{position:relative;aspect-ratio:3/4;overflow:hidden;display:grid;place-items:center;background:linear-gradient(150deg,#2c2c2c,#171717)}
|
|
1294
1533
|
.rk-clips-thumb-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
|
|
1295
|
-
.rk-clips-thumb-vid{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:none}
|
|
1534
|
+
.rk-clips-thumb-vid{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:none;z-index:1}
|
|
1535
|
+
.rk-clips-thumb.is-playing .rk-clips-thumb-vid{display:block}
|
|
1536
|
+
.rk-clips-thumb.is-playing .rk-clips-play{opacity:0!important}
|
|
1296
1537
|
.rk-clips-noimg{color:rgba(255,255,255,.68);font-size:12px;font-weight:600;letter-spacing:.01em}
|
|
1297
1538
|
.rk-clips-dur{position:absolute;left:10px;bottom:10px;padding:3px 9px;border-radius:var(--rk-r-full);background:rgba(255,255,255,.92);color:var(--rk-ink);font-size:11px;font-weight:700;font-variant-numeric:tabular-nums;box-shadow:var(--rk-shadow-xs);z-index:2}
|
|
1539
|
+
.rk-clips-fullbadge{position:absolute;left:10px;top:10px;padding:3px 9px;border-radius:var(--rk-r-full);background:var(--rk-ink);color:#fff;font-size:11px;font-weight:700;letter-spacing:.01em;box-shadow:var(--rk-shadow-xs);z-index:3}
|
|
1298
1540
|
.rk-clips-play{position:absolute;right:10px;top:10px;width:34px;height:34px;display:grid;place-items:center;padding:0 0 0 2px;border:0;border-radius:var(--rk-r-full);background:rgba(255,255,255,.92);color:var(--rk-ink);font-size:13px;cursor:pointer;opacity:0;transform:scale(.85);box-shadow:var(--rk-shadow-sm);z-index:2;transition:opacity var(--rk-dur) var(--rk-ease),transform var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease)}
|
|
1299
1541
|
.rk-clips-card:hover .rk-clips-play{opacity:1;transform:scale(1)}
|
|
1300
1542
|
@media(hover:none){.rk-clips-play{opacity:1;transform:scale(1)}}
|