mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-21 05:52:35 +08:00
feat: Omni Search
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { IconButton } from "@kilocode/kilo-web-ui/icon-button"
|
||||
import { OmniSearch } from "./OmniSearch"
|
||||
|
||||
export function AppHeader() {
|
||||
return (
|
||||
@@ -13,10 +14,7 @@ export function AppHeader() {
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<form class="omni-search" role="search">
|
||||
<span aria-hidden="true">⌘K</span>
|
||||
<input type="search" placeholder="Search projects, config, providers..." aria-label="Omni search" />
|
||||
</form>
|
||||
<OmniSearch />
|
||||
|
||||
<nav class="notification-zone" aria-label="Notifications and status">
|
||||
<IconButton icon="bubble-5" variant="ghost" aria-label="Notifications" />
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
import { useLocation, useNavigate } from "@solidjs/router"
|
||||
import { createEffect, createMemo, createResource, createSignal, For, onCleanup, onMount, Show } from "solid-js"
|
||||
import {
|
||||
discover,
|
||||
forgetCached,
|
||||
healthy,
|
||||
loadCached,
|
||||
loadVisibleProjects,
|
||||
saveCached,
|
||||
type ProjectItem,
|
||||
type ProjectQuery,
|
||||
} from "../../client"
|
||||
import { configNav } from "../../routes/config/sections"
|
||||
import { clean, friendly } from "../../shared/utils"
|
||||
|
||||
type Entry = {
|
||||
kind: "NAV" | "PROJECT"
|
||||
label: string
|
||||
href: string
|
||||
sub?: string
|
||||
mono?: boolean
|
||||
}
|
||||
|
||||
const ports = new Set(["3017", "3018"])
|
||||
|
||||
function shouldDiscover(input: URLSearchParams) {
|
||||
if (input.get("server")) return false
|
||||
return ports.has(window.location.port)
|
||||
}
|
||||
|
||||
function base(input: URLSearchParams) {
|
||||
const param = input.get("server")
|
||||
if (param) return param
|
||||
const cached = shouldDiscover(input) ? loadCached() : ""
|
||||
if (cached) return cached
|
||||
if (shouldDiscover(input)) return ""
|
||||
return window.location.origin
|
||||
}
|
||||
|
||||
function tail(input: URLSearchParams) {
|
||||
const next = new URLSearchParams(input)
|
||||
next.delete("directory")
|
||||
const query = next.toString()
|
||||
return query ? `?${query}` : ""
|
||||
}
|
||||
|
||||
function link(path: string, input: URLSearchParams) {
|
||||
return `${path}${tail(input)}`
|
||||
}
|
||||
|
||||
function repo(input: string) {
|
||||
const parts = input.split(/[\\/]/).filter(Boolean)
|
||||
return parts.at(-1) ?? "Global"
|
||||
}
|
||||
|
||||
function name(item: ProjectItem) {
|
||||
return friendly(item.name?.trim() || repo(item.worktree) || item.id.slice(0, 8))
|
||||
}
|
||||
|
||||
function short(input: string) {
|
||||
if (input.length <= 8) return input
|
||||
return input.slice(0, 8)
|
||||
}
|
||||
|
||||
function nav(input: URLSearchParams): Entry[] {
|
||||
const rows: Entry[] = [{ kind: "NAV", label: "Projects", href: link("/projects", input) }]
|
||||
|
||||
for (const item of configNav) {
|
||||
if ("items" in item) {
|
||||
for (const child of item.items) {
|
||||
const prefix = item.id === "general" ? "Global Settings" : `Global Settings · ${item.label}`
|
||||
rows.push({ kind: "NAV", label: `${prefix} · ${child.label}`, href: link(child.href, input) })
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
rows.push({ kind: "NAV", label: `Global Settings · ${item.label}`, href: link(item.href, input) })
|
||||
}
|
||||
|
||||
rows.push({ kind: "NAV", label: "Profile", href: link("/profile", input) })
|
||||
return rows
|
||||
}
|
||||
|
||||
function project(item: ProjectItem, input: URLSearchParams): Entry {
|
||||
return {
|
||||
kind: "PROJECT",
|
||||
label: name(item),
|
||||
href: link(`/projects/${encodeURIComponent(item.id)}`, input),
|
||||
sub: short(item.id),
|
||||
mono: true,
|
||||
}
|
||||
}
|
||||
|
||||
function fuzzy(hay: string, term: string) {
|
||||
return [...term].reduce((pos, char) => {
|
||||
if (pos < 0) return -1
|
||||
const hit = hay.indexOf(char, pos)
|
||||
if (hit < 0) return -1
|
||||
return hit + 1
|
||||
}, 0) >= 0
|
||||
}
|
||||
|
||||
function matches(item: Entry, input: string) {
|
||||
const term = input.trim().toLowerCase()
|
||||
if (!term) return true
|
||||
const hay = `${item.kind} ${item.label} ${item.sub ?? ""}`.toLowerCase()
|
||||
if (hay.includes(term)) return true
|
||||
return fuzzy(hay, term)
|
||||
}
|
||||
|
||||
export function OmniSearch() {
|
||||
const loc = useLocation()
|
||||
const go = useNavigate()
|
||||
const params = createMemo(() => new URLSearchParams(loc.search))
|
||||
const discoverable = () => shouldDiscover(params())
|
||||
const fallback = () => base(params())
|
||||
const [url, setUrl] = createSignal(fallback())
|
||||
const [open, setOpen] = createSignal(false)
|
||||
const [term, setTerm] = createSignal("")
|
||||
const [active, setActive] = createSignal(0)
|
||||
let box: HTMLFormElement | undefined
|
||||
let field: HTMLInputElement | undefined
|
||||
|
||||
const query = createMemo<ProjectQuery | undefined>(() => {
|
||||
const target = clean(url()) || fallback()
|
||||
if (!target) return undefined
|
||||
return { url: target, dir: "" }
|
||||
})
|
||||
const [items] = createResource(query, loadVisibleProjects)
|
||||
const entries = createMemo(() => [...nav(params()), ...[...(items() ?? [])].map((item) => project(item, params()))])
|
||||
const filtered = createMemo(() => entries().filter((item) => matches(item, term())))
|
||||
const server = createMemo(() => query()?.url ?? fallback())
|
||||
|
||||
function close() {
|
||||
setOpen(false)
|
||||
setTerm("")
|
||||
field?.blur()
|
||||
}
|
||||
|
||||
function focus() {
|
||||
setOpen(true)
|
||||
queueMicrotask(() => field?.focus())
|
||||
}
|
||||
|
||||
function select(item: Entry | undefined) {
|
||||
if (!item) return
|
||||
go(item.href)
|
||||
close()
|
||||
}
|
||||
|
||||
function move(delta: number) {
|
||||
const max = Math.max(filtered().length - 1, 0)
|
||||
setActive((value) => Math.min(Math.max(value + delta, 0), max))
|
||||
}
|
||||
|
||||
function keys(event: KeyboardEvent) {
|
||||
if (event.key === "Escape" && open()) {
|
||||
event.preventDefault()
|
||||
close()
|
||||
return
|
||||
}
|
||||
|
||||
if (!open()) return
|
||||
if (event.key === "ArrowDown") {
|
||||
event.preventDefault()
|
||||
move(1)
|
||||
return
|
||||
}
|
||||
if (event.key === "ArrowUp") {
|
||||
event.preventDefault()
|
||||
move(-1)
|
||||
return
|
||||
}
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault()
|
||||
select(filtered()[active()])
|
||||
}
|
||||
}
|
||||
|
||||
function submit(event: SubmitEvent) {
|
||||
event.preventDefault()
|
||||
if (!open()) return
|
||||
select(filtered()[active()])
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
term()
|
||||
setActive(0)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const next = params().get("server")
|
||||
if (next && next !== url()) setUrl(next)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (!discoverable()) return
|
||||
const cached = loadCached()
|
||||
void Promise.resolve(cached ? healthy(cached) : false)
|
||||
.then((ok) => {
|
||||
if (ok) return cached
|
||||
forgetCached()
|
||||
return discover()
|
||||
})
|
||||
.then((value) => {
|
||||
if (!value) return
|
||||
saveCached(value)
|
||||
setUrl(value)
|
||||
})
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const current = query()
|
||||
if (!items() || !current || !discoverable()) return
|
||||
saveCached(current.url)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (!items.error || !discoverable()) return
|
||||
const cached = loadCached()
|
||||
if (!cached || cached !== url()) return
|
||||
forgetCached()
|
||||
setUrl("")
|
||||
void discover().then((value) => {
|
||||
if (!value) return
|
||||
saveCached(value)
|
||||
setUrl(value)
|
||||
})
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
const combo = (event: KeyboardEvent) => {
|
||||
if (!(event.metaKey || event.ctrlKey) || event.key.toLowerCase() !== "k") return
|
||||
event.preventDefault()
|
||||
if (open()) {
|
||||
close()
|
||||
return
|
||||
}
|
||||
focus()
|
||||
}
|
||||
const pointer = (event: PointerEvent) => {
|
||||
if (!open()) return
|
||||
const target = event.target
|
||||
if (target instanceof Node && box?.contains(target)) return
|
||||
close()
|
||||
}
|
||||
|
||||
window.addEventListener("keydown", combo)
|
||||
window.addEventListener("pointerdown", pointer)
|
||||
onCleanup(() => {
|
||||
window.removeEventListener("keydown", combo)
|
||||
window.removeEventListener("pointerdown", pointer)
|
||||
})
|
||||
})
|
||||
|
||||
return (
|
||||
<form
|
||||
ref={(node) => (box = node)}
|
||||
class="omni-search"
|
||||
classList={{ expanded: open() }}
|
||||
role="search"
|
||||
onKeyDown={keys}
|
||||
onSubmit={submit}
|
||||
>
|
||||
<div
|
||||
class="omni-search-head"
|
||||
onPointerDown={(event) => {
|
||||
if (event.target instanceof HTMLButtonElement) return
|
||||
focus()
|
||||
}}
|
||||
>
|
||||
<span class="omni-command" aria-hidden="true">
|
||||
<svg class="omni-command-icon" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M7 9a2 2 0 1 1 2 -2v10a2 2 0 1 1 -2 -2h10a2 2 0 1 1 -2 2v-10a2 2 0 1 1 2 2h-10" />
|
||||
</svg>
|
||||
</span>
|
||||
<input
|
||||
ref={(node) => (field = node)}
|
||||
type="search"
|
||||
value={term()}
|
||||
placeholder={open() ? "" : "Search settings, projects, models..."}
|
||||
aria-label="Omni search"
|
||||
aria-expanded={open()}
|
||||
aria-controls="omni-search-results"
|
||||
onFocus={() => setOpen(true)}
|
||||
onInput={(event) => {
|
||||
setTerm(event.currentTarget.value)
|
||||
setOpen(true)
|
||||
}}
|
||||
/>
|
||||
<button class="omni-escape" type="button" aria-label="Close search" onClick={close}>
|
||||
esc
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Show when={open()}>
|
||||
<div class="omni-results" id="omni-search-results" role="listbox" aria-label="Search results">
|
||||
<Show when={filtered().length > 0} fallback={<div class="omni-empty">No matches.</div>}>
|
||||
<For each={filtered()}>
|
||||
{(item, index) => (
|
||||
<button
|
||||
type="button"
|
||||
class="omni-entry"
|
||||
classList={{ active: index() === active() }}
|
||||
role="option"
|
||||
aria-selected={index() === active()}
|
||||
onClick={() => select(item)}
|
||||
onMouseMove={() => setActive(index())}
|
||||
>
|
||||
<span class="omni-kind">{item.kind}</span>
|
||||
<span class="omni-label" classList={{ mono: item.mono }}>
|
||||
{item.label}
|
||||
</span>
|
||||
<Show when={item.sub}>
|
||||
{(sub) => <span class="omni-sub">{sub()}</span>}
|
||||
</Show>
|
||||
<Show when={index() === active()}>
|
||||
<span class="omni-enter" aria-hidden="true">
|
||||
↵
|
||||
</span>
|
||||
</Show>
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<footer class="omni-footer">
|
||||
<span class="omni-help">
|
||||
<span class="omni-kbd">↑</span>
|
||||
<span class="omni-kbd">↓</span>
|
||||
Navigate
|
||||
</span>
|
||||
<span class="omni-help">
|
||||
<span class="omni-kbd">↵</span>
|
||||
Open
|
||||
</span>
|
||||
<span class="omni-server">{server()}</span>
|
||||
</footer>
|
||||
</Show>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
@media (max-width: 980px) {
|
||||
.kilo-console .app-header {
|
||||
grid-template-columns: minmax(7rem, 10rem) minmax(0, 1fr) auto;
|
||||
}
|
||||
|
||||
.kilo-console .config-page-header,
|
||||
.kilo-console .config-toolbar,
|
||||
.kilo-console .models-filter-primary,
|
||||
@@ -66,6 +70,63 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.kilo-console .app-header {
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.kilo-console .header-title span,
|
||||
.kilo-console .notification-zone {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.kilo-console .omni-search {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.kilo-console .omni-search.expanded {
|
||||
top: 0.5rem;
|
||||
width: min(35rem, calc(100vw - 1rem));
|
||||
max-height: min(70vh, calc(100vh - 1rem));
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
.kilo-console .omni-search.expanded .omni-search-head {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.kilo-console .omni-search.expanded .omni-escape {
|
||||
min-width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
font-size: 0.625rem;
|
||||
}
|
||||
|
||||
.kilo-console .omni-entry {
|
||||
grid-template-columns: 4.75rem minmax(0, 1fr);
|
||||
min-height: 2.5rem;
|
||||
gap: 0.75rem;
|
||||
padding: 0 0.75rem;
|
||||
}
|
||||
|
||||
.kilo-console .omni-sub,
|
||||
.kilo-console .omni-enter,
|
||||
.kilo-console .omni-server {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.kilo-console .omni-footer {
|
||||
min-height: 2rem;
|
||||
gap: 0.75rem;
|
||||
padding: 0.375rem 0.75rem;
|
||||
}
|
||||
|
||||
.kilo-console .omni-kbd {
|
||||
min-width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
font-size: 0.625rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.kilo-console .explore-models,
|
||||
.kilo-console .model-info-grid {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
}
|
||||
|
||||
.kilo-console .header-brand {
|
||||
grid-column: 1;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
@@ -86,39 +87,277 @@
|
||||
}
|
||||
|
||||
.kilo-console .omni-search {
|
||||
--omni-popover: lab(9.03835 1.15298 1.92955);
|
||||
--omni-foreground: oklch(0.985 0.001 106.423);
|
||||
--omni-muted: oklch(0.268 0.007 34.298);
|
||||
--omni-muted-foreground: oklch(0.709 0.01 56.259);
|
||||
--omni-border: oklch(1 0 0 / 10%);
|
||||
--omni-input: oklch(1 0 0 / 15%);
|
||||
|
||||
position: relative;
|
||||
grid-column: 2;
|
||||
justify-self: center;
|
||||
width: min(35rem, 100%);
|
||||
height: 2.5rem;
|
||||
min-width: 0;
|
||||
overflow: visible;
|
||||
border: 0;
|
||||
border-radius: var(--radius-lg);
|
||||
background: var(--omni-popover);
|
||||
color: var(--omni-foreground);
|
||||
box-shadow: 0 0 0 1px var(--omni-border);
|
||||
padding: 0;
|
||||
transition:
|
||||
background 100ms ease,
|
||||
box-shadow 100ms ease;
|
||||
}
|
||||
|
||||
.kilo-console .omni-search.expanded {
|
||||
position: fixed;
|
||||
top: calc((var(--app-header-height) - 2.5rem) / 2);
|
||||
left: 50%;
|
||||
z-index: 80;
|
||||
display: grid;
|
||||
grid-template-rows: auto auto auto;
|
||||
width: min(35rem, calc(100vw - 2rem));
|
||||
height: auto;
|
||||
max-height: min(70vh, calc(100vh - 1rem));
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-xl);
|
||||
background: var(--omni-popover);
|
||||
box-shadow:
|
||||
0 1rem 3rem oklch(0.147 0.004 49.25 / 45%),
|
||||
0 0 0 1px var(--omni-border);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.kilo-console .omni-search.expanded .omni-search-head,
|
||||
.kilo-console .omni-search.expanded .omni-results,
|
||||
.kilo-console .omni-search.expanded .omni-footer {
|
||||
background: var(--omni-popover);
|
||||
}
|
||||
|
||||
.kilo-console .omni-search.expanded::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
background: oklch(0.147 0.004 49.25 / 72%);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.kilo-console .omni-search-head {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
align-items: center;
|
||||
justify-self: center;
|
||||
width: min(32rem, 100%);
|
||||
height: 1.875rem;
|
||||
gap: 0.75rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--input-base);
|
||||
padding: 0.25rem 0.5rem;
|
||||
height: 100%;
|
||||
gap: 0.5rem;
|
||||
padding: 0 0.75rem;
|
||||
}
|
||||
|
||||
.kilo-console .omni-search span {
|
||||
.kilo-console .omni-search.expanded .omni-search-head {
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
border-bottom: 1px solid var(--omni-border);
|
||||
padding: 0.625rem 0.75rem;
|
||||
}
|
||||
|
||||
.kilo-console .omni-escape,
|
||||
.kilo-console .omni-kbd {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 1.5rem;
|
||||
height: 1.25rem;
|
||||
flex: 0 0 auto;
|
||||
border: 0;
|
||||
border-radius: var(--radius-xs);
|
||||
background: var(--muted);
|
||||
color: var(--muted-foreground);
|
||||
background: var(--omni-muted);
|
||||
color: var(--omni-muted-foreground);
|
||||
font-size: 0.625rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.kilo-console .omni-command {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 auto;
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
color: var(--omni-muted-foreground);
|
||||
}
|
||||
|
||||
.kilo-console .omni-command-icon {
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
stroke: currentColor;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.kilo-console .omni-search.expanded .omni-escape {
|
||||
min-width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
border-radius: var(--radius-xs);
|
||||
font-size: 0.625rem;
|
||||
}
|
||||
|
||||
.kilo-console .omni-escape {
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.kilo-console .omni-search.expanded .omni-escape {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.kilo-console .omni-escape:hover,
|
||||
.kilo-console .omni-escape:focus-visible {
|
||||
background: color-mix(in oklab, var(--omni-muted) 70%, var(--omni-foreground) 10%);
|
||||
color: var(--omni-foreground);
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.kilo-console .omni-search input {
|
||||
height: 1.25rem;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
color: var(--omni-foreground);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.75rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.kilo-console .omni-search input:focus {
|
||||
border-color: transparent;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.kilo-console .omni-search input::placeholder {
|
||||
color: var(--omni-muted-foreground);
|
||||
}
|
||||
|
||||
.kilo-console .omni-search.expanded input {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.kilo-console .omni-results {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
max-height: 50vh;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
.kilo-console .omni-entry {
|
||||
display: grid;
|
||||
grid-template-columns: 3.5rem minmax(0, 1fr) auto auto;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
min-height: 2rem;
|
||||
gap: 0.625rem;
|
||||
border: 0;
|
||||
border-radius: var(--radius-sm);
|
||||
background: transparent;
|
||||
color: var(--omni-foreground);
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
padding: 0.5rem 0.625rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.kilo-console .omni-entry:hover,
|
||||
.kilo-console .omni-entry:focus-visible,
|
||||
.kilo-console .omni-entry.active {
|
||||
background: var(--omni-muted);
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.kilo-console .omni-kind {
|
||||
color: var(--omni-muted-foreground);
|
||||
font-size: 0.625rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.kilo-console .omni-label,
|
||||
.kilo-console .omni-sub {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.kilo-console .omni-label {
|
||||
color: var(--omni-foreground);
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.625;
|
||||
}
|
||||
|
||||
.kilo-console .omni-label.mono,
|
||||
.kilo-console .omni-sub,
|
||||
.kilo-console .omni-server {
|
||||
font-family: var(--font-family-mono, "SFMono-Regular", Consolas, monospace);
|
||||
}
|
||||
|
||||
.kilo-console .omni-sub {
|
||||
max-width: 7rem;
|
||||
color: var(--omni-muted-foreground);
|
||||
font-size: 0.6875rem;
|
||||
}
|
||||
|
||||
.kilo-console .omni-enter {
|
||||
color: var(--omni-muted-foreground);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.kilo-console .omni-empty {
|
||||
color: var(--omni-muted-foreground);
|
||||
font-size: 0.875rem;
|
||||
padding: 2.5rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.kilo-console .omni-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
min-height: 2rem;
|
||||
border-top: 1px solid var(--omni-border);
|
||||
color: var(--omni-muted-foreground);
|
||||
font-size: 0.6875rem;
|
||||
padding: 0.375rem 0.75rem;
|
||||
}
|
||||
|
||||
.kilo-console .omni-help {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.kilo-console .omni-kbd {
|
||||
min-width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 0.6875rem;
|
||||
}
|
||||
|
||||
.kilo-console .omni-server {
|
||||
overflow: hidden;
|
||||
margin-left: auto;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.kilo-console .notification-zone {
|
||||
grid-column: 3;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-self: end;
|
||||
@@ -167,7 +406,9 @@
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--muted-foreground);
|
||||
text-decoration: none;
|
||||
transition: background 120ms ease, color 120ms ease;
|
||||
transition:
|
||||
background 120ms ease,
|
||||
color 120ms ease;
|
||||
}
|
||||
|
||||
.kilo-console .rail-glyph {
|
||||
|
||||
Reference in New Issue
Block a user