feat: naomi did too much at once (#53)
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 53s
CI / Lint & Test (push) Successful in 14m10s
CI / Build Linux (push) Successful in 16m47s
CI / Build Windows (cross-compile) (push) Successful in 26m36s

- feat: add slash commands
- feat: toggle window always on top
- fix: save settings button closes settings panel
- feat: input history (both text and commands)
- feat: add keyboard shortcuts
- feat: add confirmation modal when closing connected tabs
- fix: better text colours in light mode
- fix: handle multiple tabs requesting permission

Closes #6
Closes #13
Closes #21
Closes #28

Reviewed-on: #53
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit was merged in pull request #53.
This commit is contained in:
2026-01-21 17:38:36 -08:00
committed by Naomi Carrigan
parent 9fe4e8a48a
commit 947e56ef41
17 changed files with 1040 additions and 137 deletions
+4 -2
View File
@@ -47,6 +47,8 @@ export const claudeStore = {
getConversationHistory: conversationsStore.getConversationHistory,
requestPermission: conversationsStore.requestPermission,
clearPermission: conversationsStore.clearPermission,
requestPermissionForConversation: conversationsStore.requestPermissionForConversation,
clearPermissionForConversation: conversationsStore.clearPermissionForConversation,
grantTool: conversationsStore.grantTool,
revokeAllTools: conversationsStore.revokeAllTools,
isToolGranted: conversationsStore.isToolGranted,
@@ -83,8 +85,8 @@ export const claudeStore = {
};
export const hasPermissionPending = derived(
claudeStore.pendingPermission,
($permission) => $permission !== null
claudeStore.activeConversation,
($conversation) => $conversation?.pendingPermission !== null
);
// Derived store to check if Claude is currently processing (can be interrupted)
+2
View File
@@ -14,6 +14,7 @@ export interface HikariConfig {
greeting_custom_prompt: string | null;
notifications_enabled: boolean;
notification_volume: number;
always_on_top: boolean;
}
const defaultConfig: HikariConfig = {
@@ -27,6 +28,7 @@ const defaultConfig: HikariConfig = {
greeting_custom_prompt: null,
notifications_enabled: true,
notification_volume: 0.7,
always_on_top: false,
};
function createConfigStore() {
+52 -3
View File
@@ -14,6 +14,7 @@ export interface Conversation {
characterState: CharacterState;
isProcessing: boolean;
grantedTools: Set<string>;
pendingPermission: PermissionRequest | null;
createdAt: Date;
lastActivityAt: Date;
}
@@ -21,7 +22,6 @@ export interface Conversation {
function createConversationsStore() {
const conversations = writable<Map<string, Conversation>>(new Map());
const activeConversationId = writable<string | null>(null);
const pendingPermission = writable<PermissionRequest | null>(null);
const pendingRetryMessage = writable<string | null>(null);
let conversationCounter = 0;
@@ -47,6 +47,7 @@ function createConversationsStore() {
characterState: "idle",
isProcessing: false,
grantedTools: new Set(),
pendingPermission: null,
createdAt: new Date(),
lastActivityAt: new Date(),
};
@@ -93,6 +94,10 @@ function createConversationsStore() {
activeConversation,
($conv) => $conv?.grantedTools || new Set<string>()
);
const pendingPermission = derived(
activeConversation,
($conv) => $conv?.pendingPermission || null
);
return {
// Expose derived stores for compatibility
@@ -148,8 +153,52 @@ function createConversationsStore() {
return convs;
});
},
requestPermission: (request: PermissionRequest) => pendingPermission.set(request),
clearPermission: () => pendingPermission.set(null),
requestPermission: (request: PermissionRequest) => {
const activeId = get(activeConversationId);
if (!activeId) return;
conversations.update((convs) => {
const conv = convs.get(activeId);
if (conv) {
conv.pendingPermission = request;
conv.lastActivityAt = new Date();
}
return convs;
});
},
clearPermission: () => {
const activeId = get(activeConversationId);
if (!activeId) return;
conversations.update((convs) => {
const conv = convs.get(activeId);
if (conv) {
conv.pendingPermission = null;
conv.lastActivityAt = new Date();
}
return convs;
});
},
requestPermissionForConversation: (conversationId: string, request: PermissionRequest) => {
conversations.update((convs) => {
const conv = convs.get(conversationId);
if (conv) {
conv.pendingPermission = request;
conv.lastActivityAt = new Date();
}
return convs;
});
},
clearPermissionForConversation: (conversationId: string) => {
conversations.update((convs) => {
const conv = convs.get(conversationId);
if (conv) {
conv.pendingPermission = null;
conv.lastActivityAt = new Date();
}
return convs;
});
},
setPendingRetryMessage: (message: string | null) => pendingRetryMessage.set(message),
// Conversation management