Major rebranding, i18n, dashboard, shared folders, role capabilities

- Rebrand: new app/agent icons, SKALD.md, warm "paper" CSS palette
  (terracotta accent, --radius tokens, WCAG contrast, reduced-motion),
  updated favicon, tray icon, skaldkonur asset
- i18n: backend crate (i18n.rs, locale column, ui_locale config),
  frontend library (web/lib/i18n.js, I18nMixin, t(key)),
  translation files (web/i18n/), every component wired
- Dashboard: <dashboard-page> replaces old home-page content;
  <app-copilot> becomes the landing page (full/dock layout modes)
- Shared folders: API endpoints (shared_folders.rs), frontend page,
  can_write membership, container mount topology, user_fs routing
- Role capabilities: new db table & authorization seam (data not enums),
  roles.attrs JSON for ui_mode / interface select
- Setup: skald-setup prompts for language + password, sets ui_locale
- General: components migrated to CSS variables, Lit conventions cleanup,
  connectors/catalog/marketplace/approval refactoring
This commit is contained in:
2026-07-18 21:38:42 +01:00
parent 2b35312abd
commit 126886e309
109 changed files with 6228 additions and 1390 deletions
+7 -6
View File
@@ -1,10 +1,11 @@
import { html, nothing } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { renderMarkdown } from './base.js';
import { t } from './i18n.js';
/**
* InboxMixin — shared fetch, action, and render logic for the agent inbox.
* Used by AgentInboxPage (full page) and HomePage (embedded section).
* Used by AgentInboxPage (full page) and DashboardPage (embedded section).
*/
export const InboxMixin = (Base) => class extends Base {
@@ -68,7 +69,7 @@ export const InboxMixin = (Base) => class extends Base {
}
_rejectWithNote(requestId, toolCallId = null) {
const note = prompt('Rejection reason (optional):') ?? '';
const note = prompt(t('inbox.reject_prompt')) ?? '';
this._resolveApproval(requestId, 'reject', note, null, null, toolCallId);
}
@@ -208,11 +209,11 @@ export const InboxMixin = (Base) => class extends Base {
<div class="inbox-card-footer approval-footer">
<button class="btn btn-success"
@click=${() => this._resolveApproval(item.request_id, 'approve', '', null, null, item.tool_call_id)}>
<i class="bi bi-check-lg"></i> Approve
<i class="bi bi-check-lg"></i> ${t('approval.approve')}
</button>
<button class="btn btn-outline-danger"
@click=${() => this._rejectWithNote(item.request_id, item.tool_call_id)}>
<i class="bi bi-x-lg"></i> Reject
<i class="bi bi-x-lg"></i> ${t('approval.reject')}
</button>
${item.request_id ? html`
@@ -233,7 +234,7 @@ export const InboxMixin = (Base) => class extends Base {
<button class="btn btn-outline-secondary"
@click=${() => this._approveWithBypass(item, 0)}
title="Approve and don't ask again for this session">
title=${t('approval.bypass_all')}>
<i class="bi bi-shield-check"></i> Sessione
</button>
` : nothing}
@@ -360,7 +361,7 @@ export const InboxMixin = (Base) => class extends Base {
${total === 0 ? html`
<div class="inbox-empty">
<i class="bi bi-inbox"></i>
<p>No pending requests</p>
<p>${t('inbox.empty')}</p>
</div>
` : html`
<div class="inbox-grid">