@g9tm/aiassistant-svelte 0.0.0-stage → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +29 -2
  2. package/dist/AssistantRoot.svelte +130 -0
  3. package/dist/AssistantRoot.svelte.d.ts +13 -0
  4. package/dist/admin.d.ts +2 -0
  5. package/dist/admin.js +2 -0
  6. package/dist/blocks/BlockView.svelte +84 -0
  7. package/dist/blocks/BlockView.svelte.d.ts +7 -0
  8. package/dist/blocks/Chart.svelte +51 -0
  9. package/dist/blocks/Chart.svelte.d.ts +9 -0
  10. package/dist/blocks/CsvButton.svelte +27 -0
  11. package/dist/blocks/CsvButton.svelte.d.ts +8 -0
  12. package/dist/browser.d.ts +16 -0
  13. package/dist/browser.js +80 -0
  14. package/dist/cards/ApprovalCard.svelte +56 -0
  15. package/dist/cards/ApprovalCard.svelte.d.ts +7 -0
  16. package/dist/cards/Cards.svelte +55 -0
  17. package/dist/cards/Cards.svelte.d.ts +11 -0
  18. package/dist/cards/SecretField.svelte +21 -0
  19. package/dist/cards/SecretField.svelte.d.ts +6 -0
  20. package/dist/context.d.ts +33 -0
  21. package/dist/context.js +35 -0
  22. package/dist/index.d.ts +38 -0
  23. package/dist/index.js +36 -0
  24. package/dist/pages/Abilities.svelte +6 -0
  25. package/dist/pages/Abilities.svelte.d.ts +6 -0
  26. package/dist/pages/ActionsPage.svelte +6 -0
  27. package/dist/pages/ActionsPage.svelte.d.ts +6 -0
  28. package/dist/pages/AdminPage.svelte +53 -0
  29. package/dist/pages/AdminPage.svelte.d.ts +6 -0
  30. package/dist/pages/FindingsPage.svelte +6 -0
  31. package/dist/pages/FindingsPage.svelte.d.ts +6 -0
  32. package/dist/pages/History.svelte +96 -0
  33. package/dist/pages/History.svelte.d.ts +6 -0
  34. package/dist/pages/KnowledgePage.svelte +6 -0
  35. package/dist/pages/KnowledgePage.svelte.d.ts +6 -0
  36. package/dist/pages/LearnedPage.svelte +6 -0
  37. package/dist/pages/LearnedPage.svelte.d.ts +6 -0
  38. package/dist/pages/Page.svelte +82 -0
  39. package/dist/pages/Page.svelte.d.ts +8 -0
  40. package/dist/pages/RoutineWizard.svelte +197 -0
  41. package/dist/pages/RoutineWizard.svelte.d.ts +6 -0
  42. package/dist/pages/SlotPicker.svelte +55 -0
  43. package/dist/pages/SlotPicker.svelte.d.ts +17 -0
  44. package/dist/pages/admin/AdminAudit.svelte +33 -0
  45. package/dist/pages/admin/AdminAudit.svelte.d.ts +3 -0
  46. package/dist/pages/admin/AdminCapabilities.svelte +91 -0
  47. package/dist/pages/admin/AdminCapabilities.svelte.d.ts +3 -0
  48. package/dist/pages/admin/AdminConnections.svelte +58 -0
  49. package/dist/pages/admin/AdminConnections.svelte.d.ts +6 -0
  50. package/dist/pages/admin/AdminFeedback.svelte +38 -0
  51. package/dist/pages/admin/AdminFeedback.svelte.d.ts +3 -0
  52. package/dist/pages/admin/AdminModels.svelte +103 -0
  53. package/dist/pages/admin/AdminModels.svelte.d.ts +6 -0
  54. package/dist/pages/admin/AdminUsage.svelte +92 -0
  55. package/dist/pages/admin/AdminUsage.svelte.d.ts +3 -0
  56. package/dist/pages/texts.d.ts +9 -0
  57. package/dist/pages/texts.js +42 -0
  58. package/dist/parts/Composer.svelte +69 -0
  59. package/dist/parts/Composer.svelte.d.ts +14 -0
  60. package/dist/parts/Message.svelte +40 -0
  61. package/dist/parts/Message.svelte.d.ts +9 -0
  62. package/dist/parts/MessageList.svelte +28 -0
  63. package/dist/parts/MessageList.svelte.d.ts +9 -0
  64. package/dist/parts/PdfButton.svelte +37 -0
  65. package/dist/parts/PdfButton.svelte.d.ts +3 -0
  66. package/dist/parts/RevealText.svelte +26 -0
  67. package/dist/parts/RevealText.svelte.d.ts +7 -0
  68. package/dist/parts/Small.svelte +24 -0
  69. package/dist/parts/Small.svelte.d.ts +10 -0
  70. package/dist/parts/StagesThinking.svelte +15 -0
  71. package/dist/parts/StagesThinking.svelte.d.ts +8 -0
  72. package/dist/parts/TextView.svelte +36 -0
  73. package/dist/parts/TextView.svelte.d.ts +6 -0
  74. package/dist/parts/ThinkingOrb.svelte +7 -0
  75. package/dist/parts/ThinkingOrb.svelte.d.ts +18 -0
  76. package/dist/parts/WorkCard.svelte +41 -0
  77. package/dist/parts/WorkCard.svelte.d.ts +7 -0
  78. package/dist/slots/Dialog.svelte +20 -0
  79. package/dist/slots/Dialog.svelte.d.ts +9 -0
  80. package/dist/slots/PinPad.svelte +6 -0
  81. package/dist/slots/PinPad.svelte.d.ts +7 -0
  82. package/dist/store.svelte.d.ts +15 -0
  83. package/dist/store.svelte.js +31 -0
  84. package/dist/surfaces/AssistantChat.svelte +31 -0
  85. package/dist/surfaces/AssistantChat.svelte.d.ts +6 -0
  86. package/dist/surfaces/AssistantPanel.svelte +52 -0
  87. package/dist/surfaces/AssistantPanel.svelte.d.ts +13 -0
  88. package/dist/surfaces/Surfaces.svelte +43 -0
  89. package/dist/surfaces/Surfaces.svelte.d.ts +16 -0
  90. package/dist/theme.d.ts +9 -0
  91. package/dist/theme.js +22 -0
  92. package/package.json +46 -3
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ // A secret the person types: sent once as the answer, then cleared; never kept in state, history or storage.
3
+ import { cls, useUi } from "../context.js";
4
+
5
+ let { onsubmit }: { onsubmit: (value: string) => void } = $props();
6
+ const ui = useUi();
7
+ let input: HTMLInputElement | undefined = $state();
8
+
9
+ function send(e: SubmitEvent): void {
10
+ e.preventDefault();
11
+ if (!input || input.value.length === 0) return;
12
+ const value = input.value;
13
+ input.value = ""; // read straight from the field, never bound to state
14
+ onsubmit(value);
15
+ }
16
+ </script>
17
+
18
+ <form class={cls(ui, "secret-field")} onsubmit={send} autocomplete="off">
19
+ <input bind:this={input} type="password" autocomplete="off" aria-label={ui.texts.t("secret.hint")} placeholder={ui.texts.t("secret.hint")} />
20
+ <button type="submit">{ui.texts.t("ask.send")}</button>
21
+ </form>
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onsubmit: (value: string) => void;
3
+ };
4
+ declare const SecretField: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type SecretField = ReturnType<typeof SecretField>;
6
+ export default SecretField;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * What every component of one assistant shares: the core assistant, its texts, the host's per-part classes
3
+ * and styles, the unstyled switch, and component overrides. Any component, small or large, can be replaced by the host;
4
+ * the replacement gets the same view model and the default component, so it can wrap it.
5
+ */
6
+ import type { Component } from "svelte";
7
+ import type { Assistant, PartName, PartStyles, Texts } from "@g9tm/aiassistant";
8
+ /** Any component a host gives (its own props: the library passes the view model and the default). */
9
+ export type HostComponent = Component<any>;
10
+ /** Host replacements: by component name, and per block type (including the host's x:* blocks). */
11
+ export interface Overrides {
12
+ [name: string]: HostComponent | Record<string, HostComponent> | undefined;
13
+ Block?: Record<string, HostComponent>;
14
+ }
15
+ export interface AssistantUi {
16
+ assistant: Assistant;
17
+ texts: Texts;
18
+ styles: PartStyles;
19
+ unstyled: boolean;
20
+ components: Overrides;
21
+ /** The layer dialogs inside the panel sit on (above the panel). */
22
+ layer: number;
23
+ }
24
+ export declare function provideUi(ui: AssistantUi): AssistantUi;
25
+ export declare function useUi(): AssistantUi;
26
+ /** The class of a part (the library's stable class and the host's). */
27
+ export declare function cls(ui: AssistantUi, part: PartName): string;
28
+ /** The inline style the host gave a part. */
29
+ export declare function sty(ui: AssistantUi, part: PartName): string | undefined;
30
+ /** The host's replacement of a component, if any. */
31
+ export declare function override(ui: AssistantUi, name: string): HostComponent | undefined;
32
+ /** The host's renderer of a block type, if any. */
33
+ export declare function blockOverride(ui: AssistantUi, type: string): HostComponent | undefined;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * What every component of one assistant shares: the core assistant, its texts, the host's per-part classes
3
+ * and styles, the unstyled switch, and component overrides. Any component, small or large, can be replaced by the host;
4
+ * the replacement gets the same view model and the default component, so it can wrap it.
5
+ */
6
+ import { getContext, setContext } from "svelte";
7
+ import { partClass } from "@g9tm/aiassistant";
8
+ const KEY = Symbol("g9-assistant-ui");
9
+ export function provideUi(ui) {
10
+ setContext(KEY, ui);
11
+ return ui;
12
+ }
13
+ export function useUi() {
14
+ const ui = getContext(KEY);
15
+ if (!ui)
16
+ throw new Error("Use the assistant's components inside AssistantRoot, AssistantPanel or AssistantChat.");
17
+ return ui;
18
+ }
19
+ /** The class of a part (the library's stable class and the host's). */
20
+ export function cls(ui, part) {
21
+ return partClass(part, ui.styles, ui.unstyled);
22
+ }
23
+ /** The inline style the host gave a part. */
24
+ export function sty(ui, part) {
25
+ return ui.styles.styles ? new Map(Object.entries(ui.styles.styles)).get(part) : undefined;
26
+ }
27
+ /** The host's replacement of a component, if any. */
28
+ export function override(ui, name) {
29
+ const c = new Map(Object.entries(ui.components)).get(name);
30
+ return typeof c === "function" ? c : undefined;
31
+ }
32
+ /** The host's renderer of a block type, if any. */
33
+ export function blockOverride(ui, type) {
34
+ return ui.components.Block ? new Map(Object.entries(ui.components.Block)).get(type) : undefined;
35
+ }
@@ -0,0 +1,38 @@
1
+ /** @g9tm/aiassistant-svelte: Svelte 5 components of the G9 AI assistant, customisable in five layers. */
2
+ export { default as AssistantRoot } from "./AssistantRoot.svelte";
3
+ export { default as AssistantPanel } from "./surfaces/AssistantPanel.svelte";
4
+ export { default as AssistantChat } from "./surfaces/AssistantChat.svelte";
5
+ export { default as Surfaces } from "./surfaces/Surfaces.svelte";
6
+ export { default as MessageList } from "./parts/MessageList.svelte";
7
+ export { default as Message } from "./parts/Message.svelte";
8
+ export { default as Composer } from "./parts/Composer.svelte";
9
+ export { default as WorkCard } from "./parts/WorkCard.svelte";
10
+ export { default as TextView } from "./parts/TextView.svelte";
11
+ export { default as RevealText } from "./parts/RevealText.svelte";
12
+ export { default as ThinkingOrb } from "./parts/ThinkingOrb.svelte";
13
+ export { default as StagesThinking } from "./parts/StagesThinking.svelte";
14
+ export { default as Small } from "./parts/Small.svelte";
15
+ export { default as BlockView } from "./blocks/BlockView.svelte";
16
+ export { default as Chart } from "./blocks/Chart.svelte";
17
+ export { default as ApprovalCard } from "./cards/ApprovalCard.svelte";
18
+ export { default as Cards } from "./cards/Cards.svelte";
19
+ export { default as SecretField } from "./cards/SecretField.svelte";
20
+ export { default as PinPad } from "./slots/PinPad.svelte";
21
+ export { default as Dialog } from "./slots/Dialog.svelte";
22
+ export { default as History } from "./pages/History.svelte";
23
+ export { default as Abilities } from "./pages/Abilities.svelte";
24
+ export { default as KnowledgePage } from "./pages/KnowledgePage.svelte";
25
+ export { default as LearnedPage } from "./pages/LearnedPage.svelte";
26
+ export { default as FindingsPage } from "./pages/FindingsPage.svelte";
27
+ export { default as RoutineWizard } from "./pages/RoutineWizard.svelte";
28
+ export { default as ActionsPage } from "./pages/ActionsPage.svelte";
29
+ export { default as SlotPicker } from "./pages/SlotPicker.svelte";
30
+ export { default as CsvButton } from "./blocks/CsvButton.svelte";
31
+ export { default as PdfButton } from "./parts/PdfButton.svelte";
32
+ export { pageTexts, pageKeys } from "./pages/texts.js";
33
+ export { browserHost } from "./browser.js";
34
+ export type { BrowserHostOptions } from "./browser.js";
35
+ export { provideUi, useUi, cls, sty, override, blockOverride } from "./context.js";
36
+ export type { AssistantUi, Overrides } from "./context.js";
37
+ export { AssistantState, Ticker } from "./store.svelte.js";
38
+ export { themeVars, LIGHT, DARK } from "./theme.js";
package/dist/index.js ADDED
@@ -0,0 +1,36 @@
1
+ /** @g9tm/aiassistant-svelte: Svelte 5 components of the G9 AI assistant, customisable in five layers. */
2
+ export { default as AssistantRoot } from "./AssistantRoot.svelte";
3
+ export { default as AssistantPanel } from "./surfaces/AssistantPanel.svelte";
4
+ export { default as AssistantChat } from "./surfaces/AssistantChat.svelte";
5
+ export { default as Surfaces } from "./surfaces/Surfaces.svelte";
6
+ export { default as MessageList } from "./parts/MessageList.svelte";
7
+ export { default as Message } from "./parts/Message.svelte";
8
+ export { default as Composer } from "./parts/Composer.svelte";
9
+ export { default as WorkCard } from "./parts/WorkCard.svelte";
10
+ export { default as TextView } from "./parts/TextView.svelte";
11
+ export { default as RevealText } from "./parts/RevealText.svelte";
12
+ export { default as ThinkingOrb } from "./parts/ThinkingOrb.svelte";
13
+ export { default as StagesThinking } from "./parts/StagesThinking.svelte";
14
+ export { default as Small } from "./parts/Small.svelte";
15
+ export { default as BlockView } from "./blocks/BlockView.svelte";
16
+ export { default as Chart } from "./blocks/Chart.svelte";
17
+ export { default as ApprovalCard } from "./cards/ApprovalCard.svelte";
18
+ export { default as Cards } from "./cards/Cards.svelte";
19
+ export { default as SecretField } from "./cards/SecretField.svelte";
20
+ export { default as PinPad } from "./slots/PinPad.svelte";
21
+ export { default as Dialog } from "./slots/Dialog.svelte";
22
+ export { default as History } from "./pages/History.svelte";
23
+ export { default as Abilities } from "./pages/Abilities.svelte";
24
+ export { default as KnowledgePage } from "./pages/KnowledgePage.svelte";
25
+ export { default as LearnedPage } from "./pages/LearnedPage.svelte";
26
+ export { default as FindingsPage } from "./pages/FindingsPage.svelte";
27
+ export { default as RoutineWizard } from "./pages/RoutineWizard.svelte";
28
+ export { default as ActionsPage } from "./pages/ActionsPage.svelte";
29
+ export { default as SlotPicker } from "./pages/SlotPicker.svelte";
30
+ export { default as CsvButton } from "./blocks/CsvButton.svelte";
31
+ export { default as PdfButton } from "./parts/PdfButton.svelte";
32
+ export { pageTexts, pageKeys } from "./pages/texts.js";
33
+ export { browserHost } from "./browser.js";
34
+ export { provideUi, useUi, cls, sty, override, blockOverride } from "./context.js";
35
+ export { AssistantState, Ticker } from "./store.svelte.js";
36
+ export { themeVars, LIGHT, DARK } from "./theme.js";
@@ -0,0 +1,6 @@
1
+ <script lang="ts">
2
+ import Page from "./Page.svelte";
3
+ let { onopen = undefined }: { onopen?: ((id: string) => void) | undefined } = $props();
4
+ </script>
5
+
6
+ <Page kind="abilities" {onopen} />
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((id: string) => void) | undefined;
3
+ };
4
+ declare const Abilities: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type Abilities = ReturnType<typeof Abilities>;
6
+ export default Abilities;
@@ -0,0 +1,6 @@
1
+ <script lang="ts">
2
+ import Page from "./Page.svelte";
3
+ let { onopen = undefined }: { onopen?: ((id: string) => void) | undefined } = $props();
4
+ </script>
5
+
6
+ <Page kind="actions" {onopen} />
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((id: string) => void) | undefined;
3
+ };
4
+ declare const ActionsPage: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type ActionsPage = ReturnType<typeof ActionsPage>;
6
+ export default ActionsPage;
@@ -0,0 +1,53 @@
1
+ <script lang="ts">
2
+ // The manager's pages (07 phases 11-12): capabilities, usage, audit, models, connections, suggestions and gates,
3
+ // with a scope picker that says where a new provider or server goes (this app, or the whole installation).
4
+ import Page from "./Page.svelte";
5
+ import AdminCapabilities from "./admin/AdminCapabilities.svelte";
6
+ import AdminUsage from "./admin/AdminUsage.svelte";
7
+ import AdminAudit from "./admin/AdminAudit.svelte";
8
+ import AdminModels from "./admin/AdminModels.svelte";
9
+ import AdminConnections from "./admin/AdminConnections.svelte";
10
+ import AdminFeedback from "./admin/AdminFeedback.svelte";
11
+ import { AdminClient, adminTexts } from "@g9tm/aiassistant/admin";
12
+ import { cls, useUi } from "../context.js";
13
+
14
+ let { onopen = undefined }: { onopen?: ((id: string) => void) | undefined } = $props();
15
+ const ui = useUi();
16
+ const t = adminTexts(ui.texts).t;
17
+ type Tab = "gates" | "capabilities" | "usage" | "audit" | "models" | "connections" | "feedback";
18
+ const tabs: Tab[] = ["capabilities", "models", "connections", "usage", "audit", "feedback", "gates"];
19
+ let tab = $state<Tab>("capabilities");
20
+ let level = $state("app");
21
+ let chain = $state<{ level: string; space: string | null; user: string | null }[]>([]);
22
+ $effect(() => { new AdminClient(ui.assistant.api).scopes().then((c) => { chain = c; }, () => { chain = []; }); });
23
+ </script>
24
+
25
+ <div class={cls(ui, "panel.body")} data-page="admin">
26
+ <div role="tablist" class="g9a-tabs">
27
+ {#each tabs as x (x)}
28
+ <button type="button" role="tab" aria-selected={tab === x} onclick={() => { tab = x; }}>{t("admin." + x)}</button>
29
+ {/each}
30
+ </div>
31
+ {#if tab === "models" || tab === "connections"}
32
+ <label class="g9a-row">{t("admin.scope")}
33
+ <select bind:value={level}>
34
+ {#each chain.filter((c) => c.level === "app" || c.level === "general") as c (c.level)}<option value={c.level}>{c.level}</option>{/each}
35
+ </select>
36
+ </label>
37
+ {/if}
38
+ {#if tab === "capabilities"}
39
+ <AdminCapabilities />
40
+ {:else if tab === "models"}
41
+ <AdminModels {level} />
42
+ {:else if tab === "connections"}
43
+ <AdminConnections {level} />
44
+ {:else if tab === "usage"}
45
+ <AdminUsage />
46
+ {:else if tab === "audit"}
47
+ <AdminAudit />
48
+ {:else if tab === "feedback"}
49
+ <AdminFeedback />
50
+ {:else}
51
+ <Page kind="admin" {onopen} />
52
+ {/if}
53
+ </div>
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((id: string) => void) | undefined;
3
+ };
4
+ declare const AdminPage: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type AdminPage = ReturnType<typeof AdminPage>;
6
+ export default AdminPage;
@@ -0,0 +1,6 @@
1
+ <script lang="ts">
2
+ import Page from "./Page.svelte";
3
+ let { onopen = undefined }: { onopen?: ((id: string) => void) | undefined } = $props();
4
+ </script>
5
+
6
+ <Page kind="findings" {onopen} />
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((id: string) => void) | undefined;
3
+ };
4
+ declare const FindingsPage: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type FindingsPage = ReturnType<typeof FindingsPage>;
6
+ export default FindingsPage;
@@ -0,0 +1,96 @@
1
+ <script lang="ts">
2
+ // The person's conversations in four tabs with their counts (today, earlier, pinned, waiting for a code), one search
3
+ // over all of them, pin or unpin, delete after a confirmation, and open. The rows and tabs come from the core's
4
+ // page helpers; this only draws them.
5
+ import { HISTORY_TABS, historyTabs, pageCalls, readConversations, tabCounts, type ConversationRow, type HistoryTab } from "@g9tm/aiassistant/pages";
6
+ import { faDigits } from "@g9tm/aiassistant";
7
+ import { cls, override, useUi } from "../context.js";
8
+ import Dialog from "../slots/Dialog.svelte";
9
+ import { pageTexts } from "./texts.js";
10
+
11
+ let { onopen = undefined }: { onopen?: ((conversationId: string) => void) | undefined } = $props();
12
+ const ui = useUi();
13
+ const t = pageTexts(ui.texts).t;
14
+ const api = ui.assistant.api;
15
+ const Confirm = $derived(override(ui, "Dialog") ?? Dialog);
16
+ let rows = $state<ConversationRow[]>([]);
17
+ let failed = $state(false);
18
+ let search = $state("");
19
+ let tab = $state<HistoryTab>("today");
20
+ let removing = $state<ConversationRow | null>(null);
21
+ let asking = $state(false);
22
+ const digits = (n: number): string => (ui.texts.locale === "fa" ? faDigits(String(n)) : String(n));
23
+
24
+ async function load(): Promise<void> {
25
+ try {
26
+ rows = readConversations(await api.conversations());
27
+ failed = false;
28
+ } catch {
29
+ failed = true;
30
+ }
31
+ }
32
+ $effect(() => { void load(); });
33
+
34
+ const tabs = $derived(historyTabs(rows, new Date(), search));
35
+ const counts = $derived(tabCounts(tabs));
36
+ const shown = $derived(new Map(Object.entries(tabs)).get(tab) ?? []);
37
+
38
+ async function call(work: Promise<unknown>): Promise<void> {
39
+ try {
40
+ await work;
41
+ } catch {
42
+ failed = true;
43
+ }
44
+ await load();
45
+ }
46
+
47
+ function askRemove(row: ConversationRow): void {
48
+ removing = row;
49
+ asking = true;
50
+ }
51
+
52
+ async function remove(): Promise<void> {
53
+ const row = removing;
54
+ asking = false;
55
+ removing = null;
56
+ if (row) await call(pageCalls.remove(api, row.id));
57
+ }
58
+
59
+ async function open(id: string): Promise<void> {
60
+ await ui.assistant.open(id);
61
+ onopen?.(id);
62
+ }
63
+ </script>
64
+
65
+ <div class={cls(ui, "panel.body") + " g9a-history"} data-page="history">
66
+ {#if failed}<p class="g9a-note" role="alert">{t("error.network")}</p>{/if}
67
+ <input type="search" bind:value={search} placeholder={t("history.search")} aria-label={t("history.search")} />
68
+ <div role="tablist" class="g9a-tabs" aria-label={t("history.tabs")}>
69
+ {#each HISTORY_TABS as x (x)}
70
+ <button type="button" role="tab" data-tab={x} aria-selected={tab === x} onclick={() => { tab = x; }}>
71
+ {t("history." + x)} <span class="g9a-count">{digits(new Map(Object.entries(counts)).get(x) ?? 0)}</span>
72
+ </button>
73
+ {/each}
74
+ </div>
75
+ <div role="tabpanel" data-tab={tab}><ul class="g9a-history__list">
76
+ {#each shown as row (row.id)}
77
+ <li class="g9a-history__row" data-id={row.id} data-pinned={row.pinned} data-waiting={row.waiting}>
78
+ <button type="button" class="g9a-history__open" onclick={() => open(row.id)}>{row.title || t("history.untitled")}</button>
79
+ <button type="button" class="g9a-history__pin" aria-pressed={row.pinned} onclick={() => call(pageCalls.pin(api, row.id, !row.pinned))}>
80
+ {row.pinned ? t("history.unpin") : t("history.pin")}
81
+ </button>
82
+ <button type="button" class="g9a-history__delete" onclick={() => askRemove(row)}>{t("history.delete")}</button>
83
+ </li>
84
+ {:else}
85
+ <li class="g9a-note">{t("history.empty")}</li>
86
+ {/each}
87
+ </ul></div>
88
+ <Confirm bind:open={asking} label={t("history.delete_confirm")}>
89
+ <p>{t("history.delete_confirm")}</p>
90
+ <p><strong>{removing?.title || t("history.untitled")}</strong></p>
91
+ <div class="g9a-row">
92
+ <button type="button" class="g9a-history__delete-yes" onclick={remove}>{t("history.delete")}</button>
93
+ <button type="button" onclick={() => { asking = false; removing = null; }}>{t("no")}</button>
94
+ </div>
95
+ </Confirm>
96
+ </div>
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((conversationId: string) => void) | undefined;
3
+ };
4
+ declare const History: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type History = ReturnType<typeof History>;
6
+ export default History;
@@ -0,0 +1,6 @@
1
+ <script lang="ts">
2
+ import Page from "./Page.svelte";
3
+ let { onopen = undefined }: { onopen?: ((id: string) => void) | undefined } = $props();
4
+ </script>
5
+
6
+ <Page kind="knowledge" {onopen} />
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((id: string) => void) | undefined;
3
+ };
4
+ declare const KnowledgePage: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type KnowledgePage = ReturnType<typeof KnowledgePage>;
6
+ export default KnowledgePage;
@@ -0,0 +1,6 @@
1
+ <script lang="ts">
2
+ import Page from "./Page.svelte";
3
+ let { onopen = undefined }: { onopen?: ((id: string) => void) | undefined } = $props();
4
+ </script>
5
+
6
+ <Page kind="learned" {onopen} />
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((id: string) => void) | undefined;
3
+ };
4
+ declare const LearnedPage: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type LearnedPage = ReturnType<typeof LearnedPage>;
6
+ export default LearnedPage;
@@ -0,0 +1,82 @@
1
+ <script lang="ts" module>
2
+ export type PageKind = "history" | "abilities" | "knowledge" | "learned" | "findings" | "routines" | "actions" | "admin";
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ // The assistant's pages, each from its own endpoint, read with type checks only.
7
+ import { arr, num, obj, str } from "@g9tm/aiassistant";
8
+ import { cls, override, useUi } from "../context.js";
9
+ import History from "./History.svelte";
10
+ import RoutineWizard from "./RoutineWizard.svelte";
11
+
12
+ let { kind, onopen = undefined }: { kind: PageKind; onopen?: ((conversationId: string) => void) | undefined } = $props();
13
+ const ui = useUi();
14
+ const t = ui.texts.t;
15
+ const api = ui.assistant.api;
16
+ let data = $state<Record<string, unknown>>({});
17
+ let error = $state<string | null>(null);
18
+ let purpose = $state("");
19
+ let code = $state("");
20
+ let token = $state<string | null>(null);
21
+
22
+ const PATHS: Record<PageKind, string> = {
23
+ history: "conversations", abilities: "capabilities", knowledge: "knowledge", learned: "learned/dashboard?capability=chat",
24
+ findings: "findings", routines: "routines", actions: "conversations", admin: "gates",
25
+ };
26
+
27
+ async function load(): Promise<void> {
28
+ try {
29
+ data = obj(await api.get(new Map(Object.entries(PATHS)).get(kind) ?? ""));
30
+ error = null;
31
+ } catch (e) {
32
+ error = e instanceof Error ? e.message : "error.network";
33
+ }
34
+ }
35
+ // History and the routine wizard are their own (replaceable) pages and read their own data.
36
+ const Own = $derived(kind === "history" ? override(ui, "History") ?? History : kind === "routines" ? override(ui, "RoutineWizard") ?? RoutineWizard : null);
37
+ $effect(() => { if (kind !== "history" && kind !== "routines") void load(); });
38
+ async function act(path: string, body?: unknown): Promise<void> {
39
+ await api.post(path, body).catch(() => undefined);
40
+ await load();
41
+ }
42
+ </script>
43
+
44
+ {#if Own}
45
+ <Own {onopen} />
46
+ {:else}
47
+ <div class={cls(ui, "panel.body")} data-page={kind}>
48
+ {#if error}<p class="g9a-note" role="alert">{t("error.network")}</p>{/if}
49
+ {#if kind === "abilities"}
50
+ {#each arr(data["groups"]).map(obj) as g (str(g["name"]))}
51
+ <h4>{str(g["title"], str(g["name"]))}</h4>
52
+ <ul>{#each arr(g["tools"]).map(obj) as tool (str(tool["name"]))}<li>{str(tool["title"], str(tool["name"]))}</li>{/each}</ul>
53
+ {/each}
54
+ {#each arr(data["closed"]).map(obj) as c (str(c["name"]))}<p class="g9a-note">{str(c["name"])} · {str(c["layer"])}</p>{/each}
55
+ {:else if kind === "knowledge"}
56
+ <ul>{#each arr(data["notes"] ?? data["items"]).map(obj) as n, i (i)}<li>{str(n["text"], str(n["name"]))}</li>{/each}</ul>
57
+ {:else if kind === "learned"}
58
+ <p>{Math.round(num(data["modelFreeRate"]) * 100)}% · {num(data["tokensSaved"])}</p>
59
+ <ul>{#each arr(data["items"]).map(obj) as it, i (i)}<li data-status={str(it["status"])}>{str(it["signature"])} · {str(it["status"])} · {num(it["hits"])}</li>{/each}</ul>
60
+ {:else if kind === "findings"}
61
+ <ul>
62
+ {#each arr(data["findings"]).map(obj) as f (str(f["id"]))}
63
+ <li>
64
+ {str(obj(f["title"])["text"], t(str(obj(f["title"])["key"])))}
65
+ <button type="button" onclick={() => act("findings/" + encodeURIComponent(str(f["id"])) + "/fixed")}>✓</button>
66
+ <button type="button" onclick={() => act("findings/" + encodeURIComponent(str(f["id"])) + "/dismiss", { reason: "—" })}>{t("no")}</button>
67
+ </li>
68
+ {/each}
69
+ </ul>
70
+ {:else if kind === "actions"}
71
+ <ul>{#each [...ui.assistant.snapshot().plans.values()].filter((p) => p.answered === "confirmed") as p (p.id)}<li>{p.title} <button type="button" onclick={() => ui.assistant.undo(p.id)}>{t("undo.all")}</button></li>{/each}</ul>
72
+ {:else if kind === "admin"}
73
+ <form onsubmit={async (e) => { e.preventDefault(); const r = obj(await api.post("gates", { purpose, code }).catch(() => null)); token = str(r["token"]) || null; purpose = ""; code = ""; await load(); }}>
74
+ <input bind:value={purpose} placeholder="purpose" aria-label="purpose" />
75
+ <input bind:value={code} type="password" placeholder={t("card.code")} aria-label={t("card.code")} />
76
+ <button type="submit">+</button>
77
+ </form>
78
+ {#if token}<p class="g9a-note"><code>{token}</code></p>{/if}
79
+ <ul>{#each arr(data["gates"]).map(obj) as g (str(g["id"]))}<li data-revoked={g["revoked"] === true}>{str(g["purpose"])} · {num(g["callsToday"])} <button type="button" onclick={async () => { await api.delete("gates/" + encodeURIComponent(str(g["id"]))); await load(); }}>×</button></li>{/each}</ul>
80
+ {/if}
81
+ </div>
82
+ {/if}
@@ -0,0 +1,8 @@
1
+ export type PageKind = "history" | "abilities" | "knowledge" | "learned" | "findings" | "routines" | "actions" | "admin";
2
+ type $$ComponentProps = {
3
+ kind: PageKind;
4
+ onopen?: ((conversationId: string) => void) | undefined;
5
+ };
6
+ declare const Page: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type Page = ReturnType<typeof Page>;
8
+ export default Page;