Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions umbod/frontend/src/app.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
:root {
color: #f5f7fb;
background: #111827;
color-scheme: light;
color: var(--admin-ink);
background: var(--admin-canvas);
font-family:
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
-webkit-font-smoothing: antialiased;
}

body {
Expand All @@ -13,3 +15,8 @@ body {
a {
color: inherit;
}

:focus-visible {
outline: 3px solid var(--admin-focus);
outline-offset: 3px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ const mcpPage = readFileSync('src/lib/components/admin/downstream-mcp-connectors

test('MCP workspace buttons expose pointer and disabled cursor styles', () => {
expect(mcpPage).toContain('<ConnectorPageHeader');
expect(connectorPageHeader).toContain('cursor: pointer');
expect(connectorPageHeader).toContain('<Button');
const button = readFileSync('src/lib/components/admin/shared/Button.svelte', 'utf8');
expect(button).toContain('cursor: pointer');
expect(button).toContain('cursor: not-allowed');
expect(button).toContain('.admin-shared-button:focus-visible');
expect(mcpWorkspace).toContain('button:not(:disabled){cursor:pointer}');
expect(mcpWorkspace).toContain('button:disabled{cursor:not-allowed}');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ describe('admin connector shared presentation', () => {
expect(pageHeader).toContain('class="connector-page-header"');
expect(pageHeader).toContain('aria-label={addLabel}');
expect(pageHeader).toContain('onclick={(event) => onadd(event.currentTarget)}');
expect(pageHeader).toContain('.add:focus-visible');
expect(pageHeader).toContain("import Button from '$lib/components/admin/shared/Button.svelte'");
expect(pageHeader).toContain('<Button');
});

test('keeps connector workspaces keyed without effect synchronization', () => {
Expand Down
17 changes: 17 additions & 0 deletions umbod/frontend/src/lib/components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,23 @@

**Read when working with:** Shared UI behavior, visual components, accessibility-visible controls, or reusable interaction patterns.

## Admin visual styling

`../styles/admin.css` defines semantic theme variables adapted from the website and
documentation palette. The admin shell, native/OpenAPI/MCP connector workspaces,
prompt and resource catalogs, group permissions, MCP setup, instance configuration,
configuration and confirmation dialogs, and shared controls and feedback consume
these variables in component-scoped styles. The home page, app header and global
light-theme defaults use the same palette.
Keep presentation changes in those components; feature state and API adapters stay
in their existing modules. Use the semantic variables for surfaces, text, actions,
focus and feedback instead of duplicating literal colours. Keep intentional vendor
logo colors in the MCP setup guide.

The shell and app header use `static/umbod-logo.svg`, copied from the website's
existing logo. `AdminShell` provides the inline-size container used by
`AdminSplitWorkspace` to stack its sidebar when the content area is narrow.

## Submodules

### `admin/`
Expand Down
54 changes: 30 additions & 24 deletions umbod/frontend/src/lib/components/admin/AdminShell.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
<div class="admin-shell">
<aside class="sidebar" aria-label="Admin sidebar">
<div class="sidebar-top">
<a class="brand" href="/admin">Umbod</a>
<a class="brand" href="/admin"><img src="/umbod-logo.svg" alt="" width="40" height="40" /><span>Umbod</span></a>
<p class="workspace">Administration</p>

<nav class="navigation" aria-label="Admin navigation">
Expand Down Expand Up @@ -85,33 +85,35 @@
background: var(--admin-canvas);
color: var(--admin-ink);
display: grid;
grid-template-columns: 220px minmax(0, 1fr);
grid-template-columns: 232px minmax(0, 1fr);
height: 100vh;
overflow: hidden;
}

.sidebar {
background:
radial-gradient(circle at 0 0, var(--admin-accent-soft), transparent 13rem),
var(--admin-sidebar);
background: var(--admin-sidebar);
border-right: 1px solid var(--admin-border);
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100vh;
min-height: 0;
padding: 18px 12px;
min-width: 0;
padding: 26px 16px 18px;
}

.brand {
border-radius: 6px;
align-items: center;
border-radius: var(--admin-radius);
color: var(--admin-ink);
display: block;
font-size: 15px;
display: flex;
gap: 8px;
font-size: 24px;
font-weight: 650;
letter-spacing: -0.06em;
line-height: 1.3;
padding: 7px 8px;
padding: 0 4px;
text-decoration: none;
}

Expand All @@ -122,28 +124,32 @@

.workspace {
color: var(--admin-muted);
font-size: 12px;
font-family: var(--admin-mono);
font-size: 10px;
letter-spacing: 0.08em;
line-height: 1.4;
margin: 2px 8px 18px;
margin: 18px 12px 12px;
text-transform: uppercase;
}

.navigation {
display: grid;
gap: 2px;
gap: 4px;
}

.navigation-link {
border-radius: 6px;
color: var(--admin-ink);
border-radius: var(--admin-radius);
color: var(--admin-muted);
font-size: 14px;
line-height: 1.35;
padding: 7px 8px;
padding: 10px 12px;
text-decoration: none;
}

.navigation-link.active {
background: linear-gradient(90deg, var(--admin-accent-soft), var(--admin-border));
box-shadow: inset 3px 0 0 var(--admin-accent);
background: var(--admin-accent-soft);
color: var(--admin-accent);
box-shadow: inset 2px 0 0 var(--admin-accent);
font-weight: 600;
}

Expand Down Expand Up @@ -186,18 +192,17 @@
}

.content-shell {
background:
radial-gradient(circle at 100% 0, rgb(147 197 253 / 8%), transparent 18rem),
var(--admin-canvas);
background: var(--admin-canvas);
box-sizing: border-box;
min-height: 0;
min-width: 0;
overflow: auto;
position: relative;
}

.content-loading {
align-items: center;
background: rgb(235 235 232 / 78%);
background: var(--admin-loading-bg);
display: flex;
inset: 0;
justify-content: center;
Expand All @@ -217,8 +222,9 @@

.content-width {
box-sizing: border-box;
container-type: inline-size;
max-width: 860px;
padding: 52px 60px;
padding: 48px clamp(24px, 4vw, 56px);
width: 100%;
}

Expand All @@ -228,7 +234,7 @@

@media (max-width: 720px) {
.admin-shell {
grid-template-columns: 1fr;
grid-template-columns: minmax(0, 1fr);
height: auto;
min-height: 100vh;
overflow: visible;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,18 @@
.catalog.embedded { border-top: 0; margin-top: 0; padding-top: 0; }
.catalog-heading, .item-heading { align-items: center; display: flex; gap: 0.5rem; }
.catalog-heading { margin-bottom: 0.875rem; }
.item-heading { justify-content: space-between; }
.item-heading { align-items: flex-start; justify-content: space-between; }
.catalog-heading span { background: var(--admin-sidebar); border: 1px solid var(--admin-border); border-radius: 999px; color: var(--admin-muted); font-size: 0.75rem; font-weight: 650; padding: 0.1rem 0.45rem; }
.catalog > p, li > p, dd { color: var(--admin-muted); }
.catalog > p { background: var(--admin-soft); border: 1px solid var(--admin-border); border-radius: var(--admin-radius); font-size: 14px; line-height: 1.55; margin: 0; padding: 1rem; }
h3, h4 { color: var(--admin-ink); margin: 0; }
h3 { font-size: 1rem; }
h4 { font-size: 0.94rem; overflow-wrap: anywhere; }
li { background: #fff; border: 1px solid var(--admin-border); border-radius: 10px; min-width: 0; padding: 1rem; }
li { background: var(--admin-panel); border: 1px solid var(--admin-border); border-radius: var(--admin-radius); min-width: 0; padding: 1rem; }
li > p, dl { margin: 0.55rem 0 0; }
li > p, dd { font-size: 0.875rem; line-height: 1.5; overflow-wrap: anywhere; }
dl { border-top: 1px solid var(--admin-border); display: grid; gap: 0.55rem; padding-top: 0.75rem; }
dl div { display: grid; gap: 0.15rem; }
dt { font-size: 0.8125rem; font-weight: 650; overflow-wrap: anywhere; }
dt { color: var(--admin-ink); font-family: var(--admin-mono); font-size: 0.75rem; font-weight: 650; overflow-wrap: anywhere; }
dd { margin: 0; }
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,11 @@
.resource-title { align-items: flex-start; flex-wrap: wrap; }
.catalog-heading span, .resource-title span { background: var(--admin-sidebar); border: 1px solid var(--admin-border); border-radius: 999px; color: var(--admin-muted); font-size: 0.75rem; font-weight: 650; padding: 0.1rem 0.45rem; }
.catalog > p, li > p { color: var(--admin-muted); }
.catalog > p { background: var(--admin-soft); border: 1px solid var(--admin-border); border-radius: var(--admin-radius); font-size: 14px; line-height: 1.55; margin: 0; padding: 1rem; }
h3, h4 { color: var(--admin-ink); margin: 0; }
h3 { font-size: 1rem; }
h4 { font-size: 0.94rem; overflow-wrap: anywhere; }
li { background: #fff; border: 1px solid var(--admin-border); border-radius: 10px; min-width: 0; padding: 1rem; }
li { background: var(--admin-panel); border: 1px solid var(--admin-border); border-radius: var(--admin-radius); min-width: 0; padding: 1rem; }
li > p { font-size: 0.875rem; line-height: 1.5; margin: 0.55rem 0 0.75rem; overflow-wrap: anywhere; }
code { background: var(--admin-sidebar); border: 1px solid var(--admin-border); border-radius: 6px; display: block; font-size: 0.75rem; line-height: 1.5; overflow-wrap: anywhere; padding: 0.4rem 0.55rem; }
code { background: var(--admin-soft); color: var(--admin-ink); font-family: var(--admin-mono); border: 1px solid var(--admin-border); border-radius: 6px; display: block; font-size: 0.75rem; line-height: 1.5; overflow-wrap: anywhere; padding: 0.4rem 0.55rem; }
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,19 @@
{#if state.message}<p role="status">{state.message}</p>{/if}
</section>

<style>.override{border-top:1px solid #e9e9e7;display:grid;gap:.5rem;margin-top:1.5rem;padding-top:1rem}.override h3{margin:0}.override textarea{border:1px solid #d9d9d6;border-radius:6px;font:inherit;min-height:4rem;padding:.6rem;resize:vertical}.override textarea:read-only{background:#f7f7f5;color:#5f5e5b}.meta,.actions{display:flex;gap:.75rem;justify-content:space-between}.error{color:#9f2d20}.actions{justify-content:flex-start}</style>
<style>
.override { color: var(--admin-ink); display: grid; gap: .75rem; margin-top: 1.25rem; min-height: 0; overflow-y: auto; padding: .25rem; }
.override h3 { font-size: 1rem; margin: 0 0 .25rem; }
.override label { font-size: 13px; font-weight: 600; }
.override textarea { background: var(--admin-panel); border: 1px solid var(--admin-border-strong); border-radius: var(--admin-radius); box-sizing: border-box; color: var(--admin-ink); font: inherit; font-size: 14px; line-height: 1.55; min-height: 5rem; min-width: 0; padding: .7rem; resize: vertical; width: 100%; }
.override textarea:read-only { background: var(--admin-soft); color: var(--admin-muted); }
.override textarea:focus-visible { border-color: var(--admin-accent); outline: 2px solid var(--admin-focus); outline-offset: 1px; }
.override textarea[aria-invalid='true'] { border-color: var(--admin-danger); }
.meta, .actions { display: flex; flex-wrap: wrap; gap: .75rem; justify-content: space-between; }
.meta { color: var(--admin-muted); font-size: 12px; line-height: 1.5; }
.error { color: var(--admin-danger-text); }
.actions { border-top: 1px solid var(--admin-border); justify-content: flex-start; padding-top: 1rem; }
.override [role='status'] { color: var(--admin-muted); font-size: 14px; line-height: 1.55; margin: 0; }
.override [role='alertdialog'] { background: var(--admin-warning-bg); border: 1px solid var(--admin-warning-border); border-radius: var(--admin-radius); color: var(--admin-warning-text); display: flex; flex-wrap: wrap; gap: .75rem; padding: 1rem; }
.override [role='alertdialog'] p { flex-basis: 100%; font-size: 14px; line-height: 1.55; margin: 0; }
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@
nav { border-bottom: 1px solid var(--admin-border); display: flex; flex-wrap: wrap; gap: 0.25rem; }
a { align-items: center; border: 1px solid transparent; border-bottom-width: 3px; border-radius: 7px 7px 0 0; color: var(--admin-muted); display: flex; flex: 0 0 auto; font-size: 0.875rem; font-weight: 650; gap: 0.45rem; margin-bottom: -1px; padding: 0.65rem 0.8rem; text-decoration: none; }
a:hover { background: var(--admin-sidebar); color: var(--admin-ink); }
a:focus-visible { outline: 3px solid rgb(47 111 235 / 24%); outline-offset: -3px; }
a.active { background: rgb(47 111 235 / 10%); border-color: rgb(47 111 235 / 20%); border-bottom-color: var(--admin-accent); color: var(--admin-ink); font-weight: 750; }
a:focus-visible { outline: 3px solid var(--admin-focus); outline-offset: -3px; }
a.active { background: var(--admin-accent-soft); border-color: var(--admin-accent-border); border-bottom-color: var(--admin-accent); color: var(--admin-ink); font-weight: 750; }
strong { background: var(--admin-sidebar); border: 1px solid var(--admin-border); border-radius: 999px; color: inherit; font-size: 0.7rem; min-width: 1.45rem; padding: 0.08rem 0.35rem; text-align: center; }
a.active strong { background: #fff; border-color: rgb(47 111 235 / 30%); color: var(--admin-accent); }
a.active strong { background: var(--admin-panel); border-color: var(--admin-accent-border); color: var(--admin-accent); }
.catalog { margin-top: 1rem; min-width: 0; }
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,12 @@
{/if}

<style>
.intro, .empty { color: #787774; margin: 1rem 0 0; }
.back { background: transparent; border: 0; color: #56554f; cursor: pointer; font: inherit; margin-top: 1rem; padding: 0; }
.intro, .empty { color: var(--admin-muted); margin: 1rem 0 0; }
.back { background: transparent; border: 0; color: var(--admin-ink); cursor: pointer; font: inherit; margin-top: 1rem; padding: 0; }
ul { display: grid; gap: 0.5rem; list-style: none; margin: 1rem 0 0; overflow: auto; padding: 0; }
li button { align-items: flex-start; background: #fbfbfa; border: 1px solid #e9e9e7; border-radius: 8px; cursor: pointer; display: flex; gap: 0.6rem; padding: 0.85rem; text-align: left; width: 100%; }
li button:hover { background: #f4f4f2; }
li button { align-items: flex-start; background: var(--admin-soft); border: 1px solid var(--admin-border); border-radius: 8px; cursor: pointer; display: flex; gap: 0.6rem; padding: 0.85rem; text-align: left; width: 100%; }
li button:hover { background: var(--admin-hover); }
.connector-copy strong { color: var(--admin-ink); }
.connector-copy { display: grid; gap: 0.3rem; }
li span { color: #787774; font-size: 13px; }
li span { color: var(--admin-muted); font-size: 13px; }
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@

<section class="configuration admin-panel" aria-labelledby="connector-configuration-title">
<div class="section-heading">
<p class="eyebrow">Connector action</p>
<h2 id="connector-configuration-title">Configure connector</h2>
<p class="eyebrow">Configuration</p>
<h2 id="connector-configuration-title">{connector.name}</h2>
</div>

<form
Expand Down Expand Up @@ -180,6 +180,8 @@
<style>
.configuration {
max-width: 64rem;
min-height: 0;
overflow-y: auto;
}

.section-heading {
Expand All @@ -188,18 +190,19 @@
}

h2 {
color: #37352f;
color: var(--admin-ink);
font-size: 18px;
font-weight: 650;
line-height: 1.3;
margin: 0;
}

.eyebrow {
color: #787774;
font-size: 12px;
font-weight: 650;
letter-spacing: 0.12em;
color: var(--admin-accent);
font-family: var(--admin-mono);
font-size: 11px;
font-weight: 500;
letter-spacing: 0.08em;
line-height: 1.4;
margin: 0;
text-transform: uppercase;
Expand All @@ -225,40 +228,42 @@
}

.feedback-message--success {
background: #edf7ed;
border: 1px solid #d3e8d3;
color: #2f6f3e;
background: var(--admin-success-bg);
border: 1px solid var(--admin-success-border);
color: var(--admin-success-text);
}

.feedback-message--warning {
background: #fbf3db;
border: 1px solid #ead9a9;
color: #7a5d16;
background: var(--admin-warning-bg);
border: 1px solid var(--admin-warning-border);
color: var(--admin-warning-text);
}

.feedback-message--error {
background: #fff6f6;
border: 1px solid #f0c7c7;
color: #8f3232;
background: var(--admin-danger-bg);
border: 1px solid var(--admin-danger-border);
color: var(--admin-danger-text);
}

.feedback-message--muted {
background: #f7f6f3;
border: 1px solid #e9e9e7;
color: #787774;
background: var(--admin-soft);
border: 1px solid var(--admin-border);
color: var(--admin-muted);
}

.form-actions {
align-items: center;
display: flex;
flex-wrap: wrap;
gap: 8px;
border-top: 1px solid var(--admin-border);
padding-top: 16px;
}

.unsupported {
background: #fbf3db;
border: 1px solid #ead9a9;
color: #7a5d16;
background: var(--admin-warning-bg);
border: 1px solid var(--admin-warning-border);
color: var(--admin-warning-text);
padding: 9px 10px;
}

Expand Down
Loading
Loading