500 lines
18 KiB
HTML
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>
|