feat(voyage): add webkitdirectory primary project-loader

This commit is contained in:
Kjell Tore Guttormsen 2026-05-10 16:17:13 +02:00
commit e04915882e

View file

@ -506,6 +506,18 @@
<body>
<a class="skip-link" href="#main-content">Hopp til hovedinnhold</a>
<header class="app-header" id="app-header" data-renderable></header>
<!-- v4.3 Step 11 — hidden directory-picker input. Click-handler triggers
this via [data-action="open-project-picker"] in renderTopbar. -->
<input
type="file"
webkitdirectory
multiple
data-load-input
class="visually-hidden"
aria-hidden="true"
tabindex="-1"
/>
<main id="main-content">
<section
class="guide-panel guide-panel--info"
@ -856,6 +868,51 @@ playground first-run shows a complete round-trip-able artifact.
'</div>';
}
// ---- v4.3 Step 11 — webkitdirectory project-loader ----------------
// Wire the [data-action="open-project-picker"] button (rendered by
// renderTopbar) to the hidden [data-load-input] file picker. On change,
// pipe files to loadProjectDirectory (Step 13). If webkitdirectory is
// unsupported, render a guide-panel--warn near the dropzone area.
function wireProjectLoader() {
var input = document.querySelector('[data-load-input]');
if (!input) return;
// Browser-support detection (Firefox <50, very old Safari)
if (!('webkitdirectory' in input) && !('directory' in input)) {
var warn = document.createElement('section');
warn.className = 'guide-panel guide-panel--warn';
warn.setAttribute('role', 'status');
warn.innerHTML =
'<div class="guide-panel__title">Nettleseren støtter ikke mappevalg</div>' +
'<div class="guide-panel__body">Bruk paste-import nedenfor for å laste inn ' +
'<code>brief.md</code>, <code>plan.md</code> eller <code>review.md</code>.</div>';
var main = document.getElementById('main-content');
if (main) main.insertBefore(warn, main.firstChild);
return;
}
// Click-delegation: clicking the visible button programmatically clicks the hidden input.
document.addEventListener('click', function (e) {
var btn = e.target && e.target.closest && e.target.closest('[data-action="open-project-picker"]');
if (!btn) return;
e.preventDefault();
input.click();
});
// Change-handler: derive basePath from first file's webkitRelativePath.
input.addEventListener('change', function (e) {
var files = e.target.files;
if (!files || !files.length) return;
var first = files[0].webkitRelativePath || '';
var basePath = first.split('/')[0] || '';
if (typeof loadProjectDirectory === 'function') {
loadProjectDirectory(files, basePath);
} else {
// Step 13 not yet wired — log-only fallback for incremental delivery.
try { console.log('[voyage] project-loader: ' + files.length + ' files in ' + basePath); } catch (_) {}
}
});
}
// ---- v4.3 Step 9 — renderPageShell --------------------------------
// Universal page-header for dashboard + artifact-detail flater.
// Returns an HTML string wrapping body content with DS Tier 3
@ -1589,6 +1646,10 @@ playground first-run shows a complete round-trip-able artifact.
// Step 8 (v4.3) — initial topbar render with single-crumb (voyage root).
// renderDashboard / drill-down (Wave 3) re-renders with deeper crumbs.
renderTopbar([{ label: 'Hjem' }]);
// Step 11 (v4.3) — webkitdirectory project-loader wiring (button +
// hidden file-input + browser-support detection).
wireProjectLoader();
}
function setThemeLabel(theme) {