Everything a site needs past the first screen — an about page, a blog, a contact page — had to live on that one page or not exist. The folder on Sia could always hold more; the studio could only ever edit index.html. Pages are now a list. Each one is a folder with its own index.html, so it answers to the address a visitor would guess (/about/), and the sidebar shows that address rather than the file behind it. The title is editable and the address is not: a published URL that moves is a link somebody else wrote down and can no longer follow. Draft and Publish both walk the whole list. Pages that link to each other are only coherent if they go up together, so there is one button and it sends everything, writes a sitemap when there is more than one page, and sweeps deleted pages off Sia in the same pass instead of leaving them serving. A site saved before this opens as a single home page and gains the rest when asked. Two things that would otherwise be quietly wrong: assets exported from a sub-folder page now carry the ../ that makes them resolve, and loading a page into the editor no longer registers as an edit.
317 lines
23 KiB
HTML
317 lines
23 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Sirius Studio — build the site for your name</title>
|
||
<meta name="description" content="Sirius Studio: a drag-and-drop site builder for BCNR names. Pages live on Sia, the name on the chain — your keys, your site.">
|
||
<link rel="icon" href="./assets/favicon.svg" type="image/svg+xml">
|
||
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,600&family=DM+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||
<link rel="stylesheet" href="./vendor/grapesjs/grapes.min.css?v=0.23.6">
|
||
<style>
|
||
:root{
|
||
--bg:#050810; --panel:#141a24; --panel2:#0a0f1c; --line:rgba(255,255,255,.08);
|
||
--ink:#f1f4fa; --mut:#b8c2d4; --dim:#6a7488; --acid:#d6ff3d; --ok:#4fd1a5; --taken:#f6768a; --warn:#ffc75f;
|
||
--serif:'Fraunces',Georgia,serif; --sans:'DM Sans',system-ui,sans-serif; --mono:'JetBrains Mono',ui-monospace,monospace;
|
||
/* GrapesJS theme hooks */
|
||
--gjs-primary-color:#0e131b; --gjs-secondary-color:#b8c2d4; --gjs-tertiary-color:#d6ff3d; --gjs-quaternary-color:#d6ff3d;
|
||
--gjs-font-color:#f1f4fa; --gjs-main-dark-color:#0a0f1c; --gjs-main-light-color:#141a24;
|
||
}
|
||
*{box-sizing:border-box}
|
||
html,body{height:100%;margin:0}
|
||
body{background:var(--bg);color:var(--ink);font:14px/1.5 var(--sans);display:flex;flex-direction:column;overflow:hidden}
|
||
a{color:var(--acid)}
|
||
.bar{display:flex;align-items:center;gap:10px;padding:8px 14px;background:rgba(11,14,20,.95);border-bottom:1px solid var(--line);flex:none;flex-wrap:wrap}
|
||
.bar .brand{font-weight:600;color:var(--ink);text-decoration:none;font-size:15px;display:flex;align-items:center;gap:6px}
|
||
.bar .brand .g{color:var(--acid)}
|
||
.bar .name{font-family:var(--mono);font-size:14px;color:var(--ink);background:var(--panel);border:1px solid var(--line);padding:5px 10px;border-radius:8px}
|
||
.bar .name .tld{color:var(--acid)}
|
||
.bar .sp{flex:1}
|
||
.bar .st{font-size:12.5px;color:var(--dim);max-width:360px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||
.bar .st.ok{color:var(--ok)} .bar .st.err{color:var(--taken)} .bar .st.busy{color:var(--warn)}
|
||
.notice{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:8px 14px;font-size:13px;color:var(--ink);
|
||
background:rgba(255,199,95,.10);border-bottom:1px solid rgba(255,199,95,.35);flex:none}
|
||
.notice[hidden]{display:none}
|
||
.notice .x{margin-left:auto;background:none;border:0;color:var(--dim);font-size:18px;cursor:pointer;line-height:1}
|
||
.btn{display:inline-flex;align-items:center;gap:6px;padding:7px 13px;border-radius:9px;background:#4b7bec;color:#fff;font:inherit;font-size:13px;border:none;cursor:pointer;text-decoration:none;line-height:1.2}
|
||
.btn.ghost{background:transparent;border:1px solid var(--line);color:var(--ink)}
|
||
.btn.acid{background:var(--acid);color:#0b0e14;font-weight:600}
|
||
.btn.small{padding:5px 10px;font-size:12px}
|
||
.btn:disabled{opacity:.45;cursor:not-allowed}
|
||
select.sel{padding:6px 28px 6px 10px;border-radius:9px;background:var(--panel);color:var(--ink);border:1px solid var(--line);font:inherit;font-size:13px}
|
||
#editor{flex:1;min-height:0;display:flex;min-width:0}
|
||
#gjs{height:100%;flex:1;min-width:0}
|
||
/* page tree */
|
||
.pages{width:178px;flex:none;display:flex;flex-direction:column;background:#0b0f18;border-right:1px solid var(--line);overflow:auto}
|
||
.pages-head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 8px 8px 12px;flex:none}
|
||
.pages-head b{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--dim)}
|
||
.pg-add{background:transparent;border:1px solid var(--line);color:var(--ink);border-radius:7px;width:24px;height:24px;font:inherit;font-size:15px;line-height:1;cursor:pointer;padding:0}
|
||
.pg-add:hover,.pg-add:focus-visible{border-color:rgba(214,255,61,.45);color:var(--acid);outline:none}
|
||
.pages-list{list-style:none;margin:0;padding:0 6px;flex:1}
|
||
.pg{position:relative;display:flex;align-items:stretch;border-radius:8px;margin-bottom:2px}
|
||
.pg:hover{background:rgba(255,255,255,.04)}
|
||
.pg.on{background:rgba(214,255,61,.10);box-shadow:inset 2px 0 0 var(--acid)}
|
||
.pg-go{flex:1;min-width:0;display:flex;flex-direction:column;align-items:flex-start;gap:1px;background:none;border:0;color:inherit;font:inherit;text-align:left;padding:7px 4px 7px 10px;cursor:pointer;border-radius:8px}
|
||
.pg-go:focus-visible{outline:2px solid rgba(214,255,61,.6);outline-offset:-2px}
|
||
.pg-t{font-size:13px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||
.pg-p{font-family:var(--mono);font-size:10.5px;color:var(--dim);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||
.pg.on .pg-t{color:var(--acid)}
|
||
/* Unsaved-changes marker. It sits where the row actions appear on hover,
|
||
so it steps aside rather than fighting them for the corner. */
|
||
.pg-dot{position:absolute;right:9px;top:50%;margin-top:-3px;width:6px;height:6px;border-radius:50%;background:var(--warn);pointer-events:none;transition:opacity .12s}
|
||
.pg:hover .pg-dot,.pg:focus-within .pg-dot{opacity:0}
|
||
.pg-acts{display:flex;align-items:center;gap:1px;padding-right:4px;opacity:0;transition:opacity .12s}
|
||
.pg:hover .pg-acts,.pg:focus-within .pg-acts{opacity:1}
|
||
.pg-act{background:none;border:0;color:var(--dim);font:inherit;font-size:13px;line-height:1;cursor:pointer;padding:4px 5px;border-radius:6px}
|
||
.pg-act:hover,.pg-act:focus-visible{color:var(--ink);background:rgba(255,255,255,.08);outline:none}
|
||
.pages-hint{flex:none;margin:0;padding:10px 12px 12px;font-size:11px;line-height:1.45;color:var(--dim);border-top:1px solid var(--line)}
|
||
/* Below the editor's own breakpoint the tree becomes a strip: a phone has
|
||
no room for a column, but the page list still has to be reachable. */
|
||
@media (max-width:820px){
|
||
#editor{flex-direction:column}
|
||
.pages{width:auto;flex-direction:row;align-items:center;gap:6px;border-right:0;border-bottom:1px solid var(--line);overflow-x:auto;overflow-y:hidden;padding:0 8px}
|
||
.pages-head{padding:6px 0}
|
||
.pages-head b{display:none}
|
||
.pages-list{display:flex;gap:4px;padding:6px 0;flex:0 0 auto}
|
||
.pg{margin:0}
|
||
.pg-go{padding:5px 8px;white-space:nowrap}
|
||
.pg-p{display:none}
|
||
.pg-acts{opacity:1}
|
||
.pages-hint{display:none}
|
||
}
|
||
/* assistant drawer */
|
||
.ai{width:340px;flex:none;display:flex;flex-direction:column;gap:8px;padding:12px 14px;background:#0e131b;border-left:1px solid var(--line);overflow:auto;font-size:13px}
|
||
.ai[hidden]{display:none}
|
||
.ai-head{display:flex;align-items:center;justify-content:space-between}
|
||
.ai-head b{font-size:14px}
|
||
.ai .x{background:none;border:0;color:var(--dim);font-size:20px;cursor:pointer;line-height:1}
|
||
.ai label{display:block;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--dim);margin:6px 0 3px}
|
||
.ai select,.ai input,.ai textarea{width:100%;padding:8px 10px;border-radius:8px;border:1px solid rgba(255,255,255,.12);background:#070a12;color:var(--ink);font:inherit;font-size:13px}
|
||
.ai textarea{resize:vertical;min-height:72px}
|
||
.ai-hint{color:var(--dim);font-size:11.5px;line-height:1.4}
|
||
.ai-hint code{font-size:11px}
|
||
.ai-status{font-size:12px;color:var(--mut);padding:7px 10px;border-radius:8px;background:rgba(255,255,255,.04);border-left:3px solid rgba(255,255,255,.12);min-height:30px}
|
||
.ai-status.ok{border-left-color:var(--ok);color:var(--ok)} .ai-status.err{border-left-color:var(--taken);color:var(--taken)} .ai-status.busy{border-left-color:var(--warn)} .ai-status.warn{border-left-color:var(--warn);color:var(--warn)}
|
||
.ai-verbs{display:flex;flex-wrap:wrap;gap:6px}
|
||
.ai-target{font-size:11.5px;color:var(--dim);font-family:var(--mono);word-break:break-word}
|
||
.ai-out{flex:1;min-height:80px;max-height:40vh;margin:0;padding:8px 10px;background:#070a12;border:1px solid rgba(255,255,255,.08);border-radius:8px;font-family:var(--mono);font-size:11px;color:var(--mut);white-space:pre-wrap;word-break:break-word;overflow:auto}
|
||
.ai-actions{display:flex;gap:6px}
|
||
.bar .btn.on{background:rgba(214,255,61,.14);border-color:rgba(214,255,61,.4);color:var(--acid)}
|
||
@media (max-width:900px){.ai{position:fixed;right:0;top:0;bottom:0;width:min(340px,92vw);z-index:60;box-shadow:-12px 0 30px rgba(0,0,0,.5)}}
|
||
/* Editor theme: GrapesJS 0.23 is themed through CSS variables. Dark panels,
|
||
near-white text (>= 12:1), acid accents. The preset's own mauve palette is
|
||
switched off in studio.js (useCustomTheme:false). */
|
||
:root{
|
||
--gjs-primary-color:#0e131b; /* panel backgrounds */
|
||
--gjs-secondary-color:#e7ebf2; /* panel text */
|
||
--gjs-tertiary-color:#d6ff3d; /* active buttons */
|
||
--gjs-quaternary-color:#d6ff3d; /* highlights, selection badge */
|
||
--gjs-main-color:#0e131b;
|
||
--gjs-main-dark-color:rgba(0,0,0,.35); /* inputs */
|
||
--gjs-main-light-color:rgba(255,255,255,.07);
|
||
--gjs-secondary-light-color:rgba(255,255,255,.82);
|
||
--gjs-secondary-dark-color:rgba(0,0,0,.3);
|
||
--gjs-font-color:#e7ebf2;
|
||
--gjs-font-color-active:#ffffff;
|
||
--gjs-light-border:rgba(255,255,255,.09);
|
||
--gjs-arrow-color:rgba(255,255,255,.8);
|
||
--gjs-color-highlight:#d6ff3d;
|
||
--gjs-font-size:.8rem;
|
||
--gjs-main-font:'DM Sans',system-ui,sans-serif;
|
||
}
|
||
.gjs-three-bg,.gjs-pn-btn.gjs-pn-active{color:#0b0e14 !important}
|
||
.gjs-pn-btn.gjs-pn-active{background:var(--acid);border-radius:6px}
|
||
.gjs-pn-panel{border-color:var(--line)}
|
||
.gjs-pn-views-container{box-shadow:none;border-left:1px solid var(--line)}
|
||
.gjs-block{min-height:78px;border:1px solid rgba(255,255,255,.08);border-radius:10px;margin:6px;padding:10px 6px;transition:border-color .12s,background .12s}
|
||
.gjs-block:hover{border-color:rgba(214,255,61,.45);background:rgba(214,255,61,.06)}
|
||
.gjs-block-label{font-size:11.5px;line-height:1.25;margin-top:6px}
|
||
.gjs-block svg{fill:#e7ebf2}
|
||
.gjs-block-category .gjs-title,.gjs-sm-sector-title,.gjs-layer-title{font-weight:600;letter-spacing:.02em}
|
||
.gjs-sm-sector .gjs-sm-property .gjs-sm-label,.gjs-trt-trait .gjs-label,.gjs-layer-name{color:#e7ebf2}
|
||
.gjs-field{border:1px solid rgba(255,255,255,.1);border-radius:6px}
|
||
.gjs-field:focus-within{border-color:rgba(214,255,61,.6)}
|
||
.gjs-field input,.gjs-field select,.gjs-field textarea{color:#fff}
|
||
.gjs-radio-item-label,.gjs-sm-btn{color:#e7ebf2}
|
||
.gjs-sm-btn{background:rgba(255,255,255,.08);border-radius:6px}
|
||
.gjs-clm-tag{background:rgba(214,255,61,.14);color:#d6ff3d}
|
||
.gjs-selected{outline:2px solid #d6ff3d !important}
|
||
.gjs-badge{background:#d6ff3d;color:#0b0e14}
|
||
.gjs-toolbar{background:#0e131b;border:1px solid rgba(255,255,255,.12);border-radius:8px}
|
||
.gjs-toolbar-item{color:#fff}
|
||
.gjs-rte-toolbar{background:#0e131b;border:1px solid rgba(255,255,255,.12);border-radius:8px}
|
||
.gjs-rte-action{color:#fff;border-right-color:rgba(255,255,255,.08)}
|
||
.gjs-rte-action select,.gjs-rte-action input[type=color]{background:#0e131b;color:#fff;border:1px solid rgba(255,255,255,.15);border-radius:4px;font:inherit;font-size:12px;cursor:pointer}
|
||
.gjs-rte-action input[type=color]{width:24px;height:22px;padding:0}
|
||
.gjs-resizer-h{border-color:#d6ff3d;background:#0b0e14;width:10px;height:10px}
|
||
.gjs-mdl-dialog{background:#141a24;color:#e7ebf2;border:1px solid var(--line)}
|
||
.gjs-mdl-header{border-bottom-color:var(--line)}
|
||
.gjs-cv-canvas{background:#fff}
|
||
/* gate / picker overlays */
|
||
.overlay{position:fixed;inset:0;background:rgba(5,8,16,.92);backdrop-filter:blur(4px);z-index:100;display:flex;align-items:center;justify-content:center;padding:20px;overflow:auto}
|
||
.overlay[hidden]{display:none}
|
||
.sheet{background:var(--panel);border:1px solid var(--line);border-radius:16px;max-width:860px;width:100%;padding:26px 28px}
|
||
.sheet h2{font-family:var(--serif);margin:0 0 .3rem;font-size:1.5rem}
|
||
.sheet p{color:var(--mut);margin:.2rem 0 1rem}
|
||
/* template gallery — the card preview is the template rendered in a
|
||
sandboxed iframe at a quarter size, so a template that breaks shows it
|
||
broken on its own card instead of behind a stale screenshot. */
|
||
.sheet.gal{max-width:940px;position:relative}
|
||
.gal-x{position:absolute;top:14px;right:16px;background:none;border:0;color:var(--dim);font-size:26px;line-height:1;cursor:pointer;padding:0 4px}
|
||
.gal-x:hover,.gal-x:focus-visible{color:var(--ink)}
|
||
.gal-tabs{display:flex;gap:6px;flex-wrap:wrap;margin:16px 0 14px}
|
||
.gal-tab{background:var(--panel2);border:1px solid var(--line);color:var(--mut);border-radius:999px;padding:6px 15px;font:inherit;font-size:12.5px;cursor:pointer;line-height:1.3}
|
||
.gal-tab:hover{border-color:rgba(214,255,61,.35);color:var(--ink)}
|
||
.gal-tab.on{background:var(--acid);border-color:var(--acid);color:#0b0e14;font-weight:600}
|
||
.gal-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:14px;max-height:58vh;overflow:auto;padding:2px 2px 4px}
|
||
.gal-card{background:var(--panel2);border:1px solid var(--line);border-radius:12px;padding:0;overflow:hidden;cursor:pointer;text-align:left;color:inherit;font:inherit;transition:border-color .12s,transform .12s;display:block;width:100%}
|
||
.gal-card:hover,.gal-card:focus-visible{border-color:rgba(214,255,61,.45);outline:none;transform:translateY(-1px)}
|
||
.gal-card:focus-visible{box-shadow:0 0 0 2px rgba(214,255,61,.5)}
|
||
/* Filtering must not display:none a card: that detaches its preview
|
||
iframe and every tab switch would flash white while sixteen srcdoc
|
||
documents reload. Out of flow and zero-sized keeps them alive. */
|
||
.gal-card[hidden]{position:absolute;width:0;height:0;margin:0;padding:0;border:0;opacity:0;overflow:hidden;pointer-events:none}
|
||
.gal-prev{position:relative;height:140px;background:#fff;overflow:hidden;border-bottom:1px solid var(--line)}
|
||
/* The preview is the page rendered four times the card's width and shrunk
|
||
to fit it, so a card fills edge to edge at any column width. */
|
||
.gal-prev iframe{position:absolute;top:0;left:0;width:400%;height:560px;border:0;transform:scale(.25);transform-origin:0 0;pointer-events:none}
|
||
.gal-card b{display:block;padding:10px 12px 2px;font-size:14px}
|
||
.gal-card span{display:block;padding:0 12px 12px;color:var(--dim);font-size:12px;line-height:1.4}
|
||
.gal-none{color:var(--dim);font-size:13px;padding:22px 2px}
|
||
@media (max-width:520px){.gal-grid{grid-template-columns:1fr;max-height:52vh}.sheet.gal{padding:22px 18px}}
|
||
.steps{margin-top:12px;font-family:var(--mono);font-size:12px;color:var(--mut);background:#0e131b;border:1px solid var(--line);border-radius:8px;padding:10px 12px;max-height:160px;overflow:auto}
|
||
.steps div::before{content:"› ";color:var(--acid)}
|
||
.row{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-top:14px}
|
||
.row.end{justify-content:flex-end}
|
||
.muted{color:var(--mut);font-size:13px}
|
||
code{font-family:var(--mono);background:#0e131b;border:1px solid var(--line);border-radius:6px;padding:1px 6px;font-size:12px}
|
||
</style>
|
||
<script src="./js/i18n.js?v=20260921b"></script>
|
||
</head>
|
||
<body>
|
||
<div class="bar">
|
||
<a class="brand" href="./portal.html#studio"><svg width="18" height="18" viewBox="0 0 64 64" style="vertical-align:-4px;margin-right:6px" aria-hidden="true"><rect width="64" height="64" rx="12" fill="#0b0e14"/><path d="M32 4 L36.4 27.6 L60 32 L36.4 36.4 L32 60 L27.6 36.4 L4 32 L27.6 27.6 Z" fill="#d6ff3d"/></svg><span class="g">Sirius</span> Studio</a>
|
||
<span class="name" id="site-name">—</span>
|
||
<button class="btn ghost small" id="btn-templates" title="Start from a template">Templates…</button>
|
||
<!-- The plain list stays behind the gallery: it is what the bar falls back to
|
||
on a narrow layout, and studio.js fills both from the same TEMPLATES map. -->
|
||
<select class="sel" id="tpl-select" title="Start from a template" hidden>
|
||
<option value="">Templates…</option>
|
||
</select>
|
||
<button class="btn ghost small" id="btn-preview" title="Preview the exported page in a new tab">Preview</button>
|
||
<button class="btn ghost small" id="btn-import" title="Replace the editor contents with the page the name serves right now" hidden>↓ Import live site</button>
|
||
<button class="btn ghost small" id="btn-ai" title="AI assistant — runs on your own device">✨ Assistant</button>
|
||
<span class="sp"></span>
|
||
<span class="st" id="status">Loading…</span>
|
||
<button class="btn ghost small" id="btn-draft" disabled>Save draft</button>
|
||
<button class="btn acid small" id="btn-publish" disabled>Publish to Sia →</button>
|
||
<a class="btn ghost small" id="btn-view" href="#" target="_blank" rel="noopener" hidden>View site →</a>
|
||
<span data-lang-slot></span>
|
||
</div>
|
||
<div class="notice" id="notice" hidden><span id="notice-text"></span> <button class="btn ghost small" id="notice-import">Import the live site</button> <button class="x" id="notice-close" title="dismiss">×</button></div>
|
||
<div id="editor">
|
||
<aside class="pages" id="pages">
|
||
<div class="pages-head"><b>Pages</b><button class="pg-add" id="page-add" title="Add a page" aria-label="Add a page">+</button></div>
|
||
<ul class="pages-list" id="page-list"></ul>
|
||
<p class="pages-hint">Publish sends every page at once.</p>
|
||
</aside>
|
||
<div id="gjs"></div>
|
||
<aside class="ai" id="ai" hidden>
|
||
<div class="ai-head"><b>✨ Assistant</b><button class="x" id="ai-close" title="close">×</button></div>
|
||
<p class="ai-hint" style="margin:0">Runs on your own device. Nothing you write or build is sent to Sirius.X or to any AI vendor.</p>
|
||
<label>Runs on</label>
|
||
<select id="ai-provider">
|
||
<option value="webllm">This browser — your GPU, private</option>
|
||
<option value="local">Local endpoint — Ollama, LM Studio</option>
|
||
</select>
|
||
<div id="ai-webllm">
|
||
<label>Model</label>
|
||
<select id="ai-model"></select>
|
||
<div class="ai-hint">Downloads once, then stays in the browser cache. Needs WebGPU (current Chrome, Edge, Firefox, Safari).</div>
|
||
<div class="ai-actions" style="margin-top:6px"><button class="btn ghost small" id="ai-load">Load model</button></div>
|
||
</div>
|
||
<div id="ai-local" hidden>
|
||
<label>Endpoint</label>
|
||
<input type="text" id="ai-url" value="http://localhost:11434/v1" spellcheck="false">
|
||
<label>Model</label>
|
||
<input type="text" id="ai-lmodel" list="ai-lmodels" placeholder="qwen2.5-coder:7b" spellcheck="false"><datalist id="ai-lmodels"></datalist>
|
||
<div class="ai-hint">Allow this site in your runtime first, e.g. <code>OLLAMA_ORIGINS=https://silentmode.st ollama serve</code>. LM Studio: enable CORS in its server settings (port 1234).</div>
|
||
<div class="ai-actions" style="margin-top:6px"><button class="btn ghost small" id="ai-connect">Connect</button></div>
|
||
</div>
|
||
<div class="ai-status" id="ai-status">—</div>
|
||
<label>What do you want?</label>
|
||
<textarea id="ai-prompt" placeholder="A hero section for a bakery in Athens, warm colours, one button that says Order"></textarea>
|
||
<div class="ai-verbs">
|
||
<button class="btn acid small" data-verb="section">Make a section</button>
|
||
<button class="btn ghost small" data-verb="text">Rewrite selected text</button>
|
||
<button class="btn ghost small" data-verb="style">Restyle selection</button>
|
||
</div>
|
||
<div class="ai-target" id="ai-target">Nothing selected</div>
|
||
<pre class="ai-out" id="ai-out"></pre>
|
||
<div class="ai-actions"><button class="btn ghost small" id="ai-undo" hidden>Undo last change</button><button class="btn ghost small" id="ai-stop" hidden>Stop</button></div>
|
||
</aside></div>
|
||
|
||
<!-- sign-in gate -->
|
||
<div class="overlay" id="gate" hidden>
|
||
<div class="sheet" style="max-width:520px">
|
||
<h2>Sign in first</h2>
|
||
<p>Sirius Studio publishes with your wallet's key. Sign in on the dashboard, then come back to this name.</p>
|
||
<div class="row end"><a class="btn acid" id="gate-link" href="./portal.html">Open the dashboard →</a></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- template picker for a fresh site -->
|
||
<div class="overlay" id="picker" hidden>
|
||
<div class="sheet">
|
||
<h2>Build the site for <span id="picker-name" style="font-family:var(--mono);color:var(--acid)">—</span></h2>
|
||
<p>Pick a starting point. Everything is editable — drag blocks in from the right, click any text to change it. Nothing leaves your browser until you press Publish; then the page is stored on Sia and the name points at it.</p>
|
||
<div class="gal-tabs" id="picker-tabs" role="tablist"></div>
|
||
<div class="gal-grid" id="picker-grid"></div>
|
||
<div class="row end"><a class="btn ghost" href="./portal.html#studio">Back to dashboard</a></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- template gallery, opened from the top bar -->
|
||
<div class="overlay" id="gallery" hidden>
|
||
<div class="sheet gal" role="dialog" aria-modal="true" aria-labelledby="gal-title">
|
||
<button class="gal-x" id="gal-close" title="Close" aria-label="Close">×</button>
|
||
<h2 id="gal-title">Start from a template</h2>
|
||
<p>Every preview below is the template itself, drawn small. Picking one replaces what is on the canvas — the published site stays as it is until you press Publish.</p>
|
||
<div class="gal-tabs" id="gal-tabs" role="tablist"></div>
|
||
<div class="gal-grid" id="gal-grid"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- publish dialog -->
|
||
<div class="overlay" id="pub" hidden>
|
||
<div class="sheet" style="max-width:620px">
|
||
<h2>Publish <span id="pub-name" style="font-family:var(--mono);color:var(--acid)">—</span></h2>
|
||
<p id="pub-intro">The page and its assets are uploaded to the name's folder on Sia, signed with your key. If the name does not point at that folder yet, one on-chain update sets it (a few cents of chain dust).</p>
|
||
<p id="pub-intro-multi" hidden>Every page in the list goes up together, and a sitemap.xml is written beside them. Pages you deleted are taken off Sia in the same pass.</p>
|
||
<div class="steps" id="pub-steps"></div>
|
||
<div class="row end">
|
||
<button class="btn ghost" id="pub-close">Close</button>
|
||
<a class="btn acid" id="pub-view" href="#" target="_blank" rel="noopener" hidden>View site →</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script type="importmap">
|
||
{ "imports": { "@bitauth/libauth": "https://silentmode.st/js/libauth.js?v=20260920usd" } }
|
||
</script>
|
||
<script src="./js/session.js?v=20260917dash"></script>
|
||
<script src="./vendor/grapesjs/grapes.min.js?v=0.23.6"></script>
|
||
<script src="./vendor/grapesjs/blocks-basic.js?v=1.0.2b"></script>
|
||
<script src="./vendor/grapesjs/preset-webpage.js?v=1.0.3b"></script>
|
||
<script src="./vendor/grapesjs/plugin-forms.js?v=2.0.6"></script>
|
||
<script src="./vendor/grapesjs/navbar.js?v=1.0.2"></script>
|
||
<script src="./vendor/grapesjs/tabs.js?v=1.0.6"></script>
|
||
<script src="./vendor/grapesjs/tooltip.js?v=0.1.8"></script>
|
||
<script src="./vendor/grapesjs/custom-code.js?v=1.0.2"></script>
|
||
<script src="./vendor/grapesjs/countdown.js?v=1.0.2"></script>
|
||
<script src="./vendor/grapesjs/style-bg.js?v=2.0.2"></script>
|
||
<script src="./vendor/grapesjs/touch.js?v=0.1.1"></script>
|
||
<script src="./vendor/grapesjs/parser-postcss.js?v=1.0.3"></script>
|
||
<script type="module" src="./js/studio.js?v=20260920usd"></script>
|
||
</body>
|
||
</html>
|