Three small helpers under window.zddc.cap, wired into every tool's
build:
cap.at(path) — Promise<AccessView|null>. Fetches
/.profile/access?path=<urlpath> and
memoises per-path for the session.
Used by tools to gate top-of-page
affordances on path_verbs / path_is_admin
/ path_can_elevate_grant.
cap.has(node, verb) — boolean. Reads the listing entry's
verbs string for the named verb.
Falls back to node.writable for 'w'
when verbs is absent (offline FS-API
listings or pre-promotion clients).
cap.handleForbidden(resp, — parses a 403 response's JSON body for
opts) missing_verb and renders an error
toast. When opts.path is supplied AND
the path-scoped access view reports
path_can_elevate_grant covering the
missing verb, the toast appends an
"Elevate" button that flips the
elevation cookie and reloads.
Browse loader.js + tree.js carry the new verbs field through to the
node objects so context-menu gating can call cap.has(node, 'w'|'d')
without changing the legacy node.writable contract. New CSS rule
.zddc-toast__action styles the inline Elevate button.
Concatenation order: cap.js comes after toast.js + elevation.js so
the dependencies (window.zddc.toast, window.zddc.elevation) are
present at module-load time.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
59 lines
1.8 KiB
CSS
59 lines
1.8 KiB
CSS
/* shared/toast.css — single-toast notification styles paired with
|
|
shared/toast.js. Uses BEM-ish .zddc-toast prefix to avoid collisions
|
|
with tool-local .toast classes; the old classifier rules can stay
|
|
alongside until this file is concatenated above them in the build. */
|
|
|
|
.zddc-toast {
|
|
position: fixed;
|
|
bottom: 2rem;
|
|
right: 2rem;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
padding: 0.875rem 1.25rem;
|
|
border-radius: var(--radius);
|
|
border: 1px solid var(--border);
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
z-index: 9000;
|
|
max-width: 400px;
|
|
font-size: 0.875rem;
|
|
cursor: pointer;
|
|
animation: zddc-toast-in 0.3s ease-out;
|
|
}
|
|
|
|
.zddc-toast--success { border-left: 4px solid var(--success); }
|
|
.zddc-toast--error { border-left: 4px solid var(--danger); }
|
|
.zddc-toast--info { border-left: 4px solid var(--info); }
|
|
.zddc-toast--warning { border-left: 4px solid var(--warning); }
|
|
|
|
.zddc-toast--fade {
|
|
animation: zddc-toast-out 0.3s ease-out forwards;
|
|
}
|
|
|
|
@keyframes zddc-toast-in {
|
|
from { transform: translateX(100%); opacity: 0; }
|
|
to { transform: translateX(0); opacity: 1; }
|
|
}
|
|
|
|
@keyframes zddc-toast-out {
|
|
from { transform: translateX(0); opacity: 1; }
|
|
to { transform: translateX(100%); opacity: 0; }
|
|
}
|
|
|
|
/* Inline action button appended to a toast by zddc.cap.handleForbidden
|
|
when an Elevate path is offered. Stops click propagation on its own
|
|
so clicking the button doesn't also dismiss the toast. */
|
|
.zddc-toast__action {
|
|
display: inline-block;
|
|
margin-left: 0.75rem;
|
|
padding: 0.25rem 0.75rem;
|
|
background: var(--accent, var(--text));
|
|
color: var(--bg);
|
|
border: none;
|
|
border-radius: var(--radius);
|
|
font-size: 0.8125rem;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
}
|
|
.zddc-toast__action:hover {
|
|
filter: brightness(1.1);
|
|
}
|