UI: remove dead control UI modules

This commit is contained in:
Vincent Koc 2026-03-17 23:52:32 -07:00
parent 0385553918
commit 5eea523f39
10 changed files with 6 additions and 384 deletions

18
pnpm-lock.yaml generated
View File

@ -625,12 +625,6 @@ importers:
ui:
dependencies:
'@lit-labs/signals':
specifier: ^0.2.0
version: 0.2.0
'@lit/context':
specifier: ^1.1.6
version: 1.1.6
'@noble/ed25519':
specifier: 3.0.1
version: 3.0.1
@ -643,15 +637,6 @@ importers:
marked:
specifier: ^17.0.4
version: 17.0.4
signal-polyfill:
specifier: ^0.2.2
version: 0.2.2
signal-utils:
specifier: ^0.21.1
version: 0.21.1(signal-polyfill@0.2.2)
vite:
specifier: 8.0.0
version: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)
devDependencies:
'@vitest/browser-playwright':
specifier: 4.1.0
@ -662,6 +647,9 @@ importers:
playwright:
specifier: ^1.58.2
version: 1.58.2
vite:
specifier: 8.0.0
version: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)
vitest:
specifier: 4.1.0
version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(@vitest/browser-playwright@4.1.0)(jsdom@29.0.0(@noble/hashes@2.0.1))(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))

View File

@ -9,20 +9,16 @@
"test": "vitest run --config vitest.config.ts"
},
"dependencies": {
"@lit-labs/signals": "^0.2.0",
"@lit/context": "^1.1.6",
"@noble/ed25519": "3.0.1",
"dompurify": "^3.3.3",
"lit": "^3.3.2",
"marked": "^17.0.4",
"signal-polyfill": "^0.2.2",
"signal-utils": "^0.21.1",
"vite": "8.0.0"
"marked": "^17.0.4"
},
"devDependencies": {
"@vitest/browser-playwright": "4.1.0",
"jsdom": "^29.0.0",
"playwright": "^1.58.2",
"vite": "8.0.0",
"vitest": "4.1.0"
}
}

View File

@ -249,6 +249,7 @@
.topnav-shell__content {
display: none;
width: 100%;
}
.topbar-nav-toggle {
@ -650,75 +651,3 @@
font-size: 12px;
}
}
/* ===========================================
Bottom Tabs (mobile navigation bar)
=========================================== */
.bottom-tabs {
display: none;
}
@media (max-width: 768px) {
.bottom-tabs {
display: flex;
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 60;
background: var(--bg);
border-top: 1px solid var(--border);
padding: 4px 0 calc(4px + env(safe-area-inset-bottom, 0px));
justify-content: space-around;
align-items: stretch;
}
.bottom-tab {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
flex: 1;
padding: 6px 4px;
border: none;
background: none;
color: var(--muted);
font-size: 10px;
cursor: pointer;
transition:
color var(--duration-fast) ease,
opacity var(--duration-fast) ease;
}
.bottom-tab__icon {
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
}
.bottom-tab__icon svg {
width: 20px;
height: 20px;
stroke: currentColor;
fill: none;
stroke-width: 1.5px;
stroke-linecap: round;
stroke-linejoin: round;
}
.bottom-tab__label {
font-weight: 500;
letter-spacing: 0.01em;
}
.bottom-tab--active {
color: var(--accent);
}
.bottom-tab:active {
opacity: 0.7;
}
}

View File

@ -1 +0,0 @@
export { exportChatMarkdown } from "./chat/export.ts";

View File

@ -1,45 +0,0 @@
export const MOONSHOT_KIMI_K2_DEFAULT_ID = "kimi-k2.5";
export const MOONSHOT_KIMI_K2_CONTEXT_WINDOW = 256000;
export const MOONSHOT_KIMI_K2_MAX_TOKENS = 8192;
export const MOONSHOT_KIMI_K2_INPUT = ["text"] as const;
export const MOONSHOT_KIMI_K2_COST = {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
} as const;
export const MOONSHOT_KIMI_K2_MODELS = [
{
id: "kimi-k2.5",
name: "Kimi K2.5",
alias: "Kimi K2.5",
reasoning: false,
},
{
id: "kimi-k2-0905-preview",
name: "Kimi K2 0905 Preview",
alias: "Kimi K2",
reasoning: false,
},
{
id: "kimi-k2-turbo-preview",
name: "Kimi K2 Turbo",
alias: "Kimi K2 Turbo",
reasoning: false,
},
{
id: "kimi-k2-thinking",
name: "Kimi K2 Thinking",
alias: "Kimi K2 Thinking",
reasoning: true,
},
{
id: "kimi-k2-thinking-turbo",
name: "Kimi K2 Thinking Turbo",
alias: "Kimi K2 Thinking Turbo",
reasoning: true,
},
] as const;
export type MoonshotKimiK2Model = (typeof MOONSHOT_KIMI_K2_MODELS)[number];

View File

@ -1,39 +0,0 @@
/**
* Map raw tool names to human-friendly labels for the chat UI.
* Unknown tools are title-cased with underscores replaced by spaces.
*/
export const TOOL_LABELS: Record<string, string> = {
exec: "Run Command",
bash: "Run Command",
read: "Read File",
write: "Write File",
edit: "Edit File",
apply_patch: "Apply Patch",
web_search: "Web Search",
web_fetch: "Fetch Page",
browser: "Browser",
message: "Send Message",
image: "Generate Image",
canvas: "Canvas",
cron: "Cron",
gateway: "Gateway",
nodes: "Nodes",
memory_search: "Search Memory",
memory_get: "Get Memory",
session_status: "Session Status",
sessions_list: "List Sessions",
sessions_history: "Session History",
sessions_send: "Send to Session",
sessions_spawn: "Spawn Session",
agents_list: "List Agents",
};
export function friendlyToolName(raw: string): string {
const mapped = TOOL_LABELS[raw];
if (mapped) {
return mapped;
}
// Title-case fallback: "some_tool_name" → "Some Tool Name"
return raw.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
}

View File

@ -1,33 +0,0 @@
import { html } from "lit";
import { icons } from "../icons.ts";
import type { Tab } from "../navigation.ts";
export type BottomTabsProps = {
activeTab: Tab;
onTabChange: (tab: Tab) => void;
};
const BOTTOM_TABS: Array<{ id: Tab; label: string; icon: keyof typeof icons }> = [
{ id: "overview", label: "Dashboard", icon: "barChart" },
{ id: "chat", label: "Chat", icon: "messageSquare" },
{ id: "sessions", label: "Sessions", icon: "fileText" },
{ id: "config", label: "Settings", icon: "settings" },
];
export function renderBottomTabs(props: BottomTabsProps) {
return html`
<nav class="bottom-tabs">
${BOTTOM_TABS.map(
(tab) => html`
<button
class="bottom-tab ${props.activeTab === tab.id ? "bottom-tab--active" : ""}"
@click=${() => props.onTabChange(tab.id)}
>
<span class="bottom-tab__icon">${icons[tab.icon]}</span>
<span class="bottom-tab__label">${tab.label}</span>
</button>
`,
)}
</nav>
`;
}

View File

@ -1,50 +0,0 @@
import { describe, expect, it } from "vitest";
import {
appendTagFilter,
getTagFilters,
hasTagFilter,
removeTagFilter,
replaceTagFilters,
toggleTagFilter,
} from "./config-search.ts";
describe("config search tag helper", () => {
it("adds a tag when query is empty", () => {
expect(appendTagFilter("", "security")).toBe("tag:security");
});
it("appends a tag to existing text query", () => {
expect(appendTagFilter("token", "security")).toBe("token tag:security");
});
it("deduplicates existing tag filters case-insensitively", () => {
expect(appendTagFilter("token tag:Security", "security")).toBe("token tag:Security");
});
it("detects exact tag terms", () => {
expect(hasTagFilter("tag:security token", "security")).toBe(true);
expect(hasTagFilter("tag:security-hard token", "security")).toBe(false);
});
it("removes only the selected active tag", () => {
expect(removeTagFilter("token tag:security tag:auth", "security")).toBe("token tag:auth");
});
it("toggle removes active tag and keeps text", () => {
expect(toggleTagFilter("token tag:security", "security")).toBe("token");
});
it("toggle adds missing tag", () => {
expect(toggleTagFilter("token", "channels")).toBe("token tag:channels");
});
it("extracts unique normalized tags from query", () => {
expect(getTagFilters("token tag:Security tag:auth tag:security")).toEqual(["security", "auth"]);
});
it("replaces only tag filters and preserves free text", () => {
expect(replaceTagFilters("token tag:security mode", ["auth", "channels"])).toBe(
"token mode tag:auth tag:channels",
);
});
});

View File

@ -1,92 +0,0 @@
function escapeRegExp(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function normalizeTag(tag: string): string {
return tag.trim().toLowerCase();
}
export function getTagFilters(query: string): string[] {
const seen = new Set<string>();
const tags: string[] = [];
const pattern = /(^|\s)tag:([^\s]+)/gi;
const raw = query.trim();
let match: RegExpExecArray | null = pattern.exec(raw);
while (match) {
const normalized = normalizeTag(match[2] ?? "");
if (normalized && !seen.has(normalized)) {
seen.add(normalized);
tags.push(normalized);
}
match = pattern.exec(raw);
}
return tags;
}
export function hasTagFilter(query: string, tag: string): boolean {
const normalizedTag = normalizeTag(tag);
if (!normalizedTag) {
return false;
}
const pattern = new RegExp(`(^|\\s)tag:${escapeRegExp(normalizedTag)}(?=\\s|$)`, "i");
return pattern.test(query.trim());
}
export function appendTagFilter(query: string, tag: string): string {
const normalizedTag = normalizeTag(tag);
const trimmed = query.trim();
if (!normalizedTag) {
return trimmed;
}
if (!trimmed) {
return `tag:${normalizedTag}`;
}
if (hasTagFilter(trimmed, normalizedTag)) {
return trimmed;
}
return `${trimmed} tag:${normalizedTag}`;
}
export function removeTagFilter(query: string, tag: string): string {
const normalizedTag = normalizeTag(tag);
const trimmed = query.trim();
if (!normalizedTag || !trimmed) {
return trimmed;
}
const pattern = new RegExp(`(^|\\s)tag:${escapeRegExp(normalizedTag)}(?=\\s|$)`, "ig");
return trimmed.replace(pattern, " ").replace(/\s+/g, " ").trim();
}
export function replaceTagFilters(query: string, tags: readonly string[]): string {
const uniqueTags: string[] = [];
const seen = new Set<string>();
for (const tag of tags) {
const normalized = normalizeTag(tag);
if (!normalized || seen.has(normalized)) {
continue;
}
seen.add(normalized);
uniqueTags.push(normalized);
}
const trimmed = query.trim();
const withoutTags = trimmed
.replace(/(^|\s)tag:([^\s]+)/gi, " ")
.replace(/\s+/g, " ")
.trim();
const tagTokens = uniqueTags.map((tag) => `tag:${tag}`).join(" ");
if (withoutTags && tagTokens) {
return `${withoutTags} ${tagTokens}`;
}
if (withoutTags) {
return withoutTags;
}
return tagTokens;
}
export function toggleTagFilter(query: string, tag: string): string {
if (hasTagFilter(query, tag)) {
return removeTagFilter(query, tag);
}
return appendTagFilter(query, tag);
}

View File

@ -1,31 +0,0 @@
import { html } from "lit";
import { t } from "../../i18n/index.ts";
import { icons } from "../icons.ts";
export type OverviewQuickActionsProps = {
onNavigate: (tab: string) => void;
onRefresh: () => void;
};
export function renderOverviewQuickActions(props: OverviewQuickActionsProps) {
return html`
<section class="ov-quick-actions">
<button class="btn ov-quick-action-btn" @click=${() => props.onNavigate("chat")}>
<span class="nav-item__icon">${icons.messageSquare}</span>
${t("overview.quickActions.newSession")}
</button>
<button class="btn ov-quick-action-btn" @click=${() => props.onNavigate("cron")}>
<span class="nav-item__icon">${icons.zap}</span>
${t("overview.quickActions.automation")}
</button>
<button class="btn ov-quick-action-btn" @click=${() => props.onRefresh()}>
<span class="nav-item__icon">${icons.loader}</span>
${t("overview.quickActions.refreshAll")}
</button>
<button class="btn ov-quick-action-btn" @click=${() => props.onNavigate("sessions")}>
<span class="nav-item__icon">${icons.monitor}</span>
${t("overview.quickActions.terminal")}
</button>
</section>
`;
}