portal-mojo 0.2.21 → 0.2.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/admin-assistant.md +75 -23
- package/docs/admin-dns-domains.md +96 -90
- package/docs/admin-dns-records.md +1 -1
- package/docs/admin-dns-registrar.md +29 -4
- package/docs/admin-dns.md +2 -3
- package/docs/admin-modals.md +21 -0
- package/docs/menus-and-access.md +24 -0
- package/package.json +1 -1
- package/src/admin/assistant/AssistantFeed.tsx +7 -4
- package/src/admin/assistant/AssistantSetupPage.tsx +141 -64
- package/src/admin/assistant/api.ts +9 -7
- package/src/admin/assistant/pages.tsx +125 -109
- package/src/admin/assistant/setup.ts +1 -1
- package/src/admin/assistant/skill-lifecycle.ts +5 -4
- package/src/admin/core/index.ts +10 -2
- package/src/admin/dns/DnsRecordsPanel.tsx +45 -9
- package/src/admin/dns/DomainAssignment.tsx +55 -0
- package/src/admin/dns/DomainDetail.tsx +55 -38
- package/src/admin/dns/DomainOnboardingContent.tsx +9 -4
- package/src/admin/dns/DomainPurchaseDetail.tsx +42 -7
- package/src/admin/dns/DomainPurchaseWizard.tsx +102 -72
- package/src/admin/dns/DomainPurchasesPage.tsx +1 -1
- package/src/admin/dns/DomainRegisterExisting.tsx +21 -6
- package/src/admin/dns/DomainRegistrarSettings.tsx +28 -48
- package/src/admin/dns/DomainRegistrationPanel.tsx +127 -0
- package/src/admin/dns/DomainsPage.tsx +25 -24
- package/src/admin/dns/ProviderCredentialsPage.tsx +32 -18
- package/src/admin/dns/RegistrantContactPage.tsx +73 -27
- package/src/admin/dns/api.ts +26 -25
- package/src/admin/dns/domain-authority.ts +11 -0
- package/src/admin/dns/index.ts +2 -2
- package/src/admin/dns/purchase-data.ts +5 -3
- package/src/admin/dns/registrar-api.ts +57 -11
- package/src/admin/domains/infrastructure.ts +8 -8
- package/src/admin/domains/operations.ts +6 -6
- package/src/admin/edge/VhostsPage.tsx +6 -1
- package/src/admin/jobs/JobDashboardPage.tsx +14 -4
- package/src/client/mock.ts +14 -8
- package/src/styles/components/admin-assistant.css +83 -20
- package/src/styles/components/admin-domains.css +67 -11
- package/src/styles/components/admin-edge.css +1 -0
- package/src/styles/components/admin-jobs.css +1 -0
- package/src/ui/SidebarNav.tsx +10 -1
- package/src/ui/menu-registry.ts +2 -0
package/src/client/mock.ts
CHANGED
|
@@ -6779,6 +6779,8 @@ let domainRegistrarSaveFault: 'warning' | 'ambiguous' | null = null;
|
|
|
6779
6779
|
let dnsWriteFault: 'reject' | 'ambiguous' | 'reconcile' | null = null;
|
|
6780
6780
|
let dnsFailNextRead = false;
|
|
6781
6781
|
const mockRegistrantScopeState = new Map<string, 'saved' | 'cleared'>();
|
|
6782
|
+
const mockRegistrantConfigured = (group: number) => mockRegistrantScopeState.get(group > 0 ? `group:${group}` : 'house') === 'saved'
|
|
6783
|
+
|| dnsRegistrarMode !== 'contact-missing' && (group === 0 || group % 2 === 1);
|
|
6782
6784
|
|
|
6783
6785
|
export type MockUploadMode = 'raw-put' | 'raw-put-unprefixed' | 'config-put' | 'config-put-bearer' | 'config-post';
|
|
6784
6786
|
export type MockUploadFault = 'initiate-ambiguous' | 'transfer-ambiguous' | 'transfer-failed' | 'transfer-expired' | 'complete-reject' | 'complete-ambiguous' | 'complete-body-malformed';
|
|
@@ -6903,7 +6905,7 @@ export function setMockDnsAcmeMode(value: 'production' | 'staging' | 'unconfigur
|
|
|
6903
6905
|
export function setMockDnsRegistrarMode(value: 'ready' | 'disabled' | 'contact-missing'): void { dnsRegistrarMode = value; }
|
|
6904
6906
|
|
|
6905
6907
|
/** Verifier-only one-shot result after a confirmation token is consumed. */
|
|
6906
|
-
export function armMockRegistrarPurchaseFault(value: 'failed' | 'ambiguous'): void { dnsRegistrarFault = value; }
|
|
6908
|
+
export function armMockRegistrarPurchaseFault(value: 'failed' | 'ambiguous' | null): void { dnsRegistrarFault = value; }
|
|
6907
6909
|
export function armMockDomainRegistrarSaveFault(value: 'warning' | 'ambiguous'): void { domainRegistrarSaveFault = value; }
|
|
6908
6910
|
|
|
6909
6911
|
/** Verifier-only one-shot DNS transport outcomes; all state still lives in db.dnsRecords. */
|
|
@@ -9767,7 +9769,7 @@ async function mockFetchInner(path: string, opts: MockFetchOpts): Promise<unknow
|
|
|
9767
9769
|
const caller = userFromBearer(opts.headers);
|
|
9768
9770
|
if (!caller) return permissionDenied(401);
|
|
9769
9771
|
const assistantGate = hasGlobalPermission(caller, ['view_admin', 'assistant']);
|
|
9770
|
-
const canInspect = (row: MockAssistantConversation) => row.user === caller.id ||
|
|
9772
|
+
const canInspect = (row: MockAssistantConversation) => row.user === caller.id || hasGlobalPermission(caller, ['view_admin']);
|
|
9771
9773
|
const canDelete = (row: MockAssistantConversation) => row.user === caller.id || hasGlobalPermission(caller, ['view_admin']);
|
|
9772
9774
|
const assistantUser = (userId: number) => { const row = db.users.find((user) => user.id === userId); return row ? { id: row.id, display_name: row.display_name } : { id: userId, display_name: `User #${userId}` }; };
|
|
9773
9775
|
const conversationWire = (row: MockAssistantConversation, detail = false) => ({ id: row.id, title: row.title, metadata: { ...row.metadata }, created: row.created, modified: row.modified, user: assistantUser(row.user), ...(detail ? { messages: row.messages.map((message) => ({ ...message, tool_calls: [...message.tool_calls], blocks: message.blocks == null ? null : structuredClone(message.blocks), usage: { ...message.usage } })) } : {}) });
|
|
@@ -9826,14 +9828,17 @@ async function mockFetchInner(path: string, opts: MockFetchOpts): Promise<unknow
|
|
|
9826
9828
|
const id = conversationMatch[1] ? Number(conversationMatch[1]) : null;
|
|
9827
9829
|
if (id != null) { const row = db.assistantConversations.find((candidate) => candidate.id === id); if (!row || !canInspect(row)) return { status: false, error: 'Conversation not found', error_code: 404 }; if (method === 'DELETE') { if (!canDelete(row)) return permissionDenied(); db.assistantConversations = db.assistantConversations.filter((candidate) => candidate.id !== id); return { status: 'deleted' }; } if (method !== 'GET') return { status: false, error: 'Method not allowed', error_code: 405 }; return { status: true, data: conversationWire(row, opts.params?.graph === 'detail'), graph: String(opts.params?.graph ?? 'default') }; }
|
|
9828
9830
|
if (method !== 'GET') return { status: false, error: 'Method not allowed', error_code: 405 };
|
|
9829
|
-
const
|
|
9831
|
+
const params = { ...opts.params, start: Math.max(0, Number(opts.params?.start ?? 0)), size: Math.min(100, Math.max(1, Number(opts.params?.size ?? 100))) };
|
|
9832
|
+
return { ...listRows(db.assistantConversations.filter(canInspect).map(row => conversationWire(row)), params, row => row.title, '-modified'), graph: 'default' };
|
|
9830
9833
|
}
|
|
9831
9834
|
const skillMatch = path.match(/^\/api\/assistant\/skill(?:\/(\d+))?$/);
|
|
9832
9835
|
if (skillMatch) {
|
|
9833
9836
|
if (!assistantGate) return permissionDenied(); const id = skillMatch[1] ? Number(skillMatch[1]) : null;
|
|
9834
9837
|
const skillWire = (row: MockAssistantSkill, detail = false) => ({ id: row.id, tier: row.tier, name: row.name, description: row.description, auto_execute: row.auto_execute, is_active: row.is_active, created: row.created, modified: row.modified, user: row.user == null ? null : assistantUser(row.user), ...(detail ? { triggers: [...row.triggers], steps: structuredClone(row.steps), metadata: { ...row.metadata } } : {}) });
|
|
9835
9838
|
if (id != null) { const row = db.assistantSkills.find((candidate) => candidate.id === id); if (!row) return { status: false, error: 'Skill not found', error_code: 404 }; if (method === 'DELETE') { db.assistantSkills = db.assistantSkills.filter((candidate) => candidate.id !== id); return { status: 'deleted' }; } if (method === 'POST') { if (!hasGlobalPermission(caller, ['view_admin'])) return permissionDenied(); if (typeof opts.body?.is_active !== 'boolean') return { status: false, error: 'is_active is required', error_code: 400 }; row.is_active = opts.body.is_active; row.modified = Math.floor(Date.now() / 1000); } else if (method !== 'GET') return { status: false, error: 'Method not allowed', error_code: 405 }; return { status: true, data: skillWire(row, opts.params?.graph === 'detail'), graph: String(opts.params?.graph ?? 'default') }; }
|
|
9836
|
-
if (method !== 'GET') return { status: false, error: 'Method not allowed', error_code: 405 };
|
|
9839
|
+
if (method !== 'GET') return { status: false, error: 'Method not allowed', error_code: 405 };
|
|
9840
|
+
const params = { ...opts.params, start: Math.max(0, Number(opts.params?.start ?? 0)), size: Math.min(100, Math.max(1, Number(opts.params?.size ?? 100))) };
|
|
9841
|
+
return { ...listRows(db.assistantSkills.map(row => skillWire(row)), params, row => row.name, 'name'), graph: 'default' };
|
|
9837
9842
|
}
|
|
9838
9843
|
const memoryMatch = path.match(/^\/api\/assistant\/memory\/(global|user|group)(?:\/([^/]+))?$/);
|
|
9839
9844
|
if (memoryMatch) {
|
|
@@ -9846,7 +9851,7 @@ async function mockFetchInner(path: string, opts: MockFetchOpts): Promise<unknow
|
|
|
9846
9851
|
}
|
|
9847
9852
|
const store: Record<string, unknown> = tier === 'global' ? db.assistantMemory.global : tier === 'user' ? (db.assistantMemory.users.get(caller.id) ?? {}) : (db.assistantMemory.groups.get(groupId!) ?? {});
|
|
9848
9853
|
if (method === 'GET' && entryKey == null) return { status: true, data: { ...store } };
|
|
9849
|
-
if (method === 'POST' && entryKey == null) { const key = typeof opts.body?.key === 'string' ? opts.body.key : ''; const value = typeof opts.body?.value === 'string' ? opts.body.value : ''; if (!/^[a-z0-9:_-]{
|
|
9854
|
+
if (method === 'POST' && entryKey == null) { const key = typeof opts.body?.key === 'string' ? opts.body.key : ''; const value = typeof opts.body?.value === 'string' ? opts.body.value : ''; if (!/^[a-z0-9][a-z0-9:_-]{0,63}$/.test(key) || value.length < 1 || value.length > 500) return { status: false, error: 'Invalid memory key or value', error_code: 400 }; store[key] = value; if (tier === 'user') db.assistantMemory.users.set(caller.id, store); if (tier === 'group') db.assistantMemory.groups.set(groupId!, store); return { status: true, data: { key, value } }; }
|
|
9850
9855
|
if (method === 'DELETE' && entryKey != null) { delete store[entryKey]; return { status: true, data: { deleted: entryKey } }; }
|
|
9851
9856
|
return { status: false, error: 'Method not allowed', error_code: 405 };
|
|
9852
9857
|
}
|
|
@@ -10339,7 +10344,7 @@ async function mockFetchInner(path: string, opts: MockFetchOpts): Promise<unknow
|
|
|
10339
10344
|
status: true,
|
|
10340
10345
|
data: {
|
|
10341
10346
|
purchase_enabled: dnsRegistrarMode !== 'disabled',
|
|
10342
|
-
registrant_contact_configured:
|
|
10347
|
+
registrant_contact_configured: mockRegistrantConfigured(groupId),
|
|
10343
10348
|
max_domain_price: '100.00', currency: 'USD', quote_ttl_minutes: 15,
|
|
10344
10349
|
allowed_record_types: ['A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'SRV', 'TXT'],
|
|
10345
10350
|
search_batch_limit: 20, suggestions_enabled: true,
|
|
@@ -10740,7 +10745,7 @@ async function mockFetchInner(path: string, opts: MockFetchOpts): Promise<unknow
|
|
|
10740
10745
|
const group = db.groups.find((candidate) => candidate.id === groupId && candidate.is_active);
|
|
10741
10746
|
if (!group || !body.domain) return missingParams('group', 'domain');
|
|
10742
10747
|
if (dnsRegistrarMode === 'disabled') return { status: false, error: 'Domain purchasing is disabled on this deployment (DNSMAN_PURCHASE_ENABLED is off).', error_code: 400 };
|
|
10743
|
-
if (
|
|
10748
|
+
if (!mockRegistrantConfigured(groupId)) return { status: false, error: 'No registrant contact is configured', error_code: 400 };
|
|
10744
10749
|
const answer = resultRow(String(body.domain));
|
|
10745
10750
|
if (answer.available !== true || !answer.tld_supported) return { status: false, error: 'The domain cannot be quoted', error_code: 400 };
|
|
10746
10751
|
const years = Number(body.years ?? 1);
|
|
@@ -10841,7 +10846,7 @@ async function mockFetchInner(path: string, opts: MockFetchOpts): Promise<unknow
|
|
|
10841
10846
|
const direct = state === 'saved' || (state == null && groupId === 1);
|
|
10842
10847
|
const settingsFile = state == null && groupId === 0;
|
|
10843
10848
|
const contact = direct || settingsFile ? canonical : null;
|
|
10844
|
-
return { status: true, data: { scope: groupId > 0 ? 'group' : 'global', group: groupId || null, contact, source: direct ? 'database' : settingsFile ? 'settings_file' : 'none', inherited: groupId > 0 && !contact && groupId % 2 === 0, effective_configured:
|
|
10849
|
+
return { status: true, data: { scope: groupId > 0 ? 'group' : 'global', group: groupId || null, contact, source: direct ? 'database' : settingsFile ? 'settings_file' : 'none', inherited: groupId > 0 && !contact && groupId % 2 === 0, effective_configured: mockRegistrantConfigured(groupId), problems: [] } };
|
|
10845
10850
|
}
|
|
10846
10851
|
|
|
10847
10852
|
if (path === '/api/dnsman/whois' || path === '/api/dnsman/whois/privacy') {
|
|
@@ -10852,6 +10857,7 @@ async function mockFetchInner(path: string, opts: MockFetchOpts): Promise<unknow
|
|
|
10852
10857
|
if (!domain) return { status: false, error: 'Domain not found', error_code: 404 };
|
|
10853
10858
|
if (domain.provider !== 'route53') return { status: false, error: 'Registrar operations are not available for this provider', error_code: 400 };
|
|
10854
10859
|
if (domain.group == null ? !caller.is_superuser : !dnsMemberCan(caller, domain.group, true)) return permissionDenied();
|
|
10860
|
+
if (domain.registered_on == null) return { status: false, error: 'This hosted zone has no registration in the Route 53 Domains account. Check its registrar.', error_code: 400 };
|
|
10855
10861
|
if (path.endsWith('/privacy')) {
|
|
10856
10862
|
domain.privacy = Boolean(input.enabled); domain.modified = Math.floor(Date.now() / 1000);
|
|
10857
10863
|
return { status: true, data: { name: domain.name, privacy: domain.privacy, operation_id: `op-privacy-${domain.id}` } };
|
|
@@ -26,25 +26,88 @@
|
|
|
26
26
|
.assistant-inspect > header, .assistant-skill-detail > header { display: flex; justify-content: space-between; gap: 12px; }.assistant-inspect > .record-feed { flex: 1; min-height: 0; }
|
|
27
27
|
.assistant-memory-controls { display: grid; gap: 12px; margin-bottom: 12px; }.assistant-memory-form { display: grid; grid-template-columns: minmax(180px, .6fr) minmax(260px, 1fr) auto; gap: 10px; align-items: end; }
|
|
28
28
|
@media (max-width: 760px) { .assistant-memory-form { grid-template-columns: 1fr; } }
|
|
29
|
-
.assistant-setup { display: grid;
|
|
30
|
-
.assistant-setup h1
|
|
31
|
-
.assistant-setup
|
|
32
|
-
.assistant-setup-
|
|
33
|
-
.assistant-setup-switch { padding-top: 20px; margin-top: 18px; border-top: 1px solid var(--line); }
|
|
34
|
-
.assistant-setup-switch > span { flex: 1; min-width: 200px; }
|
|
35
|
-
.assistant-setup-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 14px; margin: 18px 0; }
|
|
29
|
+
.assistant-setup { display: grid; min-width: 0; margin: 0 auto; width: 100%; }
|
|
30
|
+
.assistant-setup h1 { font-size: clamp(24px, 3vw, 32px); font-weight: 700; line-height: 1.2; margin: 0; }
|
|
31
|
+
.assistant-setup-header { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 14px; }
|
|
32
|
+
.assistant-setup-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px; margin: 18px 0; }
|
|
36
33
|
.assistant-setup-facts dt { color: var(--mute); font-size: 12px; }.assistant-setup-facts dd { margin: 5px 0 0; overflow-wrap: anywhere; }
|
|
37
|
-
.assistant-setup-field { display:
|
|
38
|
-
.assistant-setup-
|
|
39
|
-
|
|
40
|
-
.assistant-setup
|
|
41
|
-
.assistant-setup
|
|
42
|
-
|
|
43
|
-
.assistant-setup
|
|
44
|
-
.assistant-setup
|
|
34
|
+
.assistant-setup-field > span { display: flex; align-items: center; gap: 6px; }
|
|
35
|
+
.assistant-setup-field { display: grid; gap: 8px; margin: 20px 0; }.assistant-setup-field input, .assistant-setup-field select { width: 100%; min-width: 0; }
|
|
36
|
+
/* Setup is a compact overview. Task dialogs own deeper configuration. */
|
|
37
|
+
.assistant-setup { max-width: 960px; gap: 20px; }
|
|
38
|
+
.assistant-setup-header p { margin: 7px 0 0; }
|
|
39
|
+
.assistant-setup .assistant-setup-overview { padding: 24px; }
|
|
40
|
+
.assistant-setup-status, .assistant-setup-status > div { display: flex; align-items: center; gap: 10px; }
|
|
41
|
+
.assistant-setup-status { justify-content: space-between; flex-wrap: wrap; gap: 16px; padding-bottom: 22px; }
|
|
42
|
+
.assistant-setup-status h2 { font-weight: 600; margin: 0; font-size: 20px; }
|
|
43
|
+
.assistant-setup-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--mute); flex: 0 0 auto; }
|
|
44
|
+
.assistant-setup-dot.is-ready { background: var(--ok); }.assistant-setup-dot.is-stopped { background: var(--warn); }
|
|
45
|
+
.assistant-setup-row { display: flex; align-items: center; gap: 18px; padding: 20px 0; border-top: 1px solid var(--line); min-width: 0; }
|
|
46
|
+
.assistant-setup-row > div { flex: 1; min-width: 0; }.assistant-setup-row > .btn { flex-shrink: 0; }
|
|
47
|
+
.assistant-setup-row h3 { font-weight: 600; font-size: 15px; margin: 0; display: flex; align-items: center; gap: 6px; }
|
|
48
|
+
.assistant-setup-row p { color: var(--mute); margin: 5px 0; font-size: 14px; }
|
|
49
|
+
.assistant-setup-row code { font-size: 12px; overflow-wrap: anywhere; }
|
|
50
|
+
.assistant-setup-tools { padding: 0 24px; }.assistant-setup-tools > .assistant-setup-row:first-child { border-top: 0; }
|
|
51
|
+
.assistant-setup-row > i { color: var(--accent); font-size: 19px; flex-shrink: 0; }
|
|
52
|
+
.assistant-setup-help { display: inline-flex; vertical-align: middle; justify-content: center; align-items: center; border: 0; border-radius: 50%; width: 28px; height: 28px; padding: 0; color: var(--mute); background: transparent; cursor: pointer; flex: 0 0 auto; }
|
|
53
|
+
.assistant-setup-help:hover { color: var(--accent); background: var(--surface2); }.assistant-setup-help:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
54
|
+
.assistant-setup-help-content { width: min(340px, calc(100vw - 32px)); max-height: min(500px, 75dvh); overflow: auto; padding: 16px; font-size: 14px; line-height: 1.5; overflow-wrap: anywhere; }
|
|
55
|
+
.assistant-setup-help-content p { margin: 0 0 10px; }.assistant-setup-help-content p:last-child { margin-bottom: 0; }
|
|
56
|
+
.assistant-setup-help-content .assistant-setup-facts { grid-template-columns: 1fr; margin: 0; }
|
|
57
|
+
.assistant-setup-dialog { margin: auto; width: min(680px, calc(100vw - 32px)); max-width: none; max-height: calc(100dvh - 32px); padding: 0; border: 1px solid var(--line); border-radius: 16px; background: var(--surface); color: var(--ink); box-shadow: 0 24px 80px #0004; overflow: hidden; }
|
|
58
|
+
.assistant-setup-dialog[open] { display: flex; flex-direction: column; }.assistant-setup-dialog::backdrop { background: #0007; }
|
|
59
|
+
.assistant-setup-dialog > header { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 22px 24px 18px; border-bottom: 1px solid var(--line); flex-shrink: 0; }
|
|
60
|
+
.assistant-setup-dialog h2 { margin: 0; font-size: 22px; line-height: 1.3; font-weight: 650; }.assistant-setup-dialog h3 { margin: 24px 0 10px; font-size: 16px; font-weight: 650; }.assistant-setup-dialog-content > h3:first-child { margin-top: 0; }
|
|
61
|
+
.assistant-setup-dialog-content { overflow: auto; padding: 24px; min-height: 0; overscroll-behavior: contain; }
|
|
62
|
+
.assistant-setup-dialog-content > :first-child { margin-top: 0; }.assistant-setup-dialog-content > :last-child { margin-bottom: 0; }
|
|
63
|
+
.assistant-setup-dialog p { margin: 0 0 12px; line-height: 1.5; overflow-wrap: anywhere; }.assistant-setup-dialog code { overflow-wrap: anywhere; }
|
|
64
|
+
.assistant-setup-dialog > footer { display: flex; justify-content: flex-end; padding: 16px 24px; border-top: 1px solid var(--line); flex-shrink: 0; }
|
|
65
|
+
.assistant-setup-dialog .assistant-action-row { flex-wrap: wrap; align-items: center; gap: 10px; }
|
|
66
|
+
.assistant-setup-dialog .assistant-setup-row:first-child { border-top: 0; padding-top: 0; }
|
|
67
|
+
.assistant-setup-dialog .assistant-setup-row h3 { margin: 0; }.assistant-setup-dialog .assistant-setup-field { max-width: none; }
|
|
68
|
+
.assistant-setup-dialog .assistant-setup-key-status { padding-top: 6px; }.assistant-setup-key-status p { margin: 6px 0; }
|
|
69
|
+
.assistant-setup-current { display: grid; gap: 8px; background: var(--surface2); padding: 14px; border-radius: 8px; }.assistant-setup-current > span { font-size: 12px; color: var(--mute); }
|
|
70
|
+
.assistant-setup-catalogue { margin-top: 18px; display: flex; gap: 6px; align-items: center; font-size: 13px; }
|
|
71
|
+
.assistant-setup-danger-row { border-top: 1px solid var(--line); margin-top: 24px; padding-top: 18px; }
|
|
72
|
+
.assistant-setup-warning { border: 1px solid var(--warn); border-radius: 8px; padding: 12px; font-size: 14px; line-height: 1.5; }
|
|
73
|
+
.assistant-setup-notice { padding: 12px; background: var(--surface2); border-radius: 8px; font-size: 14px; }
|
|
74
|
+
.assistant-setup-text-button { background: none; color: var(--accent); border: 0; padding: 0; cursor: pointer; font: inherit; text-decoration: underline; }
|
|
75
|
+
.assistant-setup-dialog .assistant-table-scroll { overflow-x: auto; max-width: 100%; margin: 14px 0; }.assistant-setup-dialog table { border-collapse: collapse; width: 100%; font-size: 13px; }.assistant-setup-dialog th, .assistant-setup-dialog td { padding: 10px; text-align: left; border-bottom: 1px solid var(--line); }.assistant-setup-dialog caption { text-align: left; color: var(--mute); margin-bottom: 8px; }
|
|
76
|
+
.assistant-setup-dialog-confirm { width: min(460px, calc(100vw - 32px)); }
|
|
77
|
+
@media (max-width: 600px) {
|
|
78
|
+
.assistant-setup { gap: 16px; }.assistant-setup .assistant-setup-overview { padding: 18px; }.assistant-setup-tools { padding: 0 18px; }
|
|
79
|
+
.assistant-setup-row { flex-wrap: wrap; gap: 12px; }.assistant-setup-row > div { flex-basis: calc(100% - 40px); }.assistant-setup-row > .btn { margin-left: auto; }
|
|
80
|
+
.assistant-setup-status > .btn { width: 100%; }.assistant-setup-dialog > header { padding: 18px; }.assistant-setup-dialog-content { padding: 18px; }.assistant-setup-dialog > footer { padding: 14px 18px; }
|
|
81
|
+
.assistant-setup-dialog .assistant-action-row { flex-direction: column; align-items: stretch; }.assistant-setup-dialog .assistant-setup-facts { grid-template-columns: 1fr; }.assistant-setup-dialog button { white-space: normal; }
|
|
82
|
+
}
|
|
83
|
+
.assistant-setup-area { display: grid; gap: 20px; }
|
|
84
|
+
.assistant-setup-mcp h2 { margin: 0 0 8px; font-size: 20px; font-weight: 650; }.assistant-setup-mcp > h3 { margin: 24px 0 12px; font-size: 16px; font-weight: 600; }
|
|
85
|
+
.assistant-setup-mcp > p { margin: 0 0 16px; }.assistant-setup-mcp .assistant-setup-field { max-width: 680px; }
|
|
86
|
+
.assistant-setup-mcp .assistant-action-row { gap: 12px; }.assistant-setup-mcp .assistant-setup-row:first-of-type { margin-top: 22px; }
|
|
87
|
+
@media (max-width: 600px) { .assistant-setup-mcp.panel-pad { padding: 18px; }.assistant-setup-mcp .assistant-action-row { flex-direction: column; align-items: stretch; } }
|
|
45
88
|
|
|
46
|
-
.assistant-setup-
|
|
47
|
-
.assistant-setup-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
89
|
+
.assistant-setup-address { display: flex; align-items: center; gap: 10px; }.assistant-setup-address input { flex: 1; }.assistant-setup-address button { flex-shrink: 0; }
|
|
90
|
+
@media (max-width: 600px) { .assistant-setup-address { flex-direction: column; align-items: stretch; } }
|
|
91
|
+
|
|
92
|
+
/* Assistant management pages: one task per page, focused record dialogs. */
|
|
93
|
+
.assistant-admin-page { width: 100%; min-width: 0; display: grid; gap: 20px; max-width: 1120px; margin: 0 auto; }
|
|
94
|
+
.assistant-page-heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
|
|
95
|
+
.assistant-page-heading .eyebrow { display: block; margin-bottom: 8px; color: var(--mute); font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; }
|
|
96
|
+
.assistant-page-heading h1 { margin: 0; font-size: 28px; line-height: 1.25; font-weight: 700; display: flex; align-items: center; gap: 10px; }
|
|
97
|
+
.assistant-page-actions { display: flex; gap: 8px; align-items: center; flex-shrink: 0; }
|
|
98
|
+
.assistant-page-help { display: inline-flex; align-items: center; justify-content: center; vertical-align: middle; width: 28px; height: 28px; padding: 0; border: 0; background: transparent; color: var(--mute); cursor: pointer; border-radius: 6px; font-size: 15px; flex-shrink: 0; }
|
|
99
|
+
.assistant-page-help:hover { color: var(--ink); background: var(--surface2); }.assistant-page-help:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
100
|
+
.assistant-page-help-content { width: min(340px, calc(100vw - 24px)); padding: 16px; font-size: 13px; line-height: 1.6; overflow-wrap: anywhere; }.assistant-page-help-content p { margin: 0 0 10px; }.assistant-page-help-content p:last-child { margin: 0; }
|
|
101
|
+
.assistant-page-search { position: relative; max-width: 380px; }.assistant-page-search > i { position: absolute; left: 12px; top: 12px; color: var(--mute); }.assistant-page-search .input { padding-left: 36px; width: 100%; }
|
|
102
|
+
.assistant-page-list { overflow: hidden; min-width: 0; }.assistant-page-list table { width: 100%; border-collapse: collapse; table-layout: fixed; }.assistant-page-list th, .assistant-page-list td { padding: 16px 20px; border-bottom: 1px solid var(--line); text-align: left; vertical-align: middle; overflow-wrap: anywhere; }.assistant-page-list th { background: var(--surface2); color: var(--mute); font-size: 11px; text-transform: uppercase; letter-spacing: .07em; font-weight: 700; }.assistant-page-list th:first-child { width: 55%; }.assistant-page-list tr:last-child td { border-bottom: 0; }.assistant-page-list td small { display: block; color: var(--mute); font-size: 12px; margin-top: 5px; line-height: 1.5; }.assistant-page-list .link-button { text-align: left; font-weight: 600; overflow-wrap: anywhere; }.assistant-page-list td .badge { margin-left: 6px; }
|
|
103
|
+
.assistant-page-empty { padding: 32px 24px; margin: 0; text-align: center; color: var(--mute); }.assistant-page-empty p { margin: 0 0 8px; }.assistant-page-empty .btn { margin-top: 12px; }
|
|
104
|
+
.assistant-page-pager { display: flex; justify-content: flex-end; align-items: center; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--line); }.assistant-page-pager span { margin-right: auto; color: var(--mute); font-size: 12px; }
|
|
105
|
+
.assistant-page-dialog { width: min(640px, calc(100vw - 32px)); max-width: none; max-height: calc(100dvh - 40px); padding: 0; margin: auto; border: 1px solid var(--line); border-radius: 14px; background: var(--surface); color: var(--ink); box-shadow: var(--shadow-pop); overflow: hidden; }
|
|
106
|
+
.assistant-page-dialog[open] { display: flex; flex-direction: column; }.assistant-page-dialog::backdrop { background: rgb(0 0 0 / .55); backdrop-filter: blur(3px); }.assistant-page-dialog > header { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 20px 24px; border-bottom: 1px solid var(--line); flex: none; }.assistant-page-dialog h2 { margin: 0; font-size: 20px; font-weight: 700; line-height: 1.4; overflow-wrap: anywhere; }.assistant-page-dialog-body { min-height: 0; overflow-y: auto; padding: 24px; }.assistant-page-dialog-body > .field { margin-top: 20px; }.assistant-page-dialog-body > p:first-child { margin-top: 0; }.assistant-page-dialog > footer { display: flex; justify-content: flex-end; gap: 10px; flex-wrap: wrap; border-top: 1px solid var(--line); padding: 16px 24px; flex: none; }.assistant-page-meta { color: var(--mute); margin: 0 0 18px; font-size: 12px; line-height: 1.6; }.assistant-page-readonly { padding: 10px 12px; background: var(--surface2); color: var(--mute); border-radius: 8px; font-size: 12px; line-height: 1.6; margin: 0 0 16px; }
|
|
107
|
+
.assistant-page-dialog-conversation { width: min(840px, calc(100vw - 32px)); height: min(760px, calc(100dvh - 40px)); }.assistant-page-dialog-conversation .assistant-page-dialog-body { display: flex; flex-direction: column; overflow: hidden; flex: 1; padding: 16px 20px; }.assistant-page-dialog-conversation .record-feed { flex: 1; min-height: 0; }.assistant-page-dialog-conversation .record-feed-scroll { min-height: 0; }
|
|
108
|
+
.assistant-page-chips { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }.assistant-skill-content { display: grid; gap: 22px; }.assistant-skill-description { line-height: 1.6; margin: 0; color: var(--mute); }.assistant-skill-content h3 { margin: 0 0 12px; font-size: 14px; font-weight: 700; }.assistant-skill-triggers { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; padding: 0; margin: 0; }.assistant-skill-triggers li { padding: 5px 10px; border: 1px solid var(--line); border-radius: 6px; font-size: 12px; }.assistant-skill-steps { margin: 0; padding-left: 22px; list-style: decimal; }.assistant-skill-steps li { padding: 10px 0 10px 6px; border-bottom: 1px solid var(--line); line-height: 1.6; }.assistant-skill-steps li:last-child { border: 0; }.assistant-skill-steps small { display: block; color: var(--mute); }.assistant-page-warning { color: var(--warn); font-size: 13px; line-height: 1.6; margin: 0; }.assistant-page-notice { color: var(--ok); font-size: 13px; margin: 0 0 12px; }
|
|
109
|
+
.assistant-memory-area { padding-top: 20px; }.assistant-memory-group { max-width: 380px; margin-bottom: 20px; }.assistant-memory-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 16px; }.assistant-memory-toolbar > span { color: var(--mute); font-size: 13px; }.assistant-memory-list { list-style: none; padding: 0; margin: 0; }.assistant-memory-list > li { display: flex; justify-content: space-between; gap: 20px; padding: 20px; border-bottom: 1px solid var(--line); }.assistant-memory-list > li:last-child { border: 0; }.assistant-memory-list > li > div:first-child { min-width: 0; }.assistant-memory-list strong { font-size: 13px; overflow-wrap: anywhere; }.assistant-memory-list p { color: var(--mute); margin: 8px 0 0; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
110
|
+
@media (max-width:600px) { .assistant-admin-page { gap: 16px; }.assistant-page-heading h1 { font-size: 24px; }.assistant-page-refresh { min-width: 38px; justify-content: center; }.assistant-page-refresh > span { display: none; }.assistant-page-list th, .assistant-page-list td { padding: 12px; font-size: 12px; }.assistant-page-list th:first-child { width: 44%; }.assistant-page-list th { font-size: 10px; letter-spacing: .04em; padding: 12px 10px; white-space: nowrap; }.assistant-page-list td { padding: 12px 10px; }.assistant-page-list td small { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }.assistant-page-dialog { width: calc(100vw - 20px); max-height: calc(100dvh - 24px); }.assistant-page-dialog > header { padding: 16px; }.assistant-page-dialog-body { padding: 18px 16px; }.assistant-page-dialog > footer { padding: 14px 16px; }.assistant-page-dialog-conversation { height: calc(100dvh - 24px); }.assistant-page-dialog-conversation .assistant-page-dialog-body { padding: 12px; }.assistant-memory-toolbar { align-items: flex-start; flex-direction: column; gap: 10px; }.assistant-memory-toolbar > .assistant-page-actions { width: 100%; justify-content: flex-end; }.assistant-memory-list > li { padding: 16px; gap: 8px; } }
|
|
111
|
+
|
|
112
|
+
.assistant-page-dialog-history { height: fit-content; }.assistant-page-dialog-history .record-feed { flex: initial; height: auto; min-height: 0; max-height: calc(100dvh - 280px); }
|
|
113
|
+
.assistant-page-dialog-conversation .attachment-drop { min-block-size: 36px; inline-size: auto; padding: 7px 10px; border-style: solid; }
|
|
@@ -1,12 +1,68 @@
|
|
|
1
|
-
.domains-
|
|
1
|
+
.domains-page,.domains-page>* { min-width:0; }
|
|
2
|
+
.domains-header,.domains-actions,.domains-overview-actions,.domains-detail-header,.domains-detail-subtitle,.domains-task-help { display:flex;align-items:center;gap:10px; }
|
|
3
|
+
.domains-header { justify-content:space-between;flex-wrap:wrap;margin-bottom:24px;gap:16px; }
|
|
4
|
+
.domains-header h1 { margin:4px 0 8px;font-size:26px;line-height:1.2;font-weight:750;letter-spacing:-.025em;color:var(--ink-strong); }
|
|
5
|
+
.domains-header p { margin:0; }
|
|
6
|
+
.domains-actions,.domains-overview-actions { flex-wrap:wrap; }
|
|
7
|
+
.domains-action-menu { display:grid;gap:4px;padding:6px; }
|
|
8
|
+
.domains-action-menu .btn { justify-content:flex-start; }
|
|
9
|
+
.domains-page .toolbar-heading .panel-title { font-size:19px; }
|
|
10
|
+
.domains-scope,.domains-detail code { overflow-wrap:anywhere; }
|
|
11
|
+
.domains-narrow-scope,.domains-narrow-status { display:none; }
|
|
12
|
+
.domains-detail-header { justify-content:space-between;align-items:flex-start;padding:22px 24px 18px; }
|
|
13
|
+
.domains-detail-header>div { min-width:0; }
|
|
14
|
+
.domains-detail-header h2 { font-size:21px;overflow-wrap:anywhere;margin:0 0 8px; }
|
|
15
|
+
.domains-detail-subtitle { color:var(--mute);flex-wrap:wrap; }
|
|
16
|
+
.domains-detail>.mojo-tabs>.mojo-tab-list { padding:0 24px; }
|
|
17
|
+
.domains-detail-panel { padding:20px 24px;min-width:0; }
|
|
18
|
+
.domains-detail .flat-row { gap:12px; }
|
|
19
|
+
.domains-detail .flat-value { overflow-wrap:anywhere; }
|
|
20
|
+
.domains-overview-actions { margin-top:22px; }
|
|
21
|
+
.domains-assignment,.domains-registrar-settings { display:grid;gap:16px; }
|
|
22
|
+
.domains-assignment>.dim { margin:0; }
|
|
23
|
+
.domains-inline-action { justify-self:start; }
|
|
24
|
+
.domains-demo { display:grid;gap:16px;min-width:0;grid-template-columns:minmax(0,1fr); }
|
|
25
|
+
.domains-demo>* { min-width:0; }
|
|
26
|
+
.domains-demo .seg { flex-wrap:wrap; }
|
|
27
|
+
.domain-onboarding>.dim { margin:4px 0 20px; }
|
|
28
|
+
.domain-onboarding .dns-registrar-grid { gap:18px; }
|
|
29
|
+
.domain-onboarding .dns-onboarding-options { grid-template-columns:1fr;gap:10px;margin-top:20px; }
|
|
30
|
+
.domain-onboarding .dns-onboarding-options>button { grid-template-columns:26px 1fr;column-gap:14px;row-gap:3px;padding:16px; }
|
|
31
|
+
.domain-onboarding .dns-onboarding-options>button>i { grid-row:1/3;font-size:22px;align-self:center; }
|
|
32
|
+
.domain-onboarding .dns-onboarding-options>button>span { grid-column:2; }
|
|
33
|
+
@media(max-width:560px) {
|
|
34
|
+
.domains-header { align-items:flex-start; }
|
|
35
|
+
.domains-actions { width:100%; }
|
|
36
|
+
.domains-actions .btn-primary { margin-left:auto; }
|
|
37
|
+
.domains-detail-header { padding:18px 16px; }
|
|
38
|
+
.domains-detail-header h2 { font-size:19px; }
|
|
39
|
+
.domains-detail>.mojo-tabs>.mojo-tab-list { padding:0 12px; }
|
|
40
|
+
.domains-detail .mojo-tab { padding-left:10px;padding-right:10px; }
|
|
41
|
+
.domains-detail-panel { padding:18px 16px; }
|
|
42
|
+
.domains-detail .flat-row { grid-template-columns:100px minmax(0,1fr);gap:10px; }
|
|
43
|
+
.domains-detail .flat-action:empty { display:none; }
|
|
44
|
+
.domains-detail .flat-value>.btn { margin-top:8px; }
|
|
45
|
+
.domains-detail .dns-record-toolbar { align-items:stretch; }
|
|
46
|
+
.domains-detail .dns-toolbar-actions { flex-wrap:wrap; }
|
|
47
|
+
.domains-narrow-scope { display:inline; }
|
|
48
|
+
.domains-narrow-status { display:block; }
|
|
49
|
+
.domains-page .tbl { table-layout:fixed;width:100%; }
|
|
50
|
+
.domains-page .tbl th:first-child { width:auto; }
|
|
51
|
+
.domains-page .tbl th:last-child { width:128px; }
|
|
52
|
+
.domains-page .tbl td:last-child .btn { white-space:nowrap; }
|
|
53
|
+
.domains-page .tbl th,.domains-page .tbl td { padding-left:12px;padding-right:12px;overflow-wrap:anywhere; }
|
|
54
|
+
}
|
|
55
|
+
.domains-registration .domains-overview-actions:first-child { margin-top:0; }
|
|
56
|
+
.domains-registration .detail-grid,.domains-registration-contact .detail-grid { margin:20px 0;display:grid; }
|
|
57
|
+
.domains-registration .detail-grid>div,.domains-registration-contact .detail-grid>div { display:grid;grid-template-columns:minmax(130px,.45fr) minmax(0,1fr);gap:16px;padding:11px 0;border-bottom:1px solid var(--line); }
|
|
58
|
+
.domains-registration .detail-grid dt,.domains-registration-contact .detail-grid dt { color:var(--mute); }
|
|
59
|
+
.domains-registration .detail-grid dd,.domains-registration-contact .detail-grid dd { margin:0;overflow-wrap:anywhere; }
|
|
60
|
+
.domains-registration-contact>.dim { margin:0 0 20px; }
|
|
61
|
+
.domains-registration-contact .mojo-tab-panel,.dns-registrant-scoped .mojo-tab-panel { padding-top:18px; }
|
|
62
|
+
@media(max-width:560px) {
|
|
63
|
+
.domains-registration .detail-grid>div,.domains-registration-contact .detail-grid>div { grid-template-columns:minmax(100px,.45fr) minmax(0,1fr);gap:10px; }
|
|
64
|
+
.domains-registration-contact .col-6,.dns-registrant-scoped .col-6 { grid-column:1/-1; }
|
|
65
|
+
.dns-registrant-scoped .form-grid>.col-6:nth-child(-n+2) { grid-column:span 6; }
|
|
66
|
+
}
|
|
2
67
|
|
|
3
|
-
.
|
|
4
|
-
.domains-header { align-items: center; margin-bottom: 24px; }
|
|
5
|
-
|
|
6
|
-
.domains-page, .domains-page > * { min-width: 0; }
|
|
7
|
-
.domains-demo { grid-template-columns: minmax(0, 1fr); }
|
|
8
|
-
.domains-demo > * { min-width: 0; }
|
|
9
|
-
@media (max-width: 560px) { .domains-header { align-items: flex-start; } }
|
|
10
|
-
|
|
11
|
-
.domain-onboarding > .dim { margin: 4px 0 20px; }
|
|
12
|
-
.domain-onboarding .dns-registrar-grid { gap: 20px; }
|
|
68
|
+
.dns-registrant-scoped .dns-contact-status { flex-direction:row;align-items:center;justify-content:flex-start; }
|
|
@@ -115,6 +115,7 @@
|
|
|
115
115
|
.edge-vhost-editor .edge-route-row { align-items: end; }
|
|
116
116
|
.edge-hostname-detail .edge-route-actions .btn { white-space: nowrap; }
|
|
117
117
|
|
|
118
|
+
.edge-serving-actions { display:flex;flex-wrap:wrap;gap:8px; }
|
|
118
119
|
.edge-serving-heading.page-header { align-items: flex-start; }
|
|
119
120
|
.edge-serving-heading.page-header > div { min-width: 0; }
|
|
120
121
|
.edge-serving-evidence .edge-row-actions { align-items: center; gap: 6px; }
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
because it names a token rather than a color. */
|
|
4
4
|
|
|
5
5
|
/* ── Page shell ─────────────────────────────────────────────────── */
|
|
6
|
+
.jobs-page-links { display:flex;flex-wrap:wrap;gap:8px; }
|
|
6
7
|
.jobs-dashboard { display: grid; gap: 14px; }
|
|
7
8
|
.jobs-page-head {
|
|
8
9
|
display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
|
package/src/ui/SidebarNav.tsx
CHANGED
|
@@ -115,6 +115,15 @@ function countDestinations(items: MenuItem[]): number {
|
|
|
115
115
|
+ countDestinations(item.children ?? []), 0);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
/** Keep secondary tools searchable without filling the everyday navigation. */
|
|
119
|
+
function primaryTree(item: MenuItem, pathname: string): MenuItem | null {
|
|
120
|
+
if (item.searchOnly && !treeOwnsRoute(item, pathname)) return null;
|
|
121
|
+
if (!item.children) return item;
|
|
122
|
+
const children = item.children.map(child => primaryTree(child, pathname))
|
|
123
|
+
.filter((child): child is MenuItem => child != null);
|
|
124
|
+
return !item.route && children.length === 0 ? null : { ...item, children };
|
|
125
|
+
}
|
|
126
|
+
|
|
118
127
|
function treeOwnsRoute(item: MenuItem, pathname: string): boolean {
|
|
119
128
|
if (item.route && routesMatch(item.route, pathname, item.exact)) return true;
|
|
120
129
|
return (item.children ?? []).some((child) => treeOwnsRoute(child, pathname));
|
|
@@ -299,7 +308,7 @@ export function SidebarNav({ collapsed = false, onRequestExpand }: SidebarNavPro
|
|
|
299
308
|
const normalizedQuery = query.trim().toLocaleLowerCase();
|
|
300
309
|
const filteredItems = useMemo(() => normalizedQuery
|
|
301
310
|
? retainRunDividers(items, items.map((item) => filterTree(item, normalizedQuery)))
|
|
302
|
-
: items, [items, normalizedQuery]);
|
|
311
|
+
: retainRunDividers(items, items.map(item => primaryTree(item, pathname))), [items, normalizedQuery, pathname]);
|
|
303
312
|
const resultCount = countDestinations(filteredItems);
|
|
304
313
|
|
|
305
314
|
if (!menu) return null;
|
package/src/ui/menu-registry.ts
CHANGED
|
@@ -29,6 +29,8 @@ export interface MenuItem {
|
|
|
29
29
|
label?: string;
|
|
30
30
|
/** Extra quick-navigation terms that are never rendered. */
|
|
31
31
|
keywords?: string[];
|
|
32
|
+
/** In accordion menus, show only in search or while this destination is active. */
|
|
33
|
+
searchOnly?: boolean;
|
|
32
34
|
icon?: string;
|
|
33
35
|
/** Absolute route within the hash router, e.g. '/users'. */
|
|
34
36
|
route?: string;
|