@jskit-ai/assistant-runtime 0.1.163 → 0.1.164
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.
|
@@ -18,10 +18,11 @@ function switchWorkspace() { router.push({ query: { workspace: workspace.value =
|
|
|
18
18
|
<template>
|
|
19
19
|
<v-app>
|
|
20
20
|
<v-main>
|
|
21
|
-
<div class="fixture">
|
|
21
|
+
<div class="fixture" :class="{ 'fixture--page': route.query.page === '1' }">
|
|
22
22
|
<nav aria-label="Fixture controls">
|
|
23
23
|
<button @click="switchWorkspace">Switch workspace</button>
|
|
24
24
|
<button @click="mergeContext({ user: { id: context.user.id === '1' ? '2' : '1' } })">Switch user</button>
|
|
25
|
+
<button @click="mergeContext({ workspace: { id: '42', slug: workspace, name: 'Workspace metadata loaded' } })">Load workspace metadata</button>
|
|
25
26
|
<button @click="surface = surface === 'admin' ? 'console' : 'admin'">Switch surface</button>
|
|
26
27
|
<button @click="mounted = !mounted">Toggle view</button>
|
|
27
28
|
<button @click="second = !second">Second instance</button>
|
|
@@ -44,4 +45,6 @@ html, body, #app { height: 100%; margin: 0; }
|
|
|
44
45
|
.fixture__panes { display: flex; flex: 1 1 auto; min-height: 0; min-width: 0; }
|
|
45
46
|
.fixture__panes > * { flex: 1 1 0; min-width: 0; }
|
|
46
47
|
.fixture__panes--compact { width: min(360px, 100%); }
|
|
48
|
+
.fixture--page { height: auto; }
|
|
49
|
+
.fixture--page .fixture__panes { display: block; }
|
|
47
50
|
</style>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/assistant-runtime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.164",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -11,15 +11,15 @@
|
|
|
11
11
|
"./server/actionIds": "./src/server/actionIds.js"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@jskit-ai/assistant-core": "0.1.
|
|
15
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
14
|
+
"@jskit-ai/assistant-core": "0.1.171",
|
|
15
|
+
"@jskit-ai/database-runtime": "0.1.193",
|
|
16
16
|
"json-rest-schema": "^1.0.17"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
20
|
-
"@jskit-ai/http-web": "0.1.
|
|
21
|
-
"@jskit-ai/kernel": "0.1.
|
|
22
|
-
"@jskit-ai/shell-web": "0.1.
|
|
19
|
+
"@jskit-ai/http-runtime": "0.1.191",
|
|
20
|
+
"@jskit-ai/http-web": "0.1.38",
|
|
21
|
+
"@jskit-ai/kernel": "0.1.193",
|
|
22
|
+
"@jskit-ai/shell-web": "0.1.197",
|
|
23
23
|
"@tanstack/vue-query": "^5.90.5",
|
|
24
24
|
"vue": "^3.5.13",
|
|
25
25
|
"vuetify": "^4.0.0"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { computed, reactive, ref, toRef } from "vue";
|
|
2
|
+
import { computed, onMounted, onScopeDispose, reactive, ref, toRef, watchPostEffect } from "vue";
|
|
3
3
|
import { AssistantConversationElement } from "@jskit-ai/assistant-core/client/conversation";
|
|
4
4
|
import { useAssistantRuntime } from "../composables/useAssistantRuntime.js";
|
|
5
5
|
import { mapAssistantConversationTurns } from "../support/assistantRuntimeState.js";
|
|
@@ -15,7 +15,22 @@ const props = defineProps({
|
|
|
15
15
|
const runtime = useAssistantRuntime({ surfaceId: toRef(props, "surfaceId") });
|
|
16
16
|
const state = reactive(runtime.state);
|
|
17
17
|
const conversation = ref(null);
|
|
18
|
+
const root = ref(null);
|
|
18
19
|
const historyOpen = ref(false);
|
|
20
|
+
// Page mode owns viewport sizing; compact mode fills the host's bounded pane.
|
|
21
|
+
onMounted(() => {
|
|
22
|
+
const syncPageOffset = () => {
|
|
23
|
+
if (props.layout === "page") root.value.style.setProperty("--assistant-page-top", `${Math.max(0, root.value.getBoundingClientRect().top)}px`);
|
|
24
|
+
};
|
|
25
|
+
const observer = new ResizeObserver(syncPageOffset);
|
|
26
|
+
observer.observe(root.value.parentElement);
|
|
27
|
+
window.addEventListener("resize", syncPageOffset);
|
|
28
|
+
watchPostEffect(syncPageOffset);
|
|
29
|
+
onScopeDispose(() => {
|
|
30
|
+
observer.disconnect();
|
|
31
|
+
window.removeEventListener("resize", syncPageOffset);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
19
34
|
const selectedConversation = computed(() => state.conversationHistory.find((entry) => String(entry.id) === state.activeConversationId));
|
|
20
35
|
const turns = computed(() => mapAssistantConversationTurns(state.messages, state.pendingToolEvents, props));
|
|
21
36
|
const adapter = computed(() => ({
|
|
@@ -67,7 +82,7 @@ defineExpose({ focus: () => conversation.value?.focus() });
|
|
|
67
82
|
</script>
|
|
68
83
|
|
|
69
84
|
<template>
|
|
70
|
-
<section class="assistant-surface" :class="`assistant-surface--${layout}`" :aria-label="assistantLabel">
|
|
85
|
+
<section ref="root" class="assistant-surface" :class="`assistant-surface--${layout}`" :aria-label="assistantLabel">
|
|
71
86
|
<div class="assistant-surface__toolbar">
|
|
72
87
|
<span class="assistant-surface__title">{{ selectedConversation?.title || 'New conversation' }}</span>
|
|
73
88
|
<v-btn variant="text" @click="historyOpen = true">Conversations</v-btn>
|
|
@@ -122,7 +137,7 @@ defineExpose({ focus: () => conversation.value?.focus() });
|
|
|
122
137
|
|
|
123
138
|
<style scoped>
|
|
124
139
|
.assistant-surface { display: flex; flex-direction: column; height: 100%; max-height: 100%; min-height: 0; min-width: 0; overflow: hidden; gap: .5rem; }
|
|
125
|
-
.assistant-surface--page { padding: .5rem; }
|
|
140
|
+
.assistant-surface--page { padding: .5rem; height: calc(100dvh - var(--assistant-page-top, 0px) - var(--v-layout-bottom, 0px) - 1rem - env(safe-area-inset-bottom, 0px)); }
|
|
126
141
|
.assistant-surface__toolbar { display: flex; align-items: center; flex-wrap: wrap; flex: 0 0 auto; gap: .25rem; }
|
|
127
142
|
.assistant-surface__title { flex: 1 1 8rem; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .875rem; }
|
|
128
143
|
.assistant-surface__conversation { flex: 1 1 auto; }
|
|
@@ -168,14 +168,11 @@ function useAssistantRuntime({ api = null, surfaceId = "" } = {}) {
|
|
|
168
168
|
return {
|
|
169
169
|
targetSurfaceId: normalizeText(assistantSurface.value?.targetSurfaceId).toLowerCase(),
|
|
170
170
|
userId: normalizeRecordId(placementSnapshot.value?.user?.id, { fallback: null }),
|
|
171
|
-
workspaceSlug
|
|
172
|
-
workspaceId: assistantSurface.value?.runtimeSurfaceRequiresWorkspace
|
|
173
|
-
? normalizeRecordId(placementSnapshot.value?.workspace?.id, { fallback: null })
|
|
174
|
-
: null
|
|
171
|
+
workspaceSlug
|
|
175
172
|
};
|
|
176
173
|
});
|
|
177
174
|
const hasRuntimeScope = computed(() =>
|
|
178
|
-
Boolean(assistantSurface.value) &&
|
|
175
|
+
Boolean(assistantSurface.value && runtimeScope.value.userId) &&
|
|
179
176
|
(assistantSurface.value?.runtimeSurfaceRequiresWorkspace ? Boolean(runtimeScope.value.workspaceSlug) : true)
|
|
180
177
|
);
|
|
181
178
|
|
|
@@ -224,3 +224,37 @@ test("history restores preserve new typing and reject obsolete pages and failed
|
|
|
224
224
|
await stopProcess(vite);
|
|
225
225
|
}
|
|
226
226
|
});
|
|
227
|
+
|
|
228
|
+
test("page assistant bounds long history in a document layout and preserves drafts when workspace metadata arrives", {
|
|
229
|
+
skip: process.env.JSKIT_ASSISTANT_RUNTIME_BROWSER_INTEGRATION !== "1",
|
|
230
|
+
timeout: 60_000
|
|
231
|
+
}, async () => {
|
|
232
|
+
const vite = await startViteFixture({ fixtureRoot });
|
|
233
|
+
const browser = await chromium.launch(createChromiumLaunchOptions());
|
|
234
|
+
const page = await browser.newPage();
|
|
235
|
+
try {
|
|
236
|
+
await page.goto(`${vite.baseURL}/?page=1`);
|
|
237
|
+
await page.getByRole("button", { name: "Conversations", exact: true }).click();
|
|
238
|
+
await page.getByRole("dialog", { name: "Conversations", exact: true }).getByText("Saved conversation", { exact: true }).click();
|
|
239
|
+
await expect(page.getByText("Saved 1 message 70.", { exact: false })).toBeVisible();
|
|
240
|
+
const input = page.getByRole("textbox", { name: "Message AI assistant" });
|
|
241
|
+
for (const viewport of [{ width: 390, height: 844 }, { width: 800, height: 800 }, { width: 1280, height: 600 }]) {
|
|
242
|
+
await page.setViewportSize(viewport);
|
|
243
|
+
await expect.poll(async () => {
|
|
244
|
+
const bounds = await input.boundingBox();
|
|
245
|
+
return bounds.y + bounds.height;
|
|
246
|
+
}).toBeLessThanOrEqual(viewport.height);
|
|
247
|
+
assert.ok(await page.getByLabel("Conversation messages", { exact: true }).evaluate(element => element.scrollHeight > element.clientHeight));
|
|
248
|
+
}
|
|
249
|
+
await input.fill("Keep this draft while workspace details load.");
|
|
250
|
+
await input.evaluate(element => { element.focus(); element.setSelectionRange(5, 9); });
|
|
251
|
+
await page.getByRole("button", { name: "Load workspace metadata" }).evaluate(element => element.click());
|
|
252
|
+
await expect(input).toHaveValue("Keep this draft while workspace details load.");
|
|
253
|
+
await expect(input).toBeFocused();
|
|
254
|
+
assert.deepEqual(await input.evaluate(element => [element.selectionStart, element.selectionEnd]), [5, 9]);
|
|
255
|
+
await expect(page.getByText("Saved 1 message 70.", { exact: false })).toBeVisible();
|
|
256
|
+
} finally {
|
|
257
|
+
await browser.close();
|
|
258
|
+
await stopProcess(vite);
|
|
259
|
+
}
|
|
260
|
+
});
|