mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-24 16:02:55 +08:00
Merge pull request #7492 from Kilo-Org/kirillk/banner
feat(vscode): improve notifications UX
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
import { Component, Show, createMemo, createSignal } from "solid-js"
|
||||
import { Button } from "@kilocode/kilo-ui/button"
|
||||
import { IconButton } from "@kilocode/kilo-ui/icon-button"
|
||||
import { Icon } from "@kilocode/kilo-ui/icon"
|
||||
import { Component, Show, createEffect, createMemo, createSignal } from "solid-js"
|
||||
import { useNotifications } from "../../context/notifications"
|
||||
import { useVSCode } from "../../context/vscode"
|
||||
import { useSession } from "../../context/session"
|
||||
import { useProvider } from "../../context/provider"
|
||||
import { useLanguage } from "../../context/language"
|
||||
import { KILO_PROVIDER_ID } from "../../../../src/shared/provider-model"
|
||||
import { TelemetryEventName } from "../../../../src/services/telemetry/types"
|
||||
|
||||
@@ -14,6 +12,7 @@ export const KiloNotifications: Component = () => {
|
||||
const vscode = useVSCode()
|
||||
const session = useSession()
|
||||
const provider = useProvider()
|
||||
const language = useLanguage()
|
||||
const [index, setIndex] = createSignal(0)
|
||||
|
||||
const items = filteredNotifications
|
||||
@@ -21,18 +20,24 @@ export const KiloNotifications: Component = () => {
|
||||
const safeIndex = () => Math.min(index(), Math.max(0, total() - 1))
|
||||
const current = createMemo(() => (total() === 0 ? undefined : items()[safeIndex()]))
|
||||
|
||||
const prev = () => setIndex((i) => (i - 1 + total()) % total())
|
||||
const next = () => setIndex((i) => (i + 1) % total())
|
||||
// Clamp index whenever the list shrinks so navigation always reflects reality
|
||||
createEffect(() => {
|
||||
const max = Math.max(0, total() - 1)
|
||||
if (index() > max) setIndex(max)
|
||||
})
|
||||
|
||||
const handleAction = (url: string) => {
|
||||
vscode.postMessage({ type: "openExternal", url })
|
||||
}
|
||||
|
||||
const handleDismiss = () => {
|
||||
const n = current()
|
||||
if (!n) return
|
||||
dismiss(n.id)
|
||||
setIndex((i) => Math.min(i, Math.max(0, total() - 2)))
|
||||
const isLast = () => safeIndex() === total() - 1
|
||||
|
||||
const handleNext = () => {
|
||||
if (isLast()) {
|
||||
for (const n of items()) dismiss(n.id)
|
||||
} else {
|
||||
setIndex(safeIndex() + 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,35 +78,36 @@ export const KiloNotifications: Component = () => {
|
||||
<div class="kilo-notifications-card">
|
||||
<div class="kilo-notifications-header">
|
||||
<span class="kilo-notifications-title">{current()?.title}</span>
|
||||
<IconButton size="small" variant="ghost" icon="close" onClick={handleDismiss} title="Dismiss" />
|
||||
<Show when={total() > 1}>
|
||||
<span class="kilo-notifications-nav-count">
|
||||
{safeIndex() + 1} / {total()}
|
||||
</span>
|
||||
</Show>
|
||||
</div>
|
||||
<p class="kilo-notifications-message">{current()?.message}</p>
|
||||
<div class="kilo-notifications-footer">
|
||||
<Show when={total() > 1}>
|
||||
<div class="kilo-notifications-nav">
|
||||
<button class="kilo-notifications-nav-btn" onClick={prev} title="Previous">
|
||||
<Icon name="arrow-left" size="small" />
|
||||
</button>
|
||||
<span class="kilo-notifications-nav-count">
|
||||
{safeIndex() + 1} / {total()}
|
||||
</span>
|
||||
<button class="kilo-notifications-nav-btn" onClick={next} title="Next">
|
||||
<Icon name="arrow-right" size="small" />
|
||||
</button>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={canSwitchModel()}>
|
||||
<Button variant="primary" size="small" onClick={handleTryModel}>
|
||||
Try model
|
||||
</Button>
|
||||
<button class="kilo-notifications-action-btn" onClick={handleTryModel}>
|
||||
{language.t("notifications.action.tryModel")}
|
||||
</button>
|
||||
</Show>
|
||||
<Show when={current()?.action}>
|
||||
{(action) => (
|
||||
<Button variant="primary" size="small" onClick={() => handleAction(action().actionURL)}>
|
||||
<button class="kilo-notifications-action-btn" onClick={() => handleAction(action().actionURL)}>
|
||||
{action().actionText}
|
||||
</Button>
|
||||
</button>
|
||||
)}
|
||||
</Show>
|
||||
<div class="kilo-notifications-next-group">
|
||||
<Show when={safeIndex() > 0}>
|
||||
<button class="kilo-notifications-back-link" onClick={() => setIndex(safeIndex() - 1)}>
|
||||
{language.t("notifications.action.previous")}
|
||||
</button>
|
||||
</Show>
|
||||
<button class="kilo-notifications-primary-btn" onClick={handleNext}>
|
||||
{isLast() ? language.t("notifications.action.close") : language.t("notifications.action.next")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1188,4 +1188,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "عدة جلسات تعمل وستتوقف",
|
||||
"settings.saveBar.saveAnyway": "حفظ على أي حال",
|
||||
"settings.saveBar.cancel": "إلغاء",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
}
|
||||
|
||||
@@ -1211,4 +1211,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "Várias sessões estão em execução e serão interrompidas",
|
||||
"settings.saveBar.saveAnyway": "Salvar mesmo assim",
|
||||
"settings.saveBar.cancel": "Cancelar",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
}
|
||||
|
||||
@@ -1210,4 +1210,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "Nekoliko sesija je pokrenuto i bit će prekinuto",
|
||||
"settings.saveBar.saveAnyway": "Spremi svejedno",
|
||||
"settings.saveBar.cancel": "Otkaži",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
}
|
||||
|
||||
@@ -1203,4 +1203,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "Flere sessioner kører og vil blive afbrudt",
|
||||
"settings.saveBar.saveAnyway": "Gem alligevel",
|
||||
"settings.saveBar.cancel": "Annuller",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
}
|
||||
|
||||
@@ -1225,4 +1225,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "Mehrere Sitzungen laufen und werden unterbrochen",
|
||||
"settings.saveBar.saveAnyway": "Trotzdem speichern",
|
||||
"settings.saveBar.cancel": "Abbrechen",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
@@ -1216,4 +1216,9 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "Several sessions are running and will be interrupted",
|
||||
"settings.saveBar.saveAnyway": "Save anyway",
|
||||
"settings.saveBar.cancel": "Cancel",
|
||||
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
}
|
||||
|
||||
@@ -1214,4 +1214,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "Varias sesiones están en ejecución y se interrumpirán",
|
||||
"settings.saveBar.saveAnyway": "Guardar de todas formas",
|
||||
"settings.saveBar.cancel": "Cancelar",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
}
|
||||
|
||||
@@ -1226,4 +1226,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "Plusieurs sessions sont en cours et seront interrompues",
|
||||
"settings.saveBar.saveAnyway": "Enregistrer quand même",
|
||||
"settings.saveBar.cancel": "Annuler",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
}
|
||||
|
||||
@@ -1200,4 +1200,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "複数のセッションが実行中で中断されます",
|
||||
"settings.saveBar.saveAnyway": "それでも保存",
|
||||
"settings.saveBar.cancel": "キャンセル",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
}
|
||||
|
||||
@@ -1191,4 +1191,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "여러 세션이 실행 중이며 중단됩니다",
|
||||
"settings.saveBar.saveAnyway": "그래도 저장",
|
||||
"settings.saveBar.cancel": "취소",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
}
|
||||
|
||||
@@ -1232,4 +1232,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "Meerdere sessies zijn actief en worden onderbroken",
|
||||
"settings.saveBar.saveAnyway": "Toch opslaan",
|
||||
"settings.saveBar.cancel": "Annuleren",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
}
|
||||
|
||||
@@ -1201,4 +1201,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "Flere økter kjører og vil bli avbrutt",
|
||||
"settings.saveBar.saveAnyway": "Lagre uansett",
|
||||
"settings.saveBar.cancel": "Avbryt",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
@@ -1208,4 +1208,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "Kilka sesji jest uruchomionych i zostanie przerwanych",
|
||||
"settings.saveBar.saveAnyway": "Zapisz mimo to",
|
||||
"settings.saveBar.cancel": "Anuluj",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
}
|
||||
|
||||
@@ -1207,4 +1207,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "Несколько сеансов выполняются и будут прерваны",
|
||||
"settings.saveBar.saveAnyway": "Сохранить в любом случае",
|
||||
"settings.saveBar.cancel": "Отмена",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
}
|
||||
|
||||
@@ -1187,4 +1187,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "มีหลายเซสชันกำลังทำงานและจะถูกขัดจังหวะ",
|
||||
"settings.saveBar.saveAnyway": "บันทึกต่อไป",
|
||||
"settings.saveBar.cancel": "ยกเลิก",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
}
|
||||
|
||||
@@ -1226,4 +1226,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "Birden fazla oturum çalışıyor ve kesintiye uğrayacak",
|
||||
"settings.saveBar.saveAnyway": "Yine de kaydet",
|
||||
"settings.saveBar.cancel": "İptal",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
}
|
||||
|
||||
@@ -1171,4 +1171,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "多个会话正在运行,将被中断",
|
||||
"settings.saveBar.saveAnyway": "仍然保存",
|
||||
"settings.saveBar.cancel": "取消",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
@@ -1172,4 +1172,8 @@ export const dict = {
|
||||
"settings.saveBar.warning.many": "多個工作階段正在執行,將被中斷",
|
||||
"settings.saveBar.saveAnyway": "仍然儲存",
|
||||
"settings.saveBar.cancel": "取消",
|
||||
"notifications.action.previous": "Previous",
|
||||
"notifications.action.next": "Next",
|
||||
"notifications.action.close": "Close",
|
||||
"notifications.action.tryModel": "Try model",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
@@ -2124,7 +2124,7 @@
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-bottom: 1px solid var(--vscode-panel-border);
|
||||
@@ -2185,6 +2185,65 @@
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.kilo-notifications-next-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.kilo-notifications-action-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 24px;
|
||||
padding: 0 8px;
|
||||
background: none;
|
||||
border: 1px solid var(--vscode-focusBorder);
|
||||
border-radius: 3px;
|
||||
color: var(--vscode-focusBorder);
|
||||
font-size: 12px;
|
||||
font-family: var(--vscode-font-family);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.kilo-notifications-action-btn:hover {
|
||||
background-color: var(--vscode-toolbar-hoverBackground);
|
||||
}
|
||||
|
||||
.kilo-notifications-primary-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 24px;
|
||||
padding: 0 8px;
|
||||
background-color: var(--vscode-button-background);
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
color: var(--vscode-button-foreground);
|
||||
font-size: 12px;
|
||||
font-family: var(--vscode-font-family);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.kilo-notifications-primary-btn:hover {
|
||||
background-color: var(--vscode-button-hoverBackground);
|
||||
}
|
||||
|
||||
.kilo-notifications-back-link {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.kilo-notifications-back-link:hover {
|
||||
color: var(--vscode-foreground);
|
||||
}
|
||||
|
||||
.kilo-notifications-nav-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
Reference in New Issue
Block a user