Files
2025-09-06 17:24:09 -04:00

500 lines
18 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Q-Edit v1.1.1</title>
<link rel="icon" type="image/png" href="editor-favicon.png" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- Inline SVG icon sprite (for consistent cross-platform rendering) -->
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
style="display: none"
>
<!-- Basic arrows/chevrons -->
<symbol
id="icon-chevron-left"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M15 18l-6-6 6-6" />
</symbol>
<symbol
id="icon-chevron-right"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M9 6l6 6-6 6" />
</symbol>
<symbol
id="icon-chevron-down"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 9l6 6 6-6" />
</symbol>
<symbol
id="icon-chevrons-left"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M13 17l-5-5 5-5M19 17l-5-5 5-5" />
</symbol>
<symbol
id="icon-chevrons-right"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M11 7l5 5-5 5M5 7l5 5-5 5" />
</symbol>
<!-- Common UI icons -->
<symbol
id="icon-search"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="11" cy="11" r="7" />
<path d="M21 21l-4.3-4.3" />
</symbol>
<symbol
id="icon-upload"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 16V4" />
<path d="M8 8l4-4 4 4" />
<path d="M20 20H4" />
</symbol>
<symbol
id="icon-trash"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M3 6h18" />
<path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
<path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6" />
<path d="M10 11v6M14 11v6" />
</symbol>
<symbol
id="icon-edit"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 20h9" />
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z" />
</symbol>
<symbol
id="icon-folder"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M4 19a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h5l3 3h8a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4z" />
</symbol>
<symbol
id="icon-play-pause"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 4l10 8-10 8z" />
<path d="M18 6v12" />
</symbol>
<symbol id="icon-stop" viewBox="0 0 24 24" fill="currentColor">
<rect x="6" y="6" width="12" height="12" rx="1" ry="1" />
</symbol>
</svg>
<div id="loading-overlay" role="status" aria-live="polite" style="display: none">
<img src="editor-favicon.png" alt="" class="spinner-img" />
<div class="spinner-label">Loading...</div>
</div>
<header class="topbar" role="banner">
<div class="topbar-left">
<span class="app-title" id="app-title">Q-Edit v1.1.1</span>
</div>
<div class="topbar-right">
<div id="media-controls" class="media-controls" style="display: none">
<button
id="media-play-pause"
class="icon-button"
title="Play/Pause"
aria-label="Play/Pause"
>
<svg class="icon" aria-hidden="true">
<use href="#icon-play-pause" xlink:href="#icon-play-pause"></use>
</svg>
</button>
<button id="media-stop" class="icon-button" title="Stop" aria-label="Stop">
<svg class="icon" aria-hidden="true">
<use href="#icon-stop" xlink:href="#icon-stop"></use>
</svg>
</button>
<span id="media-title" class="media-title" title=""></span>
</div>
<button id="search-button" class="icon-button" title="Search" aria-label="Search">
<svg class="icon" aria-hidden="true">
<use href="#icon-search" xlink:href="#icon-search"></use>
</svg>
</button>
<div id="publish-menu" class="publish-menu" style="display: none">
<button
id="publish-button"
class="icon-button"
aria-haspopup="true"
aria-expanded="false"
title="Publish"
>
<svg class="icon" aria-hidden="true">
<use href="#icon-upload" xlink:href="#icon-upload"></use>
</svg>
<span>Publish</span>
</button>
<div id="publish-dropdown" class="publish-dropdown" role="menu" aria-hidden="true">
<button type="button" class="publish-item" id="publish-add-file" role="menuitem">
Add file...
</button>
<button type="button" class="publish-item" id="publish-add-folder" role="menuitem">
Add folder...
</button>
<button type="button" class="publish-item" id="publish-new-text" role="menuitem">
New text...
</button>
</div>
</div>
<button
id="toggle-deleted"
class="icon-button deleted-toggle"
title="Hide deleted content"
aria-label="Hide deleted content"
aria-pressed="true"
>
<svg class="icon" aria-hidden="true">
<use href="#icon-trash" xlink:href="#icon-trash"></use>
</svg>
</button>
<button id="auth-button" class="auth-button">Authenticate</button>
<div id="name-switcher" class="name-switcher" style="display: none">
<label for="name-select" class="visually-hidden">Active name</label>
<select id="name-select" class="name-select"></select>
</div>
</div>
</header>
<main id="app-shell">
<aside id="sidebar" aria-label="Navigation">
<div id="sidebar-banner" class="sidebar-banner">
<button
id="sidebar-collapse"
type="button"
class="icon-button"
title="Hide sidebar"
aria-label="Hide sidebar"
aria-controls="sidebar"
aria-expanded="true"
aria-pressed="false"
onclick="window.QEditToggleSidebar && window.QEditToggleSidebar('toggle'); return false;"
>
<svg class="icon" aria-hidden="true">
<use href="#icon-chevron-left" xlink:href="#icon-chevron-left"></use>
</svg>
</button>
<button id="sidebar-context" class="sidebar-context" disabled>
My Files - <span id="sidebar-name">(not authenticated)</span>
</button>
<button id="sidebar-exit-search" class="sidebar-exit" style="display: none">
Back to My Files
</button>
</div>
<nav id="file-tree" class="file-tree" role="tree" aria-label="Files"></nav>
</aside>
<button
id="sidebar-reveal"
type="button"
class="sidebar-reveal icon-button"
title="Show sidebar"
aria-label="Show sidebar"
style="display: none"
onclick="window.QEditToggleSidebar && window.QEditToggleSidebar('show'); return false;"
>
<svg class="icon" aria-hidden="true">
<use href="#icon-chevron-right" xlink:href="#icon-chevron-right"></use>
</svg>
</button>
<section id="main-pane">
<section id="search-page" style="display: none">
<form id="search-form" class="search-form" autocomplete="off">
<div class="form-row">
<label for="search-query">Query</label>
<input
id="search-query"
name="query"
type="text"
placeholder="keywords (matches name or identifier)"
/>
</div>
<div class="form-row">
<label for="search-name">Name</label>
<input
id="search-name"
name="name"
type="text"
placeholder="optional: restrict to a Qortal name"
/>
</div>
<div class="form-row">
<label for="search-identifier">Identifier</label>
<input
id="search-identifier"
name="identifier"
type="text"
placeholder="optional: identifier-only match"
/>
</div>
<div class="form-row">
<label for="search-service">Service</label>
<input
id="search-service"
name="service"
type="text"
placeholder="type or choose: e.g. IMAGE, VIDEO, BLOG_POST"
/>
</div>
<div class="form-row small">
<label><input id="search-prefix" name="prefix" type="checkbox" /> Prefix</label>
<label
><input
id="search-include-metadata"
name="includeMetadata"
type="checkbox"
checked
/>
Include metadata</label
>
<label
><input id="search-exact-names" name="exactMatchNames" type="checkbox" /> Exact
match names</label
>
<label
><input id="search-reverse" name="reverse" type="checkbox" checked /> Newest
first</label
>
</div>
<div class="form-row">
<label for="search-limit">Limit</label>
<select id="search-limit" name="limit">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100" selected>100</option>
<option value="200">200</option>
</select>
<button type="submit" id="search-submit">Search</button>
<button type="button" id="search-reset">Reset</button>
</div>
</form>
<div id="search-summary" class="search-summary"></div>
<div id="search-results" class="search-results"></div>
<div id="search-more" class="search-more" style="display: none">
<button id="search-load-more" type="button">Load more</button>
</div>
</section>
<section id="preview-page" style="display: none">
<div class="preview-header">
<button id="preview-back-btn" class="icon-button" title="Back">
<svg class="icon" aria-hidden="true">
<use href="#icon-chevron-left" xlink:href="#icon-chevron-left"></use>
</svg>
<span>Back</span>
</button>
<div class="preview-title" id="preview-title"></div>
<div id="preview-actions" class="topbar-right">
<button id="preview-edit" class="icon-button" title="Edit">
<svg class="icon" aria-hidden="true">
<use href="#icon-edit" xlink:href="#icon-edit"></use>
</svg>
<span>Edit</span>
</button>
<button id="preview-replace" class="icon-button" title="Replace">
<svg class="icon" aria-hidden="true">
<use href="#icon-folder" xlink:href="#icon-folder"></use>
</svg>
<span>Replace</span>
</button>
<button id="preview-delete" class="icon-button" title="Delete">
<svg class="icon" aria-hidden="true">
<use href="#icon-trash" xlink:href="#icon-trash"></use>
</svg>
<span>Delete</span>
</button>
</div>
</div>
<div id="preview-container" class="viewer-panel"></div>
</section>
<section id="compose-page" style="display: none">
<div class="preview-header">
<button id="compose-back-btn" class="icon-button" title="Back">
<svg class="icon" aria-hidden="true">
<use href="#icon-chevron-left" xlink:href="#icon-chevron-left"></use>
</svg>
<span>Back</span>
</button>
<div class="preview-title" id="compose-title">Compose New Text</div>
<div class="topbar-right">
<button id="compose-publish" class="icon-button" title="Publish">
<svg class="icon" aria-hidden="true">
<use href="#icon-upload" xlink:href="#icon-upload"></use>
</svg>
<span>Publish</span>
</button>
</div>
</div>
<div class="viewer-panel">
<div class="compose-form">
<div class="form-row">
<label for="compose-service">Service</label>
<input
id="compose-service"
type="text"
placeholder="type or choose: e.g. DOCUMENT, JSON"
/>
<div
id="compose-private-note"
style="display: none; margin-top: 6px; color: #ffcc66"
>
Note: Private service detected - your content will be encrypted to your account
only. No other account can decrypt it.
</div>
</div>
<div class="form-row">
<label for="compose-identifier">Identifier (optional)</label>
<input
id="compose-identifier"
type="text"
placeholder="identifier or leave blank for default"
/>
</div>
<div class="form-row">
<button id="compose-edit-metadata" type="button">Edit metadata...</button>
<span id="compose-metadata-summary" class="metadata-summary"></span>
</div>
<div class="form-row">
<label for="compose-text">Content</label>
<textarea
id="compose-text"
rows="16"
placeholder="Type your text here..."
></textarea>
</div>
</div>
</div>
</section>
<section id="info-page">
<div id="info-details">
<p>
Welcome to Q-Edit! This app is a work in progress.<br />
Authenticate to view and edit your published QDN content.
</p>
</div>
</section>
<section id="content-page" style="display: none">
<p id="account-details"></p>
<div id="items-per-page" style="margin: 10px 0">
Show
<select id="items-per-page-dropdown">
<option value="10">10</option>
<option value="25" selected>25</option>
<option value="100">100</option>
</select>
results per page
</div>
<div id="filter-options" style="margin: 10px 0">
Filter by service (toggle):
<div id="service-chips-container" class="chips-container"></div>
</div>
<div id="inline-viewer" class="viewer-panel" style="display: none"></div>
<div id="content-summary"></div>
<div id="pagination-top" class="pagination-controls"></div>
<div id="content-details"></div>
<div id="pagination-bottom" class="pagination-controls"></div>
</section>
</section>
</main>
<!-- Runtime app -->
<script src="script.js"></script>
<!-- Preferences + URL hash -->
<script type="module" src="./scripts/init-prefs.mjs"></script>
<script type="module" src="./scripts/init-hash.mjs"></script>
<script type="module" src="./scripts/init-page-hash.mjs"></script>
<script type="module" src="./scripts/init-page-jump.mjs"></script>
<!-- Render helper exposure (non-invasive) -->
<script type="module">
import { html, renderInto } from "./src/ui/render.js";
// Expose for devtools and future incremental adoption
window.QEditRender = { html, renderInto };
// Non-invasive placeholder: only if summary is empty before data load.
const host = document.getElementById("content-summary");
if (host && host.children.length === 0) {
const markup = html`<div class="summary-bar"><span>Ready.</span></div>`;
renderInto(host, markup);
}
</script>
<script type="module" src="./scripts/init-pagination-ui.mjs"></script>
<script type="module" src="./scripts/init-sidebar.mjs"></script>
</body>
</html>