Polish desktop workbench UI

This commit is contained in:
mirivlad 2026-07-02 00:49:29 +08:00
parent 12b1e5a496
commit 5b4c792de5
21 changed files with 678 additions and 264 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@ -0,0 +1,96 @@
# UI Polish Milestone
## Assumptions
- This milestone is visual polish only: no backend API changes, no plugin contract changes, and no feature removal.
- English remains the baseline UI language for this pass. Hardcoded Russian strings that are visible in core UI should be converted to English where touched.
- Official plugins are plain JS bundles with injected CSS. Introducing a new shared UI package would be larger than this milestone, so shared primitives will be expressed as global design tokens/classes plus consistent local CSS in each bundle.
- Desktop workbench density should stay compact. The goal is consistency, not a marketing-style redesign.
## Audit Findings
### Existing Tokens
- `frontend/src/App.svelte` already defines partial global button classes: `btn-primary`, `btn-secondary`, `btn-danger`, `btn-ghost`, `btn-icon`, plus global button focus/disabled behavior.
- Most shell and plugin surfaces hardcode the same dark palette directly: `#1a1a2e`, `#16213e`, `#0f3460`, `#4ecca3`, `#e94560`, `#8b8ba8`.
- There are no named CSS variables for semantic surface, selected row, border, muted text, warning, or focus ring.
- Plugin bundles repeat their own toolbar/button/list/empty/error styles instead of reusing shell-level primitives.
### Repeated Patterns
- Page/workspace headers: `WorkspaceHost.svelte`, `WorkbenchHost.svelte`, `ViewContainer.svelte`, plugin toolbars.
- Tab bars: workspace tabs use a custom active style separate from selected rows and sidebar active states.
- Toolbars: Files, Notes, Search, Activity, Journal, Browser Inbox, and default editor use similar but inconsistent dark toolbar rules.
- List rows: Files rows, Notes rows, Activity rows, Journal rows, Secrets list items, Browser Inbox rows, Global Search results, WorkspaceTree rows.
- Cards/panels: Today panels, worklog suggestions, Secrets detail/form, Browser Inbox detail text, plugin manager cards.
- Empty/error states: many are plain centered text or show technical details as primary text.
### Candidate Primitives
Implement as global classes/tokens first, then apply locally:
- PageHeader / WorkspaceHeader: compact title, metadata, right actions/search.
- TabBar / TabButton: consistent active tab, hover, focus.
- Toolbar / ToolbarGroup: grouped controls with separators and predictable height.
- Button variants: primary, secondary, ghost, danger, icon.
- ListRow: hover, selected, disabled, metadata line, actions zone.
- Card: flat desktop panel with 6px radius and subtle border.
- SplitPane: list/detail grid with shared border and selected item states.
- EmptyState: centered title, hint, optional action.
- InlineAlert: readable message plus optional details/debug text.
- StatusBadge: compact semantic label for type, status, warning/error.
- Menu: context menu with same surface, hover, danger item, separator.
## Implementation Plan
1. Add semantic CSS variables and global primitive classes in `frontend/src/App.svelte`.
- Colors: background, surface, surface-muted, surface-hover, surface-selected, border, border-strong, text-primary, text-secondary, text-muted, accent, accent-muted, danger, warning, success.
- Spacing: 4/8/12/16/24/32 via variables.
- Radius, font sizes, focus ring, and modest elevation tokens.
2. Normalize shell surfaces.
- Update `WorkspaceHost.svelte`, `WorkbenchHost.svelte`, `ViewContainer.svelte`, `Sidebar.svelte`, `WorkspaceTree.svelte`, `GlobalSearch.svelte`, and `TodaySurface.svelte` to use tokens and shared state rules.
- Convert visible Russian shell search/empty strings to English.
- Keep plugin manager architecture untouched; only global tokens should make it less visually divergent.
3. Normalize Files.
- Group toolbar into Navigation, Create, Selection, Clipboard/More, and filter/sort areas.
- Keep existing context menu and operations, restyle menu with shared surface/hover/danger states.
- Make single row click open when no modifier is used; keep ctrl/meta/shift selection behavior and keyboard selection.
- Reduce actions column visual noise by showing secondary row actions on hover/selection and using a compact More/action group.
4. Normalize Notes.
- Make New Note the primary page action.
- Keep filter/sort inside the toolbar group.
- Make entire note row readable and clickable; row actions remain visible enough and use icon-button states.
- Use shared empty/status/error styling.
5. Normalize Search.
- Render results as compact rows/cards with type badge/icon, title/path, secondary match reason, and compact Open button.
- Show provider errors as inline alert-style secondary information, not as raw main status text.
6. Normalize Activity and Journal.
- Add human-readable event labels such as "Workspace selected", "File opened", "Note edited", and "Work session detected".
- Move raw event names/source IDs into a details block or secondary metadata.
- Style plugin errors through inline alert patterns.
- Present worklog suggestions as cards with consistent title/summary/minutes actions.
7. Normalize Secrets and Browser Inbox.
- Keep split-pane list/detail layout.
- Use shared list rows, detail card/form surfaces, and empty states.
- Render "Value hidden" as password-like hidden field state.
- Use button variants: primary for save/create, secondary for copy/edit/remove where appropriate, danger for delete/clear/remove destructive actions.
8. Verify.
- Run desktop frontend build and relevant Playwright checks if available.
- Run official plugin smoke/check script.
- Capture before/after screenshots or save available after screenshots under `docs/ui-polish-assets/`.
## Out of Scope
- Backend API or Wails method changes.
- Plugin manifest/contribution contract changes.
- New heavy UI dependencies.
- Full i18n extraction.
- Redesigning the product information architecture.
- Rebuilding official plugins into Svelte packages.

View File

@ -49,6 +49,15 @@ test.describe('Activity workflow', () => {
sourcePluginId: 'verstak.files', sourcePluginId: 'verstak.files',
workspaceRootPath: 'Project', workspaceRootPath: 'Project',
}, },
{
activityId: 'activity-e2e-open',
occurredAt: '2026-06-30T08:30:00.000Z',
type: 'file.opened',
title: 'Selected file',
summary: 'Project/Notes/Research Capture.md',
sourcePluginId: 'verstak.files',
workspaceRootPath: 'Project',
},
], ],
}); });
}); });
@ -59,6 +68,7 @@ test.describe('Activity workflow', () => {
await expect(activity.locator('.activity-count')).toHaveText('2 events'); await expect(activity.locator('.activity-count')).toHaveText('2 events');
await expect(activity.locator('[data-activity-id="activity-e2e-capture"]')).toContainText('Research Capture'); await expect(activity.locator('[data-activity-id="activity-e2e-capture"]')).toContainText('Research Capture');
await expect(activity.locator('[data-activity-id="activity-e2e-note"]')).toContainText('Saved note'); await expect(activity.locator('[data-activity-id="activity-e2e-note"]')).toContainText('Saved note');
await expect(activity.locator('[data-activity-id="activity-e2e-open"]')).toHaveCount(0);
await expect(activity.locator('[data-activity-section="worklog-suggestions"]')).toContainText('Worklog suggestions'); await expect(activity.locator('[data-activity-section="worklog-suggestions"]')).toContainText('Worklog suggestions');
await expect(activity.locator('[data-worklog-suggestion="worklog:Project:2026-06-30"]')).toContainText('Project work on 2026-06-30'); await expect(activity.locator('[data-worklog-suggestion="worklog:Project:2026-06-30"]')).toContainText('Project work on 2026-06-30');
await expect(activity.locator('[data-worklog-suggestion="worklog:Project:2026-06-30"]')).toContainText('50 min'); await expect(activity.locator('[data-worklog-suggestion="worklog:Project:2026-06-30"]')).toContainText('50 min');

View File

@ -97,9 +97,9 @@ test.describe('UX quick wins', () => {
await page.goto('/'); await page.goto('/');
await page.waitForSelector('.vault-selection', { timeout: 10000 }); await page.waitForSelector('.vault-selection', { timeout: 10000 });
await expect(page.getByText('Выберите vault для начала работы')).toBeVisible(); await expect(page.getByText('Choose a vault to start working')).toBeVisible();
await expect(page.getByRole('button', { name: 'Создать vault' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Create vault' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Открыть существующий' })).toBeVisible(); await expect(page.getByRole('button', { name: 'Open existing' })).toBeVisible();
await page.close(); await page.close();
}); });
}); });

View File

@ -416,6 +416,41 @@
box-sizing: border-box; box-sizing: border-box;
} }
:global(:root) {
--vt-color-background: #101020;
--vt-color-surface: #15152c;
--vt-color-surface-muted: #111629;
--vt-color-surface-hover: #1b2440;
--vt-color-surface-selected: rgba(78, 204, 163, 0.14);
--vt-color-border: #202b46;
--vt-color-border-strong: #2c456a;
--vt-color-text-primary: #f4f7fb;
--vt-color-text-secondary: #b7c0d4;
--vt-color-text-muted: #7f8aa3;
--vt-color-accent: #4ecca3;
--vt-color-accent-muted: rgba(78, 204, 163, 0.14);
--vt-color-danger: #e94560;
--vt-color-danger-muted: rgba(233, 69, 96, 0.14);
--vt-color-warning: #ffc857;
--vt-color-warning-muted: rgba(255, 200, 87, 0.14);
--vt-color-success: #4ecca3;
--vt-space-1: 0.25rem;
--vt-space-2: 0.5rem;
--vt-space-3: 0.75rem;
--vt-space-4: 1rem;
--vt-space-6: 1.5rem;
--vt-space-8: 2rem;
--vt-radius-sm: 4px;
--vt-radius-md: 6px;
--vt-radius-lg: 8px;
--vt-font-xs: 0.72rem;
--vt-font-sm: 0.8rem;
--vt-font-md: 0.88rem;
--vt-font-lg: 1rem;
--vt-focus-ring: 0 0 0 2px rgba(78, 204, 163, 0.34);
--vt-elevation-menu: 0 14px 32px rgba(0, 0, 0, 0.42);
}
:global(html), :global(html),
:global(body), :global(body),
:global(#app) { :global(#app) {
@ -425,8 +460,8 @@
:global(body) { :global(body) {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #1a1a2e; background: var(--vt-color-background);
color: #e0e0f0; color: var(--vt-color-text-primary);
overflow: hidden; overflow: hidden;
} }
@ -437,12 +472,12 @@
gap: 0.35rem; gap: 0.35rem;
min-height: 2rem; min-height: 2rem;
padding: 0.4rem 0.85rem; padding: 0.4rem 0.85rem;
border: 1px solid #1a3a5c; border: 1px solid var(--vt-color-border-strong);
border-radius: 6px; border-radius: var(--vt-radius-md);
background: #0f3460; background: var(--vt-color-surface-hover);
color: #e0e0f0; color: var(--vt-color-text-primary);
font: inherit; font: inherit;
font-size: 0.85rem; font-size: var(--vt-font-sm);
font-weight: 600; font-weight: 600;
line-height: 1.2; line-height: 1.2;
cursor: pointer; cursor: pointer;
@ -450,14 +485,14 @@
} }
:global(button:hover:not(:disabled)) { :global(button:hover:not(:disabled)) {
background: #1a3a5c; background: #203050;
border-color: #4ecca3; border-color: var(--vt-color-accent);
color: #ffffff; color: #ffffff;
} }
:global(button:focus-visible) { :global(button:focus-visible) {
outline: 2px solid #4ecca3; outline: 0;
outline-offset: 2px; box-shadow: var(--vt-focus-ring);
} }
:global(button:disabled) { :global(button:disabled) {
@ -466,8 +501,8 @@
} }
:global(.btn-primary) { :global(.btn-primary) {
background: #4ecca3; background: var(--vt-color-accent);
border-color: #4ecca3; border-color: var(--vt-color-accent);
color: #101827; color: #101827;
} }
@ -478,14 +513,14 @@
} }
:global(.btn-secondary) { :global(.btn-secondary) {
background: #0f3460; background: var(--vt-color-surface-hover);
border-color: #533483; border-color: var(--vt-color-border-strong);
color: #e0e0f0; color: var(--vt-color-text-primary);
} }
:global(.btn-danger) { :global(.btn-danger) {
background: #e94560; background: var(--vt-color-danger);
border-color: #e94560; border-color: var(--vt-color-danger);
color: #ffffff; color: #ffffff;
} }
@ -497,13 +532,13 @@
:global(.btn-ghost) { :global(.btn-ghost) {
background: transparent; background: transparent;
border-color: transparent; border-color: transparent;
color: #a0a0b8; color: var(--vt-color-text-secondary);
} }
:global(.btn-ghost:hover:not(:disabled)) { :global(.btn-ghost:hover:not(:disabled)) {
background: rgba(15, 52, 96, 0.55); background: var(--vt-color-surface-hover);
border-color: #0f3460; border-color: var(--vt-color-border);
color: #e0e0f0; color: var(--vt-color-text-primary);
} }
:global(.btn-icon) { :global(.btn-icon) {
@ -512,6 +547,236 @@
padding: 0; padding: 0;
} }
:global(.vt-page) {
min-width: 0;
min-height: 0;
height: 100%;
display: flex;
flex-direction: column;
background: var(--vt-color-background);
color: var(--vt-color-text-primary);
}
:global(.vt-page-header),
:global(.vt-toolbar) {
min-height: 2.75rem;
display: flex;
align-items: center;
gap: var(--vt-space-2);
padding: var(--vt-space-2) var(--vt-space-3);
border-bottom: 1px solid var(--vt-color-border);
background: var(--vt-color-surface-muted);
flex-shrink: 0;
}
:global(.vt-page-title) {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--vt-color-text-primary);
font-size: var(--vt-font-lg);
font-weight: 650;
}
:global(.vt-page-subtitle) {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--vt-color-text-muted);
font-size: var(--vt-font-sm);
}
:global(.vt-toolbar-group) {
display: inline-flex;
align-items: center;
gap: var(--vt-space-1);
padding: 0 var(--vt-space-2) 0 0;
border-right: 1px solid var(--vt-color-border);
}
:global(.vt-toolbar-group:last-child) {
border-right: 0;
padding-right: 0;
}
:global(.vt-button) {
min-height: 2rem;
border-radius: var(--vt-radius-md);
}
:global(.vt-icon-button) {
width: 2rem;
min-width: 2rem;
height: 2rem;
min-height: 2rem;
padding: 0;
}
:global(.vt-tabbar) {
display: flex;
align-items: center;
gap: var(--vt-space-1);
padding: var(--vt-space-1) var(--vt-space-3) 0;
border-bottom: 1px solid var(--vt-color-border);
background: #12162a;
overflow-x: auto;
flex-shrink: 0;
}
:global(.vt-tab) {
flex-shrink: 0;
min-height: 2.1rem;
padding: 0.38rem 0.78rem;
border: 1px solid transparent;
border-bottom: 0;
border-radius: var(--vt-radius-md) var(--vt-radius-md) 0 0;
background: transparent;
color: var(--vt-color-text-muted);
font-size: var(--vt-font-sm);
}
:global(.vt-tab:hover:not(:disabled)) {
background: var(--vt-color-surface-hover);
border-color: transparent;
color: var(--vt-color-text-primary);
}
:global(.vt-tab.is-active),
:global(.vt-tab.active) {
background: var(--vt-color-background);
border-color: var(--vt-color-border);
color: var(--vt-color-accent);
}
:global(.vt-list-row) {
border-bottom: 1px solid rgba(32, 43, 70, 0.72);
color: var(--vt-color-text-primary);
}
:global(.vt-list-row:hover) {
background: var(--vt-color-surface-hover);
}
:global(.vt-list-row.selected),
:global(.vt-list-row.active),
:global(.vt-list-row.is-selected) {
background: var(--vt-color-surface-selected);
box-shadow: inset 2px 0 0 var(--vt-color-accent);
}
:global(.vt-card) {
border: 1px solid var(--vt-color-border);
border-radius: var(--vt-radius-lg);
background: var(--vt-color-surface);
}
:global(.vt-split-pane) {
display: grid;
grid-template-columns: minmax(16rem, 22rem) minmax(0, 1fr);
min-height: 0;
height: 100%;
background: var(--vt-color-background);
}
:global(.vt-split-list) {
min-height: 0;
overflow: auto;
border-right: 1px solid var(--vt-color-border);
background: var(--vt-color-surface-muted);
}
:global(.vt-split-detail) {
min-width: 0;
min-height: 0;
overflow: auto;
padding: var(--vt-space-4);
}
:global(.vt-empty-state) {
min-height: 9rem;
display: flex;
flex: 1;
flex-direction: column;
align-items: center;
justify-content: center;
gap: var(--vt-space-2);
padding: var(--vt-space-8);
color: var(--vt-color-text-muted);
font-size: var(--vt-font-sm);
line-height: 1.45;
text-align: center;
}
:global(.vt-empty-title) {
color: var(--vt-color-text-secondary);
font-size: var(--vt-font-md);
font-weight: 650;
}
:global(.vt-inline-alert) {
border: 1px solid var(--vt-color-border-strong);
border-radius: var(--vt-radius-md);
background: var(--vt-color-surface-muted);
color: var(--vt-color-text-secondary);
padding: var(--vt-space-3);
font-size: var(--vt-font-sm);
line-height: 1.45;
}
:global(.vt-inline-alert.error) {
border-color: rgba(233, 69, 96, 0.55);
background: var(--vt-color-danger-muted);
color: #ffc6ce;
}
:global(.vt-badge) {
display: inline-flex;
align-items: center;
min-height: 1.25rem;
padding: 0.1rem 0.4rem;
border: 1px solid var(--vt-color-border);
border-radius: var(--vt-radius-sm);
background: var(--vt-color-surface-muted);
color: var(--vt-color-text-muted);
font-size: var(--vt-font-xs);
font-weight: 650;
line-height: 1;
}
:global(.vt-badge.accent) {
border-color: rgba(78, 204, 163, 0.4);
background: var(--vt-color-accent-muted);
color: var(--vt-color-accent);
}
:global(.vt-menu) {
border: 1px solid var(--vt-color-border-strong);
border-radius: var(--vt-radius-md);
background: var(--vt-color-surface);
color: var(--vt-color-text-primary);
box-shadow: var(--vt-elevation-menu);
}
:global(.vt-menu-item) {
display: flex;
align-items: center;
gap: var(--vt-space-2);
padding: 0.42rem 0.7rem;
color: var(--vt-color-text-secondary);
cursor: pointer;
}
:global(.vt-menu-item:hover) {
background: var(--vt-color-surface-hover);
color: var(--vt-color-text-primary);
}
:global(.vt-menu-item.danger) {
color: #ff9aaa;
}
:global(.scroll-surface) { :global(.scroll-surface) {
min-width: 0; min-width: 0;
min-height: 0; min-height: 0;
@ -521,7 +786,7 @@
:global(*) { :global(*) {
scrollbar-width: thin; scrollbar-width: thin;
scrollbar-color: #0f3460 #1a1a2e; scrollbar-color: var(--vt-color-border-strong) var(--vt-color-background);
} }
:global(*::-webkit-scrollbar) { :global(*::-webkit-scrollbar) {
@ -530,12 +795,12 @@
} }
:global(*::-webkit-scrollbar-track) { :global(*::-webkit-scrollbar-track) {
background: #1a1a2e; background: var(--vt-color-background);
} }
:global(*::-webkit-scrollbar-thumb) { :global(*::-webkit-scrollbar-thumb) {
background: #0f3460; background: var(--vt-color-border-strong);
border-radius: 4px; border-radius: var(--vt-radius-sm);
} }
:global(*::-webkit-scrollbar-thumb:hover) { :global(*::-webkit-scrollbar-thumb:hover) {
@ -547,8 +812,8 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100vh; height: 100vh;
background: #1a1a2e; background: var(--vt-color-background);
color: #a0a0b8; color: var(--vt-color-text-muted);
font-size: 1rem; font-size: 1rem;
} }
@ -556,7 +821,7 @@
display: flex; display: flex;
height: 100vh; height: 100vh;
width: 100%; width: 100%;
background: #1a1a2e; background: var(--vt-color-background);
overflow: hidden; overflow: hidden;
} }
@ -574,7 +839,7 @@
min-height: 0; min-height: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: clamp(1rem, 2vw, 1.5rem); padding: 0;
} }
@media (max-width: 720px) { @media (max-width: 720px) {

View File

@ -161,7 +161,7 @@
next.push({ next.push({
type: 'Workspace', type: 'Workspace',
title: workspaceTitle(node), title: workspaceTitle(node),
subtitle: 'Рабочее пространство', subtitle: 'Workspace',
keywords: `${node.id || ''} ${node.rootPath || ''}`, keywords: `${node.id || ''} ${node.rootPath || ''}`,
rank: 10, rank: 10,
action: 'workspace', action: 'workspace',
@ -293,8 +293,8 @@
on:focus={handleFocus} on:focus={handleFocus}
on:blur={() => setTimeout(() => focused = false, 120)} on:blur={() => setTimeout(() => focused = false, 120)}
type="search" type="search"
placeholder={loading ? 'Индексируем...' : 'Поиск'} placeholder={loading ? 'Indexing...' : 'Search'}
aria-label="Глобальный поиск" aria-label="Global search"
data-global-search-input data-global-search-input
/> />
</div> </div>
@ -314,7 +314,7 @@
</button> </button>
{/each} {/each}
{:else} {:else}
<div class="global-search-empty">Ничего не найдено</div> <div class="global-search-empty vt-empty-title">No results</div>
{/if} {/if}
</div> </div>
{/if} {/if}
@ -324,7 +324,7 @@
.global-search { .global-search {
position: relative; position: relative;
padding: 0.55rem 0.75rem; padding: 0.55rem 0.75rem;
border-bottom: 1px solid #0f3460; border-bottom: 1px solid var(--vt-color-border);
flex-shrink: 0; flex-shrink: 0;
} }
@ -334,14 +334,14 @@
gap: 0.35rem; gap: 0.35rem;
height: 2rem; height: 2rem;
padding: 0 0.55rem; padding: 0 0.55rem;
border: 1px solid #263653; border: 1px solid var(--vt-color-border-strong);
border-radius: 5px; border-radius: var(--vt-radius-md);
background: #101626; background: #0f1424;
color: #8b8ba8; color: var(--vt-color-text-muted);
} }
:global(.global-search-icon) { :global(.global-search-icon) {
color: #8b8ba8; color: var(--vt-color-text-muted);
flex-shrink: 0; flex-shrink: 0;
} }
@ -351,17 +351,18 @@
border: 0; border: 0;
outline: 0; outline: 0;
background: transparent; background: transparent;
color: #e0e0f0; color: var(--vt-color-text-primary);
font: inherit; font: inherit;
font-size: 0.78rem; font-size: 0.78rem;
} }
.global-search input::placeholder { .global-search input::placeholder {
color: #6f7894; color: var(--vt-color-text-muted);
} }
.global-search-box:focus-within { .global-search-box:focus-within {
border-color: #4ecca3; border-color: var(--vt-color-accent);
box-shadow: var(--vt-focus-ring);
} }
.global-search-results { .global-search-results {
@ -372,10 +373,10 @@
z-index: 400; z-index: 400;
max-height: 20rem; max-height: 20rem;
overflow: auto; overflow: auto;
border: 1px solid #28466f; border: 1px solid var(--vt-color-border-strong);
border-radius: 6px; border-radius: var(--vt-radius-md);
background: #101626; background: var(--vt-color-surface);
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.45); box-shadow: var(--vt-elevation-menu);
} }
.global-search-result { .global-search-result {
@ -387,13 +388,13 @@
border: 0; border: 0;
border-bottom: 1px solid rgba(40, 70, 111, 0.55); border-bottom: 1px solid rgba(40, 70, 111, 0.55);
background: transparent; background: transparent;
color: #d9e1ef; color: var(--vt-color-text-primary);
text-align: left; text-align: left;
cursor: pointer; cursor: pointer;
} }
.global-search-result:hover { .global-search-result:hover {
background: rgba(78, 204, 163, 0.1); background: var(--vt-color-surface-hover);
} }
.global-search-result-title { .global-search-result-title {
@ -406,7 +407,7 @@
.global-search-result-meta, .global-search-result-meta,
.global-search-empty { .global-search-empty {
color: #8b8ba8; color: var(--vt-color-text-muted);
font-size: 0.7rem; font-size: 0.7rem;
} }

View File

@ -88,7 +88,7 @@
<span class="section-label">Tools</span> <span class="section-label">Tools</span>
{#each sidebarItems as item} {#each sidebarItems as item}
<button <button
class="nav-item plugin-item" class="nav-item plugin-item vt-list-row"
on:click={() => handleSidebarItem(item)} on:click={() => handleSidebarItem(item)}
type="button" type="button"
> >
@ -117,10 +117,10 @@
.sidebar { .sidebar {
width: 220px; width: 220px;
min-width: 220px; min-width: 220px;
background: #16213e; background: var(--vt-color-surface-muted);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
border-right: 1px solid #0f3460; border-right: 1px solid var(--vt-color-border);
overflow: hidden; overflow: hidden;
} }
@ -129,18 +129,18 @@
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.5rem;
padding: 1rem 1.25rem; padding: 1rem 1.25rem;
border-bottom: 1px solid #0f3460; border-bottom: 1px solid var(--vt-color-border);
} }
:global(.sidebar-logo) { :global(.sidebar-logo) {
width: 1.2rem; width: 1.2rem;
height: 1.2rem; height: 1.2rem;
color: #4ecca3; color: var(--vt-color-accent);
flex-shrink: 0; flex-shrink: 0;
} }
.sidebar-title { .sidebar-title {
color: #e0e0f0; color: var(--vt-color-text-primary);
font-size: 1rem; font-size: 1rem;
font-weight: 600; font-weight: 600;
} }
@ -150,7 +150,7 @@
flex-direction: column; flex-direction: column;
padding: 0.45rem 0.6rem 0.55rem; padding: 0.45rem 0.6rem 0.55rem;
gap: 0.15rem; gap: 0.15rem;
border-bottom: 1px solid #0f3460; border-bottom: 1px solid var(--vt-color-border);
} }
:global(workspace-tree) { :global(workspace-tree) {
@ -161,7 +161,7 @@
} }
.section-label { .section-label {
color: #a0a0b8; color: var(--vt-color-text-muted);
font-size: 0.7rem; font-size: 0.7rem;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
@ -174,22 +174,22 @@
align-items: center; align-items: center;
justify-content: flex-start; justify-content: flex-start;
gap: 0.45rem; gap: 0.45rem;
min-height: 1.7rem; min-height: 1.85rem;
padding: 0.15rem 0.45rem; padding: 0.15rem 0.45rem;
background: none; background: none;
border: none; border: none;
color: #a0a0b8; color: var(--vt-color-text-secondary);
font-size: 0.78rem; font-size: 0.78rem;
cursor: pointer; cursor: pointer;
border-radius: 3px; border-radius: var(--vt-radius-sm);
text-align: left; text-align: left;
width: 100%; width: 100%;
transition: background 0.15s, color 0.15s; transition: background 0.15s, color 0.15s;
} }
.nav-item:hover { .nav-item:hover {
background: rgba(15,52,96,0.4); background: var(--vt-color-surface-hover);
color: #e0e0f0; color: var(--vt-color-text-primary);
} }
:global(.nav-icon) { :global(.nav-icon) {
@ -199,7 +199,8 @@
color: currentColor; color: currentColor;
} }
:global(.nav-icon.icon-plugin) { :global(.nav-icon.icon-plugin) {
color: #a78bfa; color: var(--vt-color-text-muted);
opacity: 0.9;
} }
.nav-label { .nav-label {
@ -211,7 +212,7 @@
.sidebar-footer { .sidebar-footer {
margin-top: auto; margin-top: auto;
padding: 0.75rem 1.25rem; padding: 0.75rem 1.25rem;
border-top: 1px solid #0f3460; border-top: 1px solid var(--vt-color-border);
} }
.sidebar-error { .sidebar-error {
@ -219,11 +220,11 @@
align-items: center; align-items: center;
gap: 0.25rem; gap: 0.25rem;
font-size: 0.7rem; font-size: 0.7rem;
color: #e94560; color: var(--vt-color-danger);
margin-bottom: 0.25rem; margin-bottom: 0.25rem;
} }
:global(.sidebar-error-icon) { :global(.sidebar-error-icon) {
color: #e94560; color: var(--vt-color-danger);
} }
@media (max-width: 720px) { @media (max-width: 720px) {
@ -232,7 +233,7 @@
min-width: 0; min-width: 0;
max-height: 14rem; max-height: 14rem;
border-right: 0; border-right: 0;
border-bottom: 1px solid #0f3460; border-bottom: 1px solid var(--vt-color-border);
} }
.sidebar-header { .sidebar-header {

View File

@ -65,7 +65,25 @@
} }
function activityTitle(item) { function activityTitle(item) {
return item.title || item.summary || item.type || item.activityId || 'Activity event'; return item.title || item.summary || humanActivityType(item.type) || 'Activity event';
}
function humanActivityType(type) {
const labels = {
'workspace.selected': 'Workspace selected',
'case.selected': 'Workspace selected',
'file.opened': 'File opened',
'file.changed': 'File changed',
'note.saved': 'Note edited',
'browser.capture.received': 'Browser capture received',
'browser.capture.page': 'Page captured',
'browser.capture.selection': 'Selection captured',
'browser.capture.link': 'Link captured',
'browser.capture.file': 'File captured',
'browser.capture.converted': 'Capture converted',
'action.started': 'Work session detected'
};
return labels[String(type || '').toLowerCase()] || '';
} }
function worklogTitle(item) { function worklogTitle(item) {
@ -265,8 +283,8 @@
min-height: 0; min-height: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background: #101020; background: var(--vt-color-background);
color: #e0e0f0; color: var(--vt-color-text-primary);
overflow: auto; overflow: auto;
} }
@ -276,8 +294,8 @@
justify-content: space-between; justify-content: space-between;
gap: 1rem; gap: 1rem;
padding: 1rem; padding: 1rem;
border-bottom: 1px solid #16213e; border-bottom: 1px solid var(--vt-color-border);
background: #12122a; background: var(--vt-color-surface-muted);
} }
.today-header h2 { .today-header h2 {
@ -287,7 +305,7 @@
.today-header p { .today-header p {
margin: 0.25rem 0 0; margin: 0.25rem 0 0;
color: #8b8ba8; color: var(--vt-color-text-muted);
font-size: 0.8rem; font-size: 0.8rem;
} }
@ -310,13 +328,13 @@
display: grid; display: grid;
gap: 0.15rem; gap: 0.15rem;
padding: 0.65rem 0.75rem; padding: 0.65rem 0.75rem;
border: 1px solid rgba(78, 204, 163, 0.16); border: 1px solid var(--vt-color-border);
border-radius: 8px; border-radius: var(--vt-radius-lg);
background: #121a2c; background: var(--vt-color-surface);
} }
.today-summary-item strong { .today-summary-item strong {
color: #f4f7fb; color: var(--vt-color-text-primary);
font-size: 1rem; font-size: 1rem;
line-height: 1; line-height: 1;
} }
@ -326,7 +344,7 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
color: #8b8ba8; color: var(--vt-color-text-muted);
font-size: 0.74rem; font-size: 0.74rem;
} }
@ -338,8 +356,8 @@
margin: 0.75rem 0.75rem 0; margin: 0.75rem 0.75rem 0;
padding: 0.85rem 1rem; padding: 0.85rem 1rem;
border: 1px solid rgba(78, 204, 163, 0.24); border: 1px solid rgba(78, 204, 163, 0.24);
border-radius: 8px; border-radius: var(--vt-radius-lg);
background: linear-gradient(135deg, rgba(78, 204, 163, 0.11), rgba(15, 52, 96, 0.14)); background: linear-gradient(135deg, rgba(78, 204, 163, 0.11), rgba(27, 36, 64, 0.6));
} }
.today-resume-copy { .today-resume-copy {
@ -349,7 +367,7 @@
} }
.today-resume-copy span { .today-resume-copy span {
color: #4ecca3; color: var(--vt-color-accent);
font-size: 0.72rem; font-size: 0.72rem;
font-weight: 700; font-weight: 700;
text-transform: uppercase; text-transform: uppercase;
@ -361,7 +379,7 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
color: #f4f7fb; color: var(--vt-color-text-primary);
font-size: 0.98rem; font-size: 0.98rem;
} }
@ -371,7 +389,7 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
color: #9aa4bd; color: var(--vt-color-text-secondary);
font-size: 0.8rem; font-size: 0.8rem;
} }
@ -380,9 +398,9 @@
min-height: 10rem; min-height: 10rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
border: 1px solid #16213e; border: 1px solid var(--vt-color-border);
border-radius: 8px; border-radius: var(--vt-radius-lg);
background: #15152c; background: var(--vt-color-surface);
} }
.today-panel-head { .today-panel-head {
@ -392,12 +410,12 @@
justify-content: space-between; justify-content: space-between;
gap: 0.75rem; gap: 0.75rem;
padding: 0.65rem 0.75rem; padding: 0.65rem 0.75rem;
border-bottom: 1px solid rgba(22, 33, 62, 0.8); border-bottom: 1px solid var(--vt-color-border);
} }
.today-panel h3 { .today-panel h3 {
margin: 0; margin: 0;
color: #f0f0ff; color: var(--vt-color-text-primary);
font-size: 0.9rem; font-size: 0.9rem;
} }
@ -416,7 +434,7 @@
justify-content: center; justify-content: center;
margin: 0; margin: 0;
padding: 1rem; padding: 1rem;
color: #8b8ba8; color: var(--vt-color-text-muted);
font-size: 0.82rem; font-size: 0.82rem;
line-height: 1.45; line-height: 1.45;
text-align: center; text-align: center;
@ -433,9 +451,9 @@
display: grid; display: grid;
gap: 0.2rem; gap: 0.2rem;
padding: 0.55rem; padding: 0.55rem;
border: 1px solid rgba(78, 204, 163, 0.16); border: 1px solid var(--vt-color-border);
border-radius: 6px; border-radius: var(--vt-radius-md);
background: #101827; background: var(--vt-color-surface-muted);
} }
.today-row strong { .today-row strong {
@ -443,7 +461,7 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
color: #f4f7fb; color: var(--vt-color-text-primary);
font-size: 0.85rem; font-size: 0.85rem;
} }
@ -452,7 +470,7 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
color: #8b8ba8; color: var(--vt-color-text-muted);
font-size: 0.75rem; font-size: 0.75rem;
} }

View File

@ -39,20 +39,20 @@
async function createVault() { async function createVault() {
error = ''; error = '';
if (!newVaultPath.trim()) { if (!newVaultPath.trim()) {
error = 'Укажите или выберите папку для нового vault'; error = 'Choose or enter a folder for the new vault.';
return; return;
} }
creating = true; creating = true;
try { try {
const createErr = await App.CreateVault(newVaultPath.trim()); const createErr = await App.CreateVault(newVaultPath.trim());
if (createErr) { if (createErr) {
error = 'Не удалось создать vault: ' + createErr; error = 'Could not create vault: ' + createErr;
creating = false; creating = false;
return; return;
} }
const openErr = await App.OpenVault(newVaultPath.trim()); const openErr = await App.OpenVault(newVaultPath.trim());
if (openErr) { if (openErr) {
error = 'Не удалось открыть vault: ' + openErr; error = 'Could not open vault: ' + openErr;
creating = false; creating = false;
return; return;
} }
@ -70,14 +70,14 @@
async function openExistingVault() { async function openExistingVault() {
error = ''; error = '';
if (!openVaultPath.trim()) { if (!openVaultPath.trim()) {
error = 'Укажите или выберите существующий vault'; error = 'Choose or enter an existing vault.';
return; return;
} }
opening = true; opening = true;
try { try {
const openErr = await App.OpenVault(openVaultPath.trim()); const openErr = await App.OpenVault(openVaultPath.trim());
if (openErr) { if (openErr) {
error = 'Не удалось открыть vault: ' + openErr; error = 'Could not open vault: ' + openErr;
opening = false; opening = false;
return; return;
} }
@ -98,7 +98,7 @@
try { try {
const openErr = await App.OpenVault(path); const openErr = await App.OpenVault(path);
if (openErr) { if (openErr) {
error = 'Не удалось открыть vault: ' + openErr; error = 'Could not open vault: ' + openErr;
opening = false; opening = false;
return; return;
} }
@ -117,7 +117,7 @@
{#if loading} {#if loading}
<div class="vault-selection"> <div class="vault-selection">
<div class="vault-selection-inner"> <div class="vault-selection-inner">
<p class="loading-text">Загрузка...</p> <p class="loading-text">Loading...</p>
</div> </div>
</div> </div>
{:else} {:else}
@ -130,7 +130,7 @@
<line x1="9" y1="14" x2="15" y2="14"/> <line x1="9" y1="14" x2="15" y2="14"/>
</svg> </svg>
<h1>Verstak</h1> <h1>Verstak</h1>
<p class="subtitle">Выберите vault для начала работы</p> <p class="subtitle">Choose a vault to start working</p>
</div> </div>
{#if error} {#if error}
@ -142,43 +142,43 @@
<div class="actions"> <div class="actions">
<div class="action-card"> <div class="action-card">
<h3>Создать новый vault</h3> <h3>Create a new vault</h3>
<p class="hint">Создайте локальную папку vault. В ней будут храниться рабочие пространства и проекты.</p> <p class="hint">Create a local vault folder for workspaces and projects.</p>
<div class="input-row"> <div class="input-row">
<input <input
type="text" type="text"
bind:value={newVaultPath} bind:value={newVaultPath}
placeholder="Выберите или введите путь..." placeholder="Choose or enter a path..."
disabled={creating} disabled={creating}
/> />
<button class="btn-secondary" on:click={browseNewVault} type="button" disabled={creating}> <button class="btn-secondary" on:click={browseNewVault} type="button" disabled={creating}>
Выбрать… Browse...
</button> </button>
</div> </div>
<div class="button-row"> <div class="button-row">
<button class="btn-primary" on:click={createVault} type="button" disabled={creating}> <button class="btn-primary" on:click={createVault} type="button" disabled={creating}>
{creating ? 'Создаём...' : 'Создать vault'} {creating ? 'Creating...' : 'Create vault'}
</button> </button>
</div> </div>
</div> </div>
<div class="action-card"> <div class="action-card">
<h3>Открыть существующий vault</h3> <h3>Open an existing vault</h3>
<p class="hint">Используйте vault, который уже есть на этом компьютере.</p> <p class="hint">Use a vault that is already on this computer.</p>
<div class="input-row"> <div class="input-row">
<input <input
type="text" type="text"
bind:value={openVaultPath} bind:value={openVaultPath}
placeholder="Выберите или введите путь..." placeholder="Choose or enter a path..."
disabled={opening} disabled={opening}
/> />
<button class="btn-secondary" on:click={browseOpenVault} type="button" disabled={opening}> <button class="btn-secondary" on:click={browseOpenVault} type="button" disabled={opening}>
Выбрать… Browse...
</button> </button>
</div> </div>
<div class="button-row"> <div class="button-row">
<button class="btn-secondary open-existing-btn" on:click={openExistingVault} type="button" disabled={opening}> <button class="btn-secondary open-existing-btn" on:click={openExistingVault} type="button" disabled={opening}>
{opening ? 'Открываем...' : 'Открыть существующий'} {opening ? 'Opening...' : 'Open existing'}
</button> </button>
</div> </div>
</div> </div>
@ -186,7 +186,7 @@
{#if recentVaults.length > 0} {#if recentVaults.length > 0}
<div class="recent-section"> <div class="recent-section">
<h3>Недавние vault</h3> <h3>Recent vaults</h3>
<ul class="recent-list"> <ul class="recent-list">
{#each recentVaults as path} {#each recentVaults as path}
<li> <li>
@ -209,7 +209,7 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100vh; height: 100vh;
background: #1a1a2e; background: var(--vt-color-background);
padding: 2rem; padding: 2rem;
} }
.vault-selection-inner { .vault-selection-inner {
@ -217,7 +217,7 @@
width: 100%; width: 100%;
} }
.loading-text { .loading-text {
color: #a0a0b8; color: var(--vt-color-text-muted);
text-align: center; text-align: center;
font-size: 1rem; font-size: 1rem;
} }
@ -226,26 +226,26 @@
margin-bottom: 2rem; margin-bottom: 2rem;
} }
.logo h1 { .logo h1 {
color: #e0e0f0; color: var(--vt-color-text-primary);
font-size: 1.8rem; font-size: 1.8rem;
margin: 0.5rem 0 0.25rem; margin: 0.5rem 0 0.25rem;
} }
.subtitle { .subtitle {
color: #a0a0b8; color: var(--vt-color-text-muted);
font-size: 0.95rem; font-size: 0.95rem;
margin: 0; margin: 0;
} }
.error-box { .error-box {
background: rgba(233, 69, 96, 0.1); background: var(--vt-color-danger-muted);
border: 1px solid #e94560; border: 1px solid rgba(233, 69, 96, 0.5);
border-radius: 8px; border-radius: var(--vt-radius-lg);
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
gap: 0.5rem; gap: 0.5rem;
font-size: 0.85rem; font-size: 0.85rem;
color: #e94560; color: #ffc6ce;
} }
:global(.error-icon) { flex-shrink: 0; } :global(.error-icon) { flex-shrink: 0; }
.error-text { word-break: break-word; } .error-text { word-break: break-word; }
@ -256,18 +256,18 @@
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
} }
.action-card { .action-card {
background: #16213e; background: var(--vt-color-surface);
border: 1px solid #0f3460; border: 1px solid var(--vt-color-border);
border-radius: 8px; border-radius: var(--vt-radius-lg);
padding: 1.25rem; padding: 1.25rem;
} }
.action-card h3 { .action-card h3 {
color: #e0e0f0; color: var(--vt-color-text-primary);
font-size: 1rem; font-size: 1rem;
margin: 0 0 0.25rem; margin: 0 0 0.25rem;
} }
.hint { .hint {
color: #a0a0b8; color: var(--vt-color-text-muted);
font-size: 0.8rem; font-size: 0.8rem;
margin: 0 0 0.75rem; margin: 0 0 0.75rem;
} }
@ -278,27 +278,28 @@
} }
.input-row input { .input-row input {
flex: 1; flex: 1;
background: #0f3460; background: #0f1424;
border: 1px solid #1a3a5c; border: 1px solid var(--vt-color-border-strong);
color: #e0e0f0; color: var(--vt-color-text-primary);
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
border-radius: 6px; border-radius: 6px;
font-size: 0.9rem; font-size: 0.9rem;
} }
.input-row input:focus { .input-row input:focus {
outline: none; outline: none;
border-color: #4ecca3; border-color: var(--vt-color-accent);
box-shadow: var(--vt-focus-ring);
} }
.input-row input::placeholder { .input-row input::placeholder {
color: #666; color: var(--vt-color-text-muted);
} }
.button-row { .button-row {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
} }
.btn-primary { .btn-primary {
background: #4ecca3; background: var(--vt-color-accent);
color: #1a1a2e; color: #101827;
border: none; border: none;
padding: 0.5rem 1.25rem; padding: 0.5rem 1.25rem;
border-radius: 6px; border-radius: 6px;
@ -314,9 +315,9 @@
cursor: not-allowed; cursor: not-allowed;
} }
.btn-secondary { .btn-secondary {
background: #0f3460; background: var(--vt-color-surface-hover);
color: #a0a0b8; color: var(--vt-color-text-secondary);
border: 1px solid #1a3a5c; border: 1px solid var(--vt-color-border-strong);
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
border-radius: 6px; border-radius: 6px;
cursor: pointer; cursor: pointer;
@ -324,21 +325,21 @@
white-space: nowrap; white-space: nowrap;
} }
.btn-secondary:hover:not(:disabled) { .btn-secondary:hover:not(:disabled) {
background: #1a3a5c; background: var(--vt-color-surface-hover);
color: #e0e0f0; color: var(--vt-color-text-primary);
} }
.btn-secondary:disabled { .btn-secondary:disabled {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
} }
.recent-section { .recent-section {
background: #16213e; background: var(--vt-color-surface);
border: 1px solid #0f3460; border: 1px solid var(--vt-color-border);
border-radius: 8px; border-radius: var(--vt-radius-lg);
padding: 1rem 1.25rem; padding: 1rem 1.25rem;
} }
.recent-section h3 { .recent-section h3 {
color: #a0a0b8; color: var(--vt-color-text-muted);
font-size: 0.8rem; font-size: 0.8rem;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.05em; letter-spacing: 0.05em;
@ -356,14 +357,14 @@
width: 100%; width: 100%;
background: none; background: none;
border: none; border: none;
color: #e0e0f0; color: var(--vt-color-text-primary);
padding: 0.4rem 0; padding: 0.4rem 0;
cursor: pointer; cursor: pointer;
text-align: left; text-align: left;
font-size: 0.85rem; font-size: 0.85rem;
} }
.recent-item:hover { .recent-item:hover {
color: #4ecca3; color: var(--vt-color-accent);
} }
.recent-item:disabled { .recent-item:disabled {
opacity: 0.5; opacity: 0.5;

View File

@ -47,10 +47,13 @@
{#if renderError} {#if renderError}
<div class="view-container"> <div class="view-container">
<div class="error-boundary"> <div class="error-boundary">
<div class="error-fallback"> <div class="error-fallback vt-inline-alert error">
<Icon name="warning" size={24} class="error-icon" /> <Icon name="warning" size={24} class="error-icon" />
<p class="error-title">Plugin UI failed</p> <p class="error-title">Plugin UI failed</p>
<p class="error-text">{renderError}</p> <details class="error-details">
<summary>Details</summary>
<p class="error-text">{renderError}</p>
</details>
</div> </div>
</div> </div>
</div> </div>
@ -69,11 +72,14 @@
/> />
{:else} {:else}
<div class="placeholder"> <div class="placeholder">
<p class="placeholder-label">Plugin View Host</p> <p class="placeholder-label">This plugin does not provide a visual view yet.</p>
<p class="placeholder-info"><span class="placeholder-key">Plugin:</span> <strong>{currentView.pluginId}</strong></p> <details class="placeholder-details">
<p class="placeholder-info"><span class="placeholder-key">View ID:</span> <code>{currentView.id}</code></p> <summary>Details</summary>
<p class="placeholder-info"><span class="placeholder-key">Component:</span> <code>{currentView.component}</code></p> <p class="placeholder-info"><span class="placeholder-key">Plugin:</span> <strong>{currentView.pluginId}</strong></p>
<p class="placeholder-badge">frontend bundle not available</p> <p class="placeholder-info"><span class="placeholder-key">View ID:</span> <code>{currentView.id}</code></p>
<p class="placeholder-info"><span class="placeholder-key">Component:</span> <code>{currentView.component}</code></p>
</details>
<p class="placeholder-badge vt-badge">Frontend bundle unavailable</p>
</div> </div>
{/if} {/if}
</div> </div>
@ -81,7 +87,7 @@
</div> </div>
{:else if activeView} {:else if activeView}
<div class="view-container empty"> <div class="view-container empty">
<p>View "{activeView}" not found in contributions</p> <p>View is unavailable.</p>
</div> </div>
{:else} {:else}
<div class="view-container empty"> <div class="view-container empty">
@ -97,7 +103,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-width: 0; min-width: 0;
background: #1a1a2e; background: var(--vt-color-background);
} }
.view { .view {
flex: 1; flex: 1;
@ -107,7 +113,7 @@
padding: 1.5rem; padding: 1.5rem;
} }
.view.degraded { .view.degraded {
border-left: 3px solid #ffc857; border-left: 3px solid var(--vt-color-warning);
} }
.view-header { .view-header {
display: flex; display: flex;
@ -115,18 +121,18 @@
gap: 0.5rem; gap: 0.5rem;
margin-bottom: 1rem; margin-bottom: 1rem;
padding-bottom: 0.75rem; padding-bottom: 0.75rem;
border-bottom: 1px solid #16213e; border-bottom: 1px solid var(--vt-color-border);
} }
.view-header h2 { .view-header h2 {
margin: 0; margin: 0;
font-size: 1.2rem; font-size: 1.2rem;
color: #e0e0f0; color: var(--vt-color-text-primary);
flex: 1; flex: 1;
} }
:global(.view-icon) { :global(.view-icon) {
width: 1.3rem; width: 1.3rem;
height: 1.3rem; height: 1.3rem;
color: #a78bfa; color: var(--vt-color-text-muted);
flex-shrink: 0; flex-shrink: 0;
} }
.view-content { .view-content {
@ -134,33 +140,33 @@
min-width: 0; min-width: 0;
} }
.placeholder { .placeholder {
color: #666; color: var(--vt-color-text-muted);
font-style: italic; font-style: italic;
padding: 2rem; padding: 2rem;
text-align: center; text-align: center;
border: 1px dashed #333; border: 1px dashed var(--vt-color-border-strong);
border-radius: 8px; border-radius: var(--vt-radius-lg);
} }
.placeholder-label { .placeholder-label {
font-size: 1rem; font-size: 1rem;
color: #a0a0b8; color: var(--vt-color-text-secondary);
font-weight: 600; font-weight: 600;
margin-bottom: 1rem; margin-bottom: 1rem;
font-style: normal; font-style: normal;
} }
.placeholder-info { .placeholder-info {
font-size: 0.85rem; font-size: 0.85rem;
color: #666; color: var(--vt-color-text-muted);
margin: 0.3rem 0; margin: 0.3rem 0;
font-style: normal; font-style: normal;
} }
.placeholder-key { .placeholder-key {
color: #a0a0b8; color: var(--vt-color-text-muted);
} }
.placeholder-info strong { color: #4ecca3; } .placeholder-info strong { color: var(--vt-color-accent); }
.placeholder-info code { .placeholder-info code {
color: #e0e0f0; color: var(--vt-color-text-primary);
background: #16213e; background: var(--vt-color-surface-muted);
padding: 0.1rem 0.3rem; padding: 0.1rem 0.3rem;
border-radius: 3px; border-radius: 3px;
font-size: 0.8rem; font-size: 0.8rem;
@ -169,8 +175,8 @@
display: inline-block; display: inline-block;
margin-top: 1rem; margin-top: 1rem;
padding: 0.25rem 0.75rem; padding: 0.25rem 0.75rem;
background: #533483; background: var(--vt-color-surface-muted);
color: #e0e0f0; color: var(--vt-color-text-secondary);
border-radius: 12px; border-radius: 12px;
font-size: 0.75rem; font-size: 0.75rem;
font-weight: 600; font-weight: 600;
@ -188,16 +194,16 @@
padding: 2rem; padding: 2rem;
} }
:global(.error-icon) { :global(.error-icon) {
color: #e94560; color: var(--vt-color-danger);
} }
.error-title { .error-title {
color: #e94560; color: var(--vt-color-danger);
font-size: 1.1rem; font-size: 1.1rem;
font-weight: 600; font-weight: 600;
margin: 0.5rem 0; margin: 0.5rem 0;
} }
.error-text { .error-text {
color: #a0a0b8; color: var(--vt-color-text-secondary);
font-size: 0.85rem; font-size: 0.85rem;
font-family: monospace; font-family: monospace;
margin-top: 0.5rem; margin-top: 0.5rem;
@ -208,9 +214,13 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100%; height: 100%;
color: #555; color: var(--vt-color-text-muted);
font-size: 1rem; font-size: 1rem;
text-align: center; text-align: center;
} }
.empty .sub { font-size: 0.85rem; color: #444; margin-top: 0.5rem; } .empty .sub { font-size: 0.85rem; color: var(--vt-color-text-muted); margin-top: 0.5rem; }
.placeholder-details,
.error-details { margin-top: 0.75rem; color: var(--vt-color-text-muted); }
.placeholder-details summary,
.error-details summary { cursor: pointer; color: var(--vt-color-text-secondary); }
</style> </style>

View File

@ -26,24 +26,24 @@
} }
</script> </script>
<div class="workbench-host"> <div class="workbench-host vt-page">
{#if openedResource?.status === 'no-provider'} {#if openedResource?.status === 'no-provider'}
<div class="workbench-header"> <div class="workbench-header vt-page-header">
<span class="workbench-title">{resourcePath}</span> <span class="workbench-title vt-page-title">{resourcePath}</span>
<span class="workbench-provider">no-provider</span> <span class="workbench-provider vt-badge">No provider</span>
<button class="close-btn" type="button" title="Close" aria-label="Close" on:click={closeWorkbench}> <button class="close-btn btn-ghost btn-icon" type="button" title="Close" aria-label="Close" on:click={closeWorkbench}>
<Icon name="x" size={18} /> <Icon name="x" size={18} />
</button> </button>
</div> </div>
<div class="workbench-empty no-provider" data-workbench-status="no-provider"> <div class="workbench-empty no-provider vt-empty-state" data-workbench-status="no-provider">
<p>No viewer/editor available</p> <p class="vt-empty-title">No viewer/editor available</p>
<p class="workbench-meta">{requestMode} · {requestContext}</p> <p class="workbench-meta">{requestMode} · {requestContext}</p>
</div> </div>
{:else if openedResource} {:else if openedResource}
<div class="workbench-header"> <div class="workbench-header vt-page-header">
<span class="workbench-title">{resourcePath}</span> <span class="workbench-title vt-page-title">{resourcePath}</span>
<span class="workbench-provider">{providerId}</span> <span class="workbench-provider vt-badge accent">{providerId}</span>
<button class="close-btn" type="button" title="Close" aria-label="Close" on:click={closeWorkbench}> <button class="close-btn btn-ghost btn-icon" type="button" title="Close" aria-label="Close" on:click={closeWorkbench}>
<Icon name="x" size={18} /> <Icon name="x" size={18} />
</button> </button>
</div> </div>
@ -57,8 +57,8 @@
{/key} {/key}
</div> </div>
{:else} {:else}
<div class="workbench-empty"> <div class="workbench-empty vt-empty-state">
<p>No resource opened</p> <p class="vt-empty-title">No resource opened</p>
</div> </div>
{/if} {/if}
</div> </div>
@ -71,20 +71,20 @@
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background: #1a1a2e; background: var(--vt-color-background);
} }
.workbench-header { .workbench-header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.75rem; gap: 0.75rem;
padding: 0.75rem 1rem; padding: var(--vt-space-2) var(--vt-space-4);
border-bottom: 1px solid #16213e; border-bottom: 1px solid var(--vt-color-border);
flex-shrink: 0; flex-shrink: 0;
} }
.workbench-title { .workbench-title {
color: #e0e0f0; color: var(--vt-color-text-primary);
font-size: 0.95rem; font-size: 0.95rem;
font-weight: 600; font-weight: 600;
min-width: 0; min-width: 0;
@ -98,21 +98,19 @@
height: 2rem; height: 2rem;
min-height: 0; min-height: 0;
padding: 0; padding: 0;
border: 1px solid #1a3a5c; border-radius: var(--vt-radius-md);
border-radius: 4px; color: var(--vt-color-text-secondary);
background: #0f3460;
color: #a0a0b8;
flex-shrink: 0; flex-shrink: 0;
cursor: pointer; cursor: pointer;
} }
.close-btn:hover { .close-btn:hover {
color: #e0e0f0; color: var(--vt-color-text-primary);
background: #1a4a7a; background: var(--vt-color-surface-hover);
} }
.workbench-provider { .workbench-provider {
color: #4ecca3; color: var(--vt-color-accent);
font-size: 0.75rem; font-size: 0.75rem;
margin-left: auto; margin-left: auto;
} }
@ -132,7 +130,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: #666; color: var(--vt-color-text-muted);
} }
.workbench-empty.no-provider { .workbench-empty.no-provider {
@ -142,7 +140,7 @@
.workbench-meta { .workbench-meta {
margin: 0; margin: 0;
color: #8b8ba8; color: var(--vt-color-text-muted);
font-size: 0.8rem; font-size: 0.8rem;
} }
</style> </style>

View File

@ -128,12 +128,12 @@
} }
</script> </script>
<div class="workspace-host"> <div class="workspace-host vt-page">
{#if selectedWorkspace} {#if selectedWorkspace}
<div class="workspace-header"> <div class="workspace-header vt-page-header">
<div class="workspace-title-group"> <div class="workspace-title-group">
<span class="workspace-title">{workspaceTitle}</span> <span class="workspace-title vt-page-title">{workspaceTitle}</span>
<span class="workspace-type">{workspaceType}</span> <span class="workspace-type vt-badge accent">{workspaceType}</span>
</div> </div>
<div class="workspace-search" aria-label="Workspace search"> <div class="workspace-search" aria-label="Workspace search">
<GlobalSearch /> <GlobalSearch />
@ -141,10 +141,11 @@
</div> </div>
{#if displayedTools.length > 0} {#if displayedTools.length > 0}
<div class="workspace-tabs" role="tablist" aria-label="Workspace tools"> <div class="workspace-tabs vt-tabbar" role="tablist" aria-label="Workspace tools">
{#each displayedTools as tool (tool.id + tool.pluginId)} {#each displayedTools as tool (tool.id + tool.pluginId)}
<button <button
class:active={toolKey(tool) === toolKey(activeTool)} class="vt-tab"
class:is-active={toolKey(tool) === toolKey(activeTool)}
role="tab" role="tab"
aria-selected={toolKey(tool) === toolKey(activeTool)} aria-selected={toolKey(tool) === toolKey(activeTool)}
type="button" type="button"
@ -174,15 +175,15 @@
{/if} {/if}
</div> </div>
{:else} {:else}
<div class="workspace-empty"> <div class="workspace-empty vt-empty-state">
<p>Для этого рабочего пространства пока нет инструментов</p> <p class="vt-empty-title">No workspace tools available</p>
<p class="workspace-hint">Включите плагины с workspace-инструментами или откройте Plugin Manager через меню настроек.</p> <p class="workspace-hint">Enable plugins with workspace tools or open Plugin Manager from settings.</p>
</div> </div>
{/if} {/if}
{:else} {:else}
<div class="workspace-empty"> <div class="workspace-empty vt-empty-state">
<p>Создайте рабочее пространство или выберите существующее в боковой панели</p> <p class="vt-empty-title">Select a workspace</p>
<p class="workspace-hint">Нажмите «+» в разделе Workspaces, чтобы добавить первый проект.</p> <p class="workspace-hint">Use the + button in Workspaces to add your first project.</p>
</div> </div>
{/if} {/if}
</div> </div>
@ -194,7 +195,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
background: #1a1a2e; background: var(--vt-color-background);
} }
.workspace-header { .workspace-header {
@ -202,8 +203,8 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 1rem; gap: 1rem;
padding: 0.65rem 1rem; padding: var(--vt-space-2) var(--vt-space-4);
border-bottom: 1px solid #16213e; border-bottom: 1px solid var(--vt-color-border);
flex-shrink: 0; flex-shrink: 0;
} }
@ -215,17 +216,17 @@
} }
.workspace-title { .workspace-title {
color: #e0e0f0; color: var(--vt-color-text-primary);
font-size: 0.95rem; font-size: 0.95rem;
font-weight: 600; font-weight: 600;
} }
.workspace-type { .workspace-type {
color: #4ecca3; color: var(--vt-color-accent);
font-size: 0.75rem; font-size: 0.75rem;
padding: 0.1rem 0.4rem; padding: 0.1rem 0.4rem;
border-radius: 3px; border-radius: var(--vt-radius-sm);
background: #1a2a3a; background: var(--vt-color-accent-muted);
} }
.workspace-search { .workspace-search {
@ -240,8 +241,8 @@
} }
.workspace-search :global(.global-search-box) { .workspace-search :global(.global-search-box) {
background: #101626; background: #0f1424;
border-color: #243957; border-color: var(--vt-color-border-strong);
} }
.workspace-search :global(.global-search-results) { .workspace-search :global(.global-search-results) {
@ -266,9 +267,9 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.25rem; gap: 0.25rem;
padding: 0.35rem 0.75rem 0; padding: var(--vt-space-1) var(--vt-space-3) 0;
background: #12122a; background: #12162a;
border-bottom: 1px solid #16213e; border-bottom: 1px solid var(--vt-color-border);
flex-shrink: 0; flex-shrink: 0;
overflow-x: auto; overflow-x: auto;
scrollbar-gutter: auto; scrollbar-gutter: auto;
@ -280,23 +281,23 @@
padding: 0.35rem 0.8rem; padding: 0.35rem 0.8rem;
border: 1px solid transparent; border: 1px solid transparent;
border-bottom: none; border-bottom: none;
border-radius: 6px 6px 0 0; border-radius: var(--vt-radius-md) var(--vt-radius-md) 0 0;
background: transparent; background: transparent;
color: #8b8ba8; color: var(--vt-color-text-muted);
cursor: pointer; cursor: pointer;
font: inherit; font: inherit;
font-size: 0.8rem; font-size: 0.8rem;
} }
.workspace-tabs button:hover { .workspace-tabs button:hover {
color: #e0e0f0; color: var(--vt-color-text-primary);
background: rgba(15, 52, 96, 0.4); background: var(--vt-color-surface-hover);
} }
.workspace-tabs button.active { .workspace-tabs button.is-active {
color: #4ecca3; color: var(--vt-color-accent);
background: #1a1a2e; background: var(--vt-color-background);
border-color: #16213e; border-color: var(--vt-color-border);
} }
.workspace-tool-content { .workspace-tool-content {
@ -311,7 +312,7 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: #666; color: var(--vt-color-text-muted);
gap: 0.5rem; gap: 0.5rem;
padding: 2rem; padding: 2rem;
text-align: center; text-align: center;
@ -319,7 +320,7 @@
.workspace-hint { .workspace-hint {
font-size: 0.8rem; font-size: 0.8rem;
color: #555; color: var(--vt-color-text-muted);
max-width: 300px; max-width: 300px;
text-align: center; text-align: center;
} }

View File

@ -192,7 +192,7 @@
<div class="wt-list"> <div class="wt-list">
{#each workspaces as workspace (wsName(workspace))} {#each workspaces as workspace (wsName(workspace))}
{@const id = wsName(workspace)} {@const id = wsName(workspace)}
<div class="wt-node" class:selected={id === $activeWorkspaceId}> <div class="wt-node vt-list-row" class:selected={id === $activeWorkspaceId}>
<div class="wt-row"> <div class="wt-row">
<span class="wt-icon"><Icon name="space" size={13} class="wt-node-icon" /></span> <span class="wt-icon"><Icon name="space" size={13} class="wt-node-icon" /></span>
{#if renamingId === id} {#if renamingId === id}
@ -206,7 +206,7 @@
}} }}
/> />
<button class="wt-btn wt-btn-small wt-always" on:click={() => commitRename(workspace)} title="Save rename" type="button" disabled={busyId === id}>OK</button> <button class="wt-btn wt-btn-small wt-always" on:click={() => commitRename(workspace)} title="Save rename" type="button" disabled={busyId === id}>OK</button>
<button class="wt-btn wt-btn-small wt-always" on:click={cancelRename} title="Cancel rename" type="button" disabled={busyId === id}>x</button> <button class="wt-btn wt-btn-small wt-always" on:click={cancelRename} title="Cancel rename" type="button" disabled={busyId === id}>Cancel</button>
{:else} {:else}
<button class="wt-label" on:click={() => selectWorkspace(workspace)} type="button">{id}</button> <button class="wt-label" on:click={() => selectWorkspace(workspace)} type="button">{id}</button>
<button class="wt-icon-btn" on:click={() => startRename(workspace)} title="Rename workspace" type="button" disabled={busyId === id}> <button class="wt-icon-btn" on:click={() => startRename(workspace)} title="Rename workspace" type="button" disabled={busyId === id}>
@ -225,7 +225,7 @@
<div class="wt-create"> <div class="wt-create">
<div class="wt-create-header"> <div class="wt-create-header">
<span>New workspace</span> <span>New workspace</span>
<button class="wt-btn" on:click={() => { showCreate = false; newWorkspaceName = ''; }} type="button">x</button> <button class="wt-btn btn-ghost" on:click={() => { showCreate = false; newWorkspaceName = ''; }} type="button">Close</button>
</div> </div>
<input type="text" bind:value={newWorkspaceName} placeholder="Name..." disabled={creating} /> <input type="text" bind:value={newWorkspaceName} placeholder="Name..." disabled={creating} />
<div class="wt-create-actions"> <div class="wt-create-actions">
@ -238,35 +238,35 @@
<style> <style>
.wt { display: flex; flex-direction: column; flex: 1; overflow: hidden; position: relative; } .wt { display: flex; flex-direction: column; flex: 1; overflow: hidden; position: relative; }
.wt-header { display: flex; align-items: center; justify-content: space-between; padding: 0.7rem 0.6rem 0.35rem; border-bottom: 1px solid #0f3460; flex-shrink: 0; } .wt-header { display: flex; align-items: center; justify-content: space-between; padding: 0.7rem 0.6rem 0.35rem; border-bottom: 1px solid var(--vt-color-border); flex-shrink: 0; }
.wt-title { color: #a0a0b8; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; } .wt-title { color: var(--vt-color-text-muted); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; }
.wt-list { min-height: 0; overflow-y: auto; padding: 0.2rem 0.6rem; } .wt-list { min-height: 0; overflow-y: auto; padding: 0.2rem 0.6rem; }
.wt-btn { min-height: 0; background: none; border: none; color: #666; cursor: pointer; font-size: 0.85rem; padding: 0.1rem 0.3rem; border-radius: 3px; } .wt-btn { min-height: 1.55rem; background: transparent; border: 1px solid transparent; color: var(--vt-color-text-muted); cursor: pointer; font-size: 0.78rem; padding: 0.12rem 0.38rem; border-radius: var(--vt-radius-sm); }
.wt-btn:hover:not(:disabled) { color: #4ecca3; background: rgba(78,204,163,0.1); } .wt-btn:hover:not(:disabled) { color: var(--vt-color-accent); background: var(--vt-color-accent-muted); border-color: rgba(78,204,163,0.25); }
.wt-btn-small { font-size: 0.68rem; opacity: 0; } .wt-btn-small { font-size: 0.7rem; opacity: 0; }
.wt-always { opacity: 1; } .wt-always { opacity: 1; }
.wt-row:hover .wt-btn-small { opacity: 1; } .wt-row:hover .wt-btn-small { opacity: 1; }
.wt-loading, .wt-error { padding: 0.5rem; font-size: 0.75rem; color: #666; } .wt-loading, .wt-error { padding: 0.5rem; font-size: 0.75rem; color: var(--vt-color-text-muted); }
.wt-error { color: #e94560; } .wt-error { color: var(--vt-color-danger); }
.wt-row { display: flex; align-items: center; gap: 0.45rem; padding: 0.15rem 0.45rem; min-height: 1.7rem; border-radius: 3px; } .wt-row { display: flex; align-items: center; gap: 0.45rem; padding: 0.18rem 0.45rem; min-height: 1.85rem; border-radius: var(--vt-radius-sm); }
.wt-row:hover { background: rgba(15,52,96,0.4); } .wt-row:hover { background: var(--vt-color-surface-hover); }
.wt-node.selected > .wt-row { background: rgba(78,204,163,0.1); } .wt-node.selected > .wt-row { background: var(--vt-color-surface-selected); box-shadow: inset 2px 0 0 var(--vt-color-accent); }
.wt-icon { width: 0.9rem; height: 0.9rem; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; color: #a0a0b8; } .wt-icon { width: 0.95rem; height: 0.95rem; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--vt-color-text-muted); }
:global(.wt-node-icon) { display: block; } :global(.wt-node-icon) { display: block; }
.wt-label { flex: 1; min-width: 0; min-height: 0; justify-content: flex-start; background: none; border: none; color: #a0a0b8; font-size: 0.78rem; text-align: left; cursor: pointer; padding: 0.1rem 0; border-radius: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .wt-label { flex: 1; min-width: 0; min-height: 0; justify-content: flex-start; background: none; border: none; color: var(--vt-color-text-secondary); font-size: 0.78rem; text-align: left; cursor: pointer; padding: 0.1rem 0; border-radius: var(--vt-radius-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wt-label:hover { color: #4ecca3; } .wt-label:hover { color: var(--vt-color-accent); }
.wt-icon-btn { width: 1.25rem; height: 1.25rem; min-height: 0; padding: 0; border: none; background: transparent; color: #666; opacity: 0; flex-shrink: 0; cursor: pointer; border-radius: 3px; } .wt-icon-btn { width: 1.45rem; height: 1.45rem; min-height: 0; padding: 0; border: 1px solid transparent; background: transparent; color: var(--vt-color-text-muted); opacity: 0.75; flex-shrink: 0; cursor: pointer; border-radius: var(--vt-radius-sm); }
.wt-row:hover .wt-icon-btn { opacity: 1; } .wt-row:hover .wt-icon-btn { opacity: 1; }
.wt-icon-btn:hover:not(:disabled) { color: #4ecca3; background: rgba(78,204,163,0.1); } .wt-icon-btn:hover:not(:disabled) { color: var(--vt-color-accent); background: var(--vt-color-accent-muted); border-color: rgba(78,204,163,0.25); }
.wt-icon-btn.danger:hover:not(:disabled) { color: #e94560; background: rgba(233,69,96,0.12); } .wt-icon-btn.danger:hover:not(:disabled) { color: var(--vt-color-danger); background: var(--vt-color-danger-muted); border-color: rgba(233,69,96,0.35); }
.wt-rename { flex: 1; min-width: 0; background: #0f3460; border: 1px solid #1a3a5c; color: #e0e0f0; padding: 0.2rem 0.35rem; border-radius: 4px; font-size: 0.78rem; } .wt-rename { flex: 1; min-width: 0; background: #0f1424; border: 1px solid var(--vt-color-border-strong); color: var(--vt-color-text-primary); padding: 0.2rem 0.35rem; border-radius: var(--vt-radius-sm); font-size: 0.78rem; }
.wt-rename:focus { outline: none; border-color: #4ecca3; } .wt-rename:focus { outline: none; border-color: var(--vt-color-accent); box-shadow: var(--vt-focus-ring); }
.wt-create { position: absolute; bottom: 0; left: 0; right: 0; background: #16213e; border-top: 1px solid #0f3460; padding: 0.6rem; z-index: 10; } .wt-create { position: absolute; bottom: 0; left: 0; right: 0; background: var(--vt-color-surface-muted); border-top: 1px solid var(--vt-color-border); padding: 0.6rem; z-index: 10; }
.wt-create-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.4rem; color: #a0a0b8; font-size: 0.7rem; text-transform: uppercase; } .wt-create-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.4rem; color: var(--vt-color-text-muted); font-size: 0.7rem; text-transform: uppercase; }
.wt-create input { width: 100%; background: #0f3460; border: 1px solid #1a3a5c; color: #e0e0f0; padding: 0.35rem 0.5rem; border-radius: 4px; font-size: 0.8rem; margin-bottom: 0.4rem; box-sizing: border-box; } .wt-create input { width: 100%; background: #0f1424; border: 1px solid var(--vt-color-border-strong); color: var(--vt-color-text-primary); padding: 0.35rem 0.5rem; border-radius: var(--vt-radius-sm); font-size: 0.8rem; margin-bottom: 0.4rem; box-sizing: border-box; }
.wt-create input:focus { outline: none; border-color: #4ecca3; } .wt-create input:focus { outline: none; border-color: var(--vt-color-accent); box-shadow: var(--vt-focus-ring); }
.wt-create-actions { display: flex; gap: 0.4rem; justify-content: flex-end; } .wt-create-actions { display: flex; gap: 0.4rem; justify-content: flex-end; }
.wt-btn-primary { background: #4ecca3; color: #1a1a2e; border: none; padding: 0.3rem 0.6rem; border-radius: 4px; cursor: pointer; font-size: 0.75rem; font-weight: 600; } .wt-btn-primary { background: var(--vt-color-accent); color: #101827; border: none; padding: 0.3rem 0.6rem; border-radius: var(--vt-radius-sm); cursor: pointer; font-size: 0.75rem; font-weight: 600; }
.wt-btn-primary:hover:not(:disabled) { background: #3dbb92; } .wt-btn-primary:hover:not(:disabled) { background: #3dbb92; }
.wt-btn-primary:disabled, .wt-btn:disabled, .wt-icon-btn:disabled { opacity: 0.5; cursor: not-allowed; } .wt-btn-primary:disabled, .wt-btn:disabled, .wt-icon-btn:disabled { opacity: 0.5; cursor: not-allowed; }
</style> </style>

View File

@ -1384,6 +1384,13 @@
function activityBundle() { function activityBundle() {
return '(' + function () { return '(' + function () {
var PLUGIN_ID = 'verstak.activity'; var PLUGIN_ID = 'verstak.activity';
var LOW_VALUE_EVENT_TYPES = {
'workspace.selected': true,
'case.selected': true,
'file.selected': true,
'file.opened': true,
'note.opened': true
};
function injectStyles() { function injectStyles() {
if (document.getElementById('mock-activity-style')) return; if (document.getElementById('mock-activity-style')) return;
@ -1435,6 +1442,12 @@
return Array.isArray(value) ? value.filter(function (item) { return item && typeof item === 'object'; }) : []; return Array.isArray(value) ? value.filter(function (item) { return item && typeof item === 'object'; }) : [];
} }
function meaningfulRows(value) {
return rows(value).filter(function (activity) {
return !LOW_VALUE_EVENT_TYPES[String(activity.type || '').toLowerCase()];
});
}
function eventTime(value) { function eventTime(value) {
var date = new Date(value || 0); var date = new Date(value || 0);
return isNaN(date.getTime()) ? 0 : date.getTime(); return isNaN(date.getTime()) ? 0 : date.getTime();
@ -1576,7 +1589,7 @@
} }
readSettings().then(function (settings) { readSettings().then(function (settings) {
events = rows(settings[key]); events = meaningfulRows(settings[key]);
render(); render();
}); });
render(); render();