clone

one developer profile for links and private tcp services, reached peer to peer over iroh with no open ports

Themes are set from the CLI, like everything else on a profile

Dami committed 06bd0da
The website had one control that wrote to the server: an Appearance panel for
editing your theme. Nothing else about a profile is editable there — links,
exposures and sharing are all `devsite` commands — so the panel was the odd one
out, and a theme is no more a browser concern than an exposure is.

Removed. `devsite theme set|show|clear|properties` was always the other half of
this and is now the whole of it. The website reads; the CLI writes. The one
exception stays where it has to: claiming a handle happens in the browser,
because that is where signing in finishes.

/api/theme keeps its GET and PUT — they are what the CLI talks to. Their auth is
unchanged, and deliberately: /api/resources accepts a session cookie too, so
"CLI only" is a statement about where the controls live, not a new restriction on
who may call what.

`is_owner` goes with it. It existed to decide whether to draw the panel, and
nothing reads it now — what an owner sees that others do not is already carried
by the entries themselves, and by the "shared with me" list that is populated for
them alone.
commit
06bd0da6292b57ea10a00e9128d0685f4673403e
parent
cb8d53952fb75bcb4a503f83344fd78406c364b0
author
Dami <dami.etoile@gmail.com>
3 files changed, +15 -57
Mcrates/devsite-server/src/api.rs6++-
Mdocs/profile-template.md9+++--
Mweb/app.js57+++---------------------------
M crates/devsite-server/src/api.rs +4 -2
M docs/profile-template.md +5 -4
M web/app.js +6 -51
@@ -1,5 +1,11 @@
11 // dev.site — profile rendering, Shoo sign-in, and opening a local service over Iroh.22 //3+// The website reads; `devsite` writes. Links, exposures, sharing and themes are4+// all set from the CLI, so the only thing here that changes server state is5+// claiming a handle — which has to happen in the browser because that is where6+// you finish signing in. Anything else that mutates a profile belongs in the7+// CLI, next to the rest of it.8+//39 // The markup this file produces is a contract, not an implementation detail: a410 // profile's theme is a list of --pico-* assignments that only mean anything511 // against the elements below. Keep it semantic, keep it documented, and change
@@ -263,57 +269,6 @@ function renderProfile(profile) {
263269 }264270 265271 main.append(article);266- if (profile.is_owner) main.append(appearanceEditor(profile));267-}268-269-/// The theme editor, on your own profile only.270-///271-/// It deliberately shows the stored text rather than a set of colour pickers:272-/// the thing being edited really is CSS, and the server's refusals are specific273-/// enough to be worth reading.274-function appearanceEditor(profile) {275- const details = document.createElement('details');276- details.className = 'appearance';277- details.innerHTML = `278- <summary>Appearance</summary>279- <form>280- <label for="theme-css">281- Pico variables, as declarations. Everything else is refused.282- <textarea id="theme-css" name="css" rows="8" spellcheck="false"283- placeholder="--devsite-scheme: dark;&#10;--pico-primary: #7b3fe4;&#10;--pico-border-radius: 0.5rem;"></textarea>284- </label>285- <small id="theme-note">286- <a href="/api/theme/properties" target="_blank" rel="noopener">What you may set</a>287- </small>288- <fieldset role="group">289- <button type="submit">Save</button>290- <button type="button" class="secondary outline" id="theme-clear">Clear</button>291- </fieldset>292- </form>`;293-294- const form = details.querySelector('form');295- const field = details.querySelector('#theme-css');296- const note = details.querySelector('#theme-note');297- field.value = profile.theme298- .map((d) => `${d.property}: ${d.value};`)299- .join('\n');300-301- const save = async (css) => {302- try {303- const saved = await api('/api/theme', { method: 'PUT', body: JSON.stringify({ css }) });304- field.value = saved.css.trimEnd();305- field.removeAttribute('aria-invalid');306- note.innerHTML = '<span class="ok">Saved.</span>';307- applyTheme(profile.handle, saved.theme);308- } catch (err) {309- field.setAttribute('aria-invalid', 'true');310- note.innerHTML = `<span class="error">${esc(err.message)}</span>`;311- }312- };313-314- form.addEventListener('submit', (e) => { e.preventDefault(); save(field.value); });315- details.querySelector('#theme-clear').addEventListener('click', () => save(''));316- return details;317272 }318273 319274 // -- opening a service ---------------------------------------------------------