Skip to content

fix(dashboard): icon pass — Tabler sprites for the hosted-chrome tool/permission badges (DES-04) - #558

Merged
schubydoo merged 2 commits into
mainfrom
fix/433-icon-pass-hosted-chrome
Jun 24, 2026
Merged

fix(dashboard): icon pass — Tabler sprites for the hosted-chrome tool/permission badges (DES-04)#558
schubydoo merged 2 commits into
mainfrom
fix/433-icon-pass-hosted-chrome

Conversation

@schubydoo

Copy link
Copy Markdown
Owner

#433 DES-04 — the hosted live-view transcript used structural Unicode emoji as iconography (🔧 tool badge, / permission decision), breaking the Tabler sprite-icon system the rest of the UI uses.

Changes

  • Added ic-tool / ic-check / ic-x Tabler outline symbols to _tabler_sprite.html (from tabler/tabler-icons, the source you shared).
  • Swapped 🔧<use href="#ic-tool">, and the resolved permission decision ✓ allowed / ✕ deniedic-check/ic-x sprites (interrupted / answered stay text).

Verification

Dev-verified the new sprites render at the .ico 15×15 size (valid paths). Render-test guards the symbols are present. DES-03's broader prose / status glyphs are intentionally left as-is.

Refs #433 (DES-03/04).

🤖 Generated with Claude Code

@greptile-apps

greptile-apps Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces structural Unicode emoji (🔧, , ) in the hosted live-view transcript with Tabler SVG sprite icons, aligning the hosted-chrome badges with the sprite system used elsewhere in the dashboard.

  • Three new <symbol> entries (ic-tool, ic-check, ic-x) are appended to _tabler_sprite.html; paths match the Tabler outline set and follow the same attribute pattern as existing symbols.
  • dashboard.html swaps the emoji in the tool badge and the resolved-permission span for <svg><use href="#..."/></svg> elements; interrupted and answered states remain text-only as stated in the PR description.
  • A new render-test asserts all three sprite IDs are present in the served page.

Confidence Score: 5/5

Safe to merge — the change is purely presentational, swapping Unicode emoji for inline SVG sprites with no logic changes.

All four decision states are handled correctly by the x-show guards and the x-text ternary. The new sprite symbols are consistent with the existing sprite sheet. The test guards sprite presence at render time, and no existing behaviour is altered.

No files require special attention.

Important Files Changed

Filename Overview
src/clauster/templates/_tabler_sprite.html Adds ic-tool, ic-check, and ic-x symbols; paths and attributes are consistent with the existing sprite definitions.
src/clauster/templates/dashboard.html Tool badge and resolved-permission spans updated to use sprite icons; x-show guards are logically correct for all four decision values.
tests/test_app.py New test validates sprite symbol presence in the rendered page; follows the same TestClient pattern as adjacent GET-only tests.
.changeset/433-icon-pass-hosted-chrome.md Patch-level changeset entry correctly describing the visual badge change.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["it.resolved === true\n(x-if renders span)"] --> B{it.decision}
    B -->|allow| C["ic-check visible\nx-show=true\n+ 'allowed' text"]
    B -->|deny| D["ic-x visible\nx-show=true\n+ 'denied' text"]
    B -->|interrupted| E["both SVGs hidden\n(display:none)\n+ '— session ended' text"]
    B -->|answered| F["both SVGs hidden\n(display:none)\n+ 'answered' text"]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["it.resolved === true\n(x-if renders span)"] --> B{it.decision}
    B -->|allow| C["ic-check visible\nx-show=true\n+ 'allowed' text"]
    B -->|deny| D["ic-x visible\nx-show=true\n+ 'denied' text"]
    B -->|interrupted| E["both SVGs hidden\n(display:none)\n+ '— session ended' text"]
    B -->|answered| F["both SVGs hidden\n(display:none)\n+ 'answered' text"]
Loading

Reviews (3): Last reviewed commit: "fix(dashboard): drop redundant x-cloak o..." | Re-trigger Greptile

Comment thread src/clauster/templates/dashboard.html Outdated
schubydoo and others added 2 commits June 23, 2026 19:20
…/permission badges (DES-04)

The hosted-transcript chrome used structural Unicode emoji as iconography (🔧 tool
badge, ✓/✕ permission decision), breaking the Tabler sprite-icon system the rest of
the UI uses. Add `ic-tool` / `ic-check` / `ic-x` Tabler outline symbols (from
tabler/tabler-icons) and swap the emoji for `<svg><use>` sprites — verified the new
symbols render at the .ico size. DES-03's broader prose ⚠ / status glyphs are left
as-is (a separate call).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ide x-if

The allowed/denied check/x SVGs live inside <template x-if="it.resolved">, so
Alpine only stamps them into the DOM after it has initialised — there is no
pre-Alpine paint window for x-cloak to guard. Removing it drops dead noise; the
server-rendered x-cloak guards elsewhere (orphan badge, connLost alert) stay.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@schubydoo
schubydoo force-pushed the fix/433-icon-pass-hosted-chrome branch from cd84222 to 63cf9dc Compare June 24, 2026 02:21
@schubydoo
schubydoo merged commit 765bae3 into main Jun 24, 2026
23 checks passed
@schubydoo
schubydoo deleted the fix/433-icon-pass-hosted-chrome branch June 24, 2026 02:26
schubydoo pushed a commit that referenced this pull request Jun 24, 2026
Merging this PR tags and publishes **v0.12.3**.

Generated by knope from the `.changeset/` fragments — don't edit it by
hand; add or amend a changeset on the source PR instead.

---

## Features

### Config editor: the **Claustrum (hosted live-view)** block is now
editable in-app (#539).
([#563](#563))

Previously `claustrum.enabled` could only be flipped by hand-editing
`clauster.yml` and
restarting, so a user with claustrum installed had no discoverable way
to turn the hosted
channel on. The editor now surfaces the `claustrum.enabled` master
toggle plus its
operational fields (`socket_path`, `spawn_timeout_seconds`,
`keep_children`,
`request_timeout_seconds`), which grey out when the channel is off (same
depends-on
mechanism as the metrics block). Like every config edit, saving prompts
to restart Clauster
to apply. `claustrum.binary` is intentionally left out — executable
paths stay non-editable,
matching `claude.binary`.

## Fixes

- Dashboard (hosted live view): the tool and permission-decision badges
in the hosted transcript now use Tabler sprite icons instead of Unicode
emoji (🔧 → `tool`, ✓/✕ → `check`/`x`), matching the sprite-icon system
the rest of the UI uses.
([#558](#558))
- Dashboard polish (#433): the project name now truncates at a
**viewport-relative** width (≈10rem on a phone up to 28rem on a wide
screen) instead of a fixed 16rem cap, so long names adapt to the screen
(DES-07). The Active-list "Open in Claude" link now validates that the
session URL is `http(s)` before binding it to the `href`, so a non-http
value can never reach the link (FE-03 hardening).
([#546](#546))
- Dashboard: de-jargon the (opt-in) Maintenance panel. "Reap ghost
environments" → "Clean up leftover environments", with plainer copy
explaining that these are cloud session environments left over after a
session ends (the cloud `Default` and any environment still backing a
live session are never touched), and the destructive action labelled
"Permanently delete — this can't be undone" (the typed-`DELETE` confirm
is unchanged). ([#554](#554))
- Config editor: several correctness + UX papercuts. The usage badge
mode (`usage.mode`) is now authoritative over the deprecated
`usage.show_cost` alias — `show_cost` is flagged deprecated in the panel
(with a plain-language note pointing at `usage.mode`) instead of leaking
its raw docstring, and an explicit `usage.mode` wins if both are set.
The transcript-recap toggle now greys out under the pty (true-resume)
launch mode, where it has no effect. The bridge-log retention knobs
(`logs.retention_max_age_days` / `_max_files` / `_max_total_mb`) are now
editable in-app alongside the other log settings. And a blank
`usage.currency_symbol` falls back to the default symbol instead of
rendering an empty badge.
([#564](#564))
- Security hardening (batch). The reverse-proxy `trusted_ips` allowlist
is now validated as IP/CIDR at load — a malformed entry fails fast
instead of silently never matching at runtime. The opt-in webhook SSRF
guard (`webhooks.block_private_targets`) now **resolves DNS hostnames**
and drops a URL whose name points at an internal IP, closing the
hostname-bypass (a rebinding domain that re-resolves between the check
and the POST remains an acknowledged TOCTOU residual). The clone
`allow_private_hosts` description now states the field semantics rather
than the default's effect. Internal hardening: direct unit tests for the
hosted-stream redactor, a parity test pinning that every WebSocket
endpoint enforces the same auth gate, and an inline note documenting why
the session cookie's `SameSite=Lax` is safe (state changes are
independently Origin-gated).
([#565](#565))
- Config editor: the **Default permission mode** dropdown now shows the
same friendly labels as the "Run Claude here" launch menu (e.g. "Ask
each time (default)", "Plan only (read-only)") instead of bare enum
tokens — the saved value is unchanged. The **recap size limit** now
greys out when "Recap prior transcript on restart" is off, and the
**"Where new sessions run"** and **"Enable metrics line"** descriptions
were rewritten to say what they actually do (bridge launches only; the
per-session CPU / memory / disk-I/O line).
([#544](#544))
- Hosted sessions: fix `ProcessStream` subscriber leaks.
`HostedSession.start()` and `reattach()` now drop their stream
subscription if the spawn/reattach RPC fails or times out (the error
path previously left an undrained subscriber on the stream), and the
pump loop drops its subscription whenever it exits on its own — a
natural agent exit or a daemon-loss error — rather than leaving it until
a later `stop()`/`detach()`.
([#552](#552))
- Dashboard: the live-tail "reconnecting…" and "disconnected" banners no
longer appear falsely while the log is streaming. A `d-flex` utility
class (`display:flex !important`) was overriding the inline
`display:none` that `x-show` uses to hide them, so both banners stayed
pinned visible whenever the log panel was open — regardless of the
tail's actual state. The flex layout now lives on an inner wrapper,
leaving `x-show` free to hide the banner.
([#538](#538))
- Dashboard polish: the UI no longer shows raw enum tokens where a
friendly label exists. The **Run** button and the hosted-session row now
read "Auto-accept edits" / "Never prompt" etc. instead of `acceptEdits`
/ `dontAsk`; the Active-zone **filter chips** use the launch-menu
product names (Desktop / Browser / Fire-and-forget) instead of internal
tokens; and the config editor's **launch-mode, spawn-mode, and
usage-mode** dropdowns get the same friendly labels as permission mode
(the saved value is unchanged). Also fixes two accessibility gaps: the
"First prompt" input and the New-project "Type" radio group are now
properly associated with their labels for screen readers.
([#547](#547))
- Projects zone: the "Show all / Show fewer" toggle now appears in
**every** sort, not only A–Z. In a last-used or cost sort the 6-row cap
now applies by the chosen sort order — showing the top 6 with the toggle
revealing the rest — instead of silently showing the full list with no
toggle. A project with a live session is still never hidden by the cap.
([#545](#545))
- pty bridges: the keeper now waits on its PTY with `poll()` instead of
`select()`. `select.select()` raises "filedescriptor out of range in
select()" once the PTY master file descriptor reaches FD_SETSIZE (1024)
— which a long-lived Clauster managing many bridges/keepers (or running
on a busy host with many open fds) could hit, crashing the keeper's read
loop. `poll()` has no such ceiling.
([#557](#557))
- Security (redaction): the `sk-…` token pattern in the log/stream
redactor now includes `_` in its character class, matching the
GitHub/GitLab/clauster token patterns. Anthropic `sk-ant-…` keys can
contain underscores — without this, such a key would not fully redact
and could leak into the bridge debug log or the live-tail WebSocket
stream. ([#551](#551))
- Bridge runner: fix a race where `stop()` could orphan a bridge.
`stop()` read `bridge_pid` without holding the per-project spawn lock,
so a stop arriving while `spawn()` was suspended in `to_thread(_popen)`
would see `bridge_pid=None`, mark the instance STOPPED, and return —
leaving the freshly-spawned bridge running but untracked. `stop()` now
takes the same lock `spawn()`/`forget()`/`resume()` use, so it waits for
any in-flight spawn to publish its pid before reading it.
([#553](#553))

### Dashboard: unified the two pre-launch warning vocabularies (UX-07).
Both the header ([#560](#560))

system-wide pill and the per-project pill now read as "readiness checks"
— the header
tooltip says "System readiness … affects every launch on this host", the
per-project
pill drops the "preflight" jargon for "N check(s)" with a "for this
project before
launch" tooltip and a "Readiness checks for &lt;project&gt;" detail
heading. One term,
scoped by wording; internal names, API routes, and test hooks are
unchanged.

### Renamed the confusing `claude.resume_mode` config key to
`claude.launch_mode` (#540). The
([#561](#561))

old name read like a resume on/off toggle, but the field actually picks
the bridge launch
mode (`standard` vs `pty`). Existing `clauster.yml` files keep working:
the legacy
`claude.resume_mode` key — and the `CLAUSTER_CLAUDE_RESUME_MODE` env var
— are still
accepted as deprecated aliases that map to `launch_mode` with a warning
(if both the old
and new key are set, the new one wins). Config-editor label and docs
updated.

Co-authored-by: clauster-ci[bot] <289303168+clauster-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant