| 1 | import { createStore } from "/js/AlpineStore.js"; |
| 2 | import { callJsonApi } from "/js/api.js"; |
| 3 | import { openModal, closeModal } from "/js/modals.js"; |
| 4 | import { |
| 5 | toastFrontendError, |
| 6 | toastFrontendSuccess, |
| 7 | } from "/components/notifications/notification-store.js"; |
| 8 | import { store as chatsStore } from "/components/sidebar/chats/chats-store.js"; |
| 9 | import { store as sidebarStore } from "/components/sidebar/sidebar-store.js"; |
| 10 | import { store as tasksStore } from "/components/sidebar/tasks/tasks-store.js"; |
| 11 | |
| 12 | const PLUGIN_ID = "_chat_naming"; |
| 13 | const MODAL_PATH = `/plugins/${PLUGIN_ID}/webui/rename.html`; |
| 14 | |
| 15 | const model = { |
| 16 | itemId: "", |
| 17 | kind: "chat", |
| 18 | name: "", |
| 19 | loading: false, |
| 20 | generating: false, |
| 21 | saving: false, |
| 22 | |
| 23 | async openFromMenu(menuId, kind) { |
| 24 | const prefix = `${kind}:`; |
| 25 | if (typeof menuId !== "string" || !menuId.startsWith(prefix)) return; |
| 26 | this.itemId = menuId.slice(prefix.length); |
| 27 | this.kind = kind; |
| 28 | this.name = this.localName(); |
| 29 | sidebarStore.rowMenuClose(); |
| 30 | await openModal(MODAL_PATH); |
| 31 | }, |
| 32 | |
| 33 | async onOpen() { |
| 34 | if (!this.itemId) return; |
| 35 | this.loading = true; |
| 36 | try { |
| 37 | const response = await this.call("get"); |
| 38 | this.name = response?.name || ""; |
| 39 | } catch (error) { |
| 40 | void toastFrontendError(error?.message || "Failed to load the name.", "Chat Naming"); |
| 41 | } finally { |
| 42 | this.loading = false; |
| 43 | } |
| 44 | }, |
| 45 | |
| 46 | async generate() { |
| 47 | if (!this.itemId || this.generating) return; |
| 48 | this.generating = true; |
| 49 | try { |
| 50 | const response = await this.call("generate"); |
| 51 | this.name = response?.name || this.name; |
| 52 | } catch (error) { |
| 53 | void toastFrontendError(error?.message || "Failed to generate a name.", "Chat Naming"); |
| 54 | } finally { |
| 55 | this.generating = false; |
| 56 | } |
| 57 | }, |
| 58 | |
| 59 | async openSettings() { |
| 60 | try { |
| 61 | const { store } = await import("/components/plugins/plugin-settings-store.js"); |
| 62 | const item = this.localItem(); |
| 63 | await store.openConfig( |
| 64 | PLUGIN_ID, |
| 65 | item?.project?.name || "", |
| 66 | item?.agent_profile || "", |
| 67 | ); |
| 68 | } catch (error) { |
| 69 | void toastFrontendError( |
| 70 | error?.message || "Failed to open settings.", |
| 71 | "Chat Naming", |
| 72 | ); |
| 73 | } |
| 74 | }, |
| 75 | |
| 76 | async save() { |
| 77 | const name = this.name.trim(); |
| 78 | if (!this.itemId || !name || this.saving) return; |
| 79 | this.saving = true; |
| 80 | try { |
| 81 | const response = await this.call("save", { name }); |
| 82 | this.name = response?.name || name; |
| 83 | this.updateLocalName(this.name); |
| 84 | await closeModal(MODAL_PATH); |
| 85 | void toastFrontendSuccess( |
| 86 | this.kind === "task" ? "Task renamed." : "Chat renamed.", |
| 87 | "Chat Naming", |
| 88 | ); |
| 89 | } catch (error) { |
| 90 | void toastFrontendError(error?.message || "Failed to save the name.", "Chat Naming"); |
| 91 | } finally { |
| 92 | this.saving = false; |
| 93 | } |
| 94 | }, |
| 95 | |
| 96 | close() { |
| 97 | return closeModal(MODAL_PATH); |
| 98 | }, |
| 99 | |
| 100 | call(action, extra = {}) { |
| 101 | return callJsonApi(`/plugins/${PLUGIN_ID}/chat_name`, { |
| 102 | action, |
| 103 | kind: this.kind, |
| 104 | item_id: this.itemId, |
| 105 | ...extra, |
| 106 | }); |
| 107 | }, |
| 108 | |
| 109 | localName() { |
| 110 | const item = this.localItem(); |
| 111 | return this.kind === "task" ? item?.task_name || "" : item?.name || ""; |
| 112 | }, |
| 113 | |
| 114 | localItem() { |
| 115 | return this.kind === "task" |
| 116 | ? tasksStore.tasks.find((task) => task.id === this.itemId) |
| 117 | : chatsStore.contexts.find((context) => context.id === this.itemId); |
| 118 | }, |
| 119 | |
| 120 | updateLocalName(name) { |
| 121 | if (this.kind === "task") { |
| 122 | tasksStore.tasks = tasksStore.tasks.map((task) => |
| 123 | task.id === this.itemId ? { ...task, name, task_name: name } : task, |
| 124 | ); |
| 125 | return; |
| 126 | } |
| 127 | chatsStore.contexts = chatsStore.contexts.map((context) => |
| 128 | context.id === this.itemId |
| 129 | ? { |
| 130 | ...context, |
| 131 | name, |
| 132 | ...(context.parent_context_id ? { parent_context_label: name } : {}), |
| 133 | } |
| 134 | : context, |
| 135 | ); |
| 136 | if (chatsStore.selectedContext?.id === this.itemId) { |
| 137 | chatsStore.selectedContext = { |
| 138 | ...chatsStore.selectedContext, |
| 139 | name, |
| 140 | ...(chatsStore.selectedContext.parent_context_id |
| 141 | ? { parent_context_label: name } |
| 142 | : {}), |
| 143 | }; |
| 144 | } |
| 145 | }, |
| 146 | }; |
| 147 | |
| 148 | export const store = createStore("chatNaming", model); |