@d4y/agent-runtime-nuxt 0.1.0 → 0.1.1
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.
- package/dist/module.json +1 -1
- package/dist/runtime/frontend.d.ts +5 -0
- package/dist/runtime/frontend.js +14 -0
- package/dist/runtime/server/utils/agent-runtime.d.ts +1 -1
- package/dist/runtime/server/utils/agent-runtime.js +1 -1
- package/dist/runtime/shared.d.ts +16 -0
- package/dist/runtime/shared.js +22 -0
- package/package.json +22 -11
- package/src/frontend.ts +0 -16
- package/src/module.ts +0 -155
- package/src/nitro-globals.d.ts +0 -8
- package/src/runtime/components/AgentRuntimeArtifactPreview.vue +0 -192
- package/src/runtime/composables/useAgentRuntime.ts +0 -527
- package/src/runtime/composables/useAgentRuntimeMarkdown.ts +0 -145
- package/src/runtime/server/api/app.get.ts +0 -50
- package/src/runtime/server/api/conversations/[id]/abort.post.ts +0 -26
- package/src/runtime/server/api/conversations/[id]/env.post.ts +0 -34
- package/src/runtime/server/api/conversations/[id]/files/raw/[...path].get.ts +0 -48
- package/src/runtime/server/api/conversations/[id]/files.get.ts +0 -33
- package/src/runtime/server/api/conversations/[id]/history.get.ts +0 -20
- package/src/runtime/server/api/conversations/[id]/messages.post.ts +0 -29
- package/src/runtime/server/api/conversations/[id]/stream.get.ts +0 -41
- package/src/runtime/server/api/conversations/[id].delete.ts +0 -22
- package/src/runtime/server/api/conversations.post.ts +0 -26
- package/src/runtime/server/utils/agent-runtime.ts +0 -33
- package/src/runtime/utils/files.ts +0 -78
- package/src/shared.ts +0 -46
- package/src/vue-shim.d.ts +0 -6
package/dist/module.json
CHANGED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { AgentRuntimeMarkdownRenderOptions } from './composables/useAgentRuntimeMarkdown.js';
|
|
2
|
+
export type { AgentRuntimeFileLike, AgentRuntimeFilePreviewKind } from './utils/files.js';
|
|
3
|
+
export { default as AgentRuntimeArtifactPreview } from './components/AgentRuntimeArtifactPreview.vue.js';
|
|
4
|
+
export { createAgentRuntimeMarkdownRenderer, useAgentRuntimeMarkdown } from './composables/useAgentRuntimeMarkdown.js';
|
|
5
|
+
export { canPreviewAgentRuntimeFileInline, getAgentRuntimeFilePreviewKind, isAgentRuntimeImageMimeType, isAgentRuntimeImagePath, isAgentRuntimePdfMimeType, isAgentRuntimePdfPath, isAgentRuntimeTextMimeType, isAgentRuntimeTextPath, resolveAgentRuntimeWorkspaceUri, toAgentRuntimeWorkspaceRelativePath, } from './utils/files.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { default as AgentRuntimeArtifactPreview } from "./components/AgentRuntimeArtifactPreview.vue";
|
|
2
|
+
export { createAgentRuntimeMarkdownRenderer, useAgentRuntimeMarkdown } from "./composables/useAgentRuntimeMarkdown.js";
|
|
3
|
+
export {
|
|
4
|
+
canPreviewAgentRuntimeFileInline,
|
|
5
|
+
getAgentRuntimeFilePreviewKind,
|
|
6
|
+
isAgentRuntimeImageMimeType,
|
|
7
|
+
isAgentRuntimeImagePath,
|
|
8
|
+
isAgentRuntimePdfMimeType,
|
|
9
|
+
isAgentRuntimePdfPath,
|
|
10
|
+
isAgentRuntimeTextMimeType,
|
|
11
|
+
isAgentRuntimeTextPath,
|
|
12
|
+
resolveAgentRuntimeWorkspaceUri,
|
|
13
|
+
toAgentRuntimeWorkspaceRelativePath
|
|
14
|
+
} from "./utils/files.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentRuntimeResolvedConfig } from '
|
|
1
|
+
import { type AgentRuntimeResolvedConfig } from '../../shared.js';
|
|
2
2
|
/** Resolved server-side configuration for talking to agent-runtime. */
|
|
3
3
|
export type AgentRuntimeRuntime = AgentRuntimeResolvedConfig;
|
|
4
4
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createAgentRuntimeRequestHeaders, resolveAgentRuntimeConfig } from "
|
|
1
|
+
import { createAgentRuntimeRequestHeaders, resolveAgentRuntimeConfig } from "../../shared.js";
|
|
2
2
|
export const agentRuntime = () => {
|
|
3
3
|
try {
|
|
4
4
|
return resolveAgentRuntimeConfig(useRuntimeConfig().agentRuntime);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface AgentRuntimeConfigInput {
|
|
2
|
+
baseUrl?: string | null;
|
|
3
|
+
appKey?: string | null;
|
|
4
|
+
appId?: string | null;
|
|
5
|
+
}
|
|
6
|
+
export interface AgentRuntimeResolvedConfig {
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
appKey: string;
|
|
9
|
+
appId: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const normalizeAgentRuntimeBaseUrl: (value: string | null | undefined) => string;
|
|
12
|
+
export declare const createScopeFingerprint: (parts: Array<string | null | undefined>) => string;
|
|
13
|
+
export declare const resolveAgentRuntimeConfig: (config: AgentRuntimeConfigInput | null | undefined, options?: {
|
|
14
|
+
defaultAppId?: string;
|
|
15
|
+
}) => AgentRuntimeResolvedConfig;
|
|
16
|
+
export declare const createAgentRuntimeRequestHeaders: (config: Pick<AgentRuntimeResolvedConfig, "appKey">, extra?: Record<string, string>) => Record<string, string>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export const normalizeAgentRuntimeBaseUrl = (value) => String(value || "").trim().replace(/\/+$/, "");
|
|
3
|
+
export const createScopeFingerprint = (parts) => {
|
|
4
|
+
const normalized = parts.map((part) => typeof part === "string" && part.trim().length > 0 ? part.trim() : "none").join("|");
|
|
5
|
+
return createHash("sha256").update(normalized).digest("hex").slice(0, 12);
|
|
6
|
+
};
|
|
7
|
+
export const resolveAgentRuntimeConfig = (config, options) => {
|
|
8
|
+
const appKey = String(config?.appKey || "").trim();
|
|
9
|
+
if (!appKey) {
|
|
10
|
+
throw new Error("[agent-runtime] AGENT_RUNTIME_APP_KEY is not configured");
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
baseUrl: normalizeAgentRuntimeBaseUrl(config?.baseUrl),
|
|
14
|
+
appKey,
|
|
15
|
+
appId: String(config?.appId || options?.defaultAppId || "omnisearch")
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export const createAgentRuntimeRequestHeaders = (config, extra = {}) => ({
|
|
19
|
+
"X-Agent-Runtime-App-Key": config.appKey,
|
|
20
|
+
"content-type": "application/json",
|
|
21
|
+
...extra
|
|
22
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@d4y/agent-runtime-nuxt",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Headless Nuxt module that connects a Nuxt app to an agent-runtime server. Ships server-side proxy routes (so your X-Agent-Runtime-App-Key never leaves the server) and a single composable, useAgentRuntime(), that exposes a typed chat client.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,25 +12,36 @@
|
|
|
12
12
|
"access": "public",
|
|
13
13
|
"registry": "https://registry.npmjs.org/"
|
|
14
14
|
},
|
|
15
|
-
"main": "./
|
|
16
|
-
"types": "./src/module.ts",
|
|
15
|
+
"main": "./dist/module.mjs",
|
|
17
16
|
"exports": {
|
|
18
17
|
".": {
|
|
19
|
-
"types": "./
|
|
20
|
-
"import": "./
|
|
18
|
+
"types": "./dist/module.d.mts",
|
|
19
|
+
"import": "./dist/module.mjs"
|
|
21
20
|
},
|
|
22
21
|
"./frontend": {
|
|
23
|
-
"types": "./
|
|
24
|
-
"import": "./
|
|
22
|
+
"types": "./dist/runtime/frontend.d.ts",
|
|
23
|
+
"import": "./dist/runtime/frontend.js"
|
|
25
24
|
},
|
|
26
25
|
"./shared": {
|
|
27
|
-
"types": "./
|
|
28
|
-
"import": "./
|
|
26
|
+
"types": "./dist/runtime/shared.d.ts",
|
|
27
|
+
"import": "./dist/runtime/shared.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"typesVersions": {
|
|
31
|
+
"*": {
|
|
32
|
+
".": [
|
|
33
|
+
"./dist/module.d.mts"
|
|
34
|
+
],
|
|
35
|
+
"frontend": [
|
|
36
|
+
"./dist/runtime/frontend.d.ts"
|
|
37
|
+
],
|
|
38
|
+
"shared": [
|
|
39
|
+
"./dist/runtime/shared.d.ts"
|
|
40
|
+
]
|
|
29
41
|
}
|
|
30
42
|
},
|
|
31
43
|
"files": [
|
|
32
|
-
"dist"
|
|
33
|
-
"src"
|
|
44
|
+
"dist"
|
|
34
45
|
],
|
|
35
46
|
"keywords": [
|
|
36
47
|
"nuxt",
|
package/src/frontend.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export type { AgentRuntimeMarkdownRenderOptions } from './runtime/composables/useAgentRuntimeMarkdown'
|
|
2
|
-
export type { AgentRuntimeFileLike, AgentRuntimeFilePreviewKind } from './runtime/utils/files'
|
|
3
|
-
export { default as AgentRuntimeArtifactPreview } from './runtime/components/AgentRuntimeArtifactPreview.vue'
|
|
4
|
-
export { createAgentRuntimeMarkdownRenderer, useAgentRuntimeMarkdown } from './runtime/composables/useAgentRuntimeMarkdown'
|
|
5
|
-
export {
|
|
6
|
-
canPreviewAgentRuntimeFileInline,
|
|
7
|
-
getAgentRuntimeFilePreviewKind,
|
|
8
|
-
isAgentRuntimeImageMimeType,
|
|
9
|
-
isAgentRuntimeImagePath,
|
|
10
|
-
isAgentRuntimePdfMimeType,
|
|
11
|
-
isAgentRuntimePdfPath,
|
|
12
|
-
isAgentRuntimeTextMimeType,
|
|
13
|
-
isAgentRuntimeTextPath,
|
|
14
|
-
resolveAgentRuntimeWorkspaceUri,
|
|
15
|
-
toAgentRuntimeWorkspaceRelativePath,
|
|
16
|
-
} from './runtime/utils/files'
|
package/src/module.ts
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import { defineNuxtModule, createResolver, addImportsDir, addServerHandler, addTypeTemplate } from '@nuxt/kit'
|
|
2
|
-
import { defu } from 'defu'
|
|
3
|
-
|
|
4
|
-
// Re-export the public composable surface so consumers can `import type
|
|
5
|
-
// { UIMessage, AppInfo, … } from '@d4y/agent-runtime-nuxt'` without reaching into
|
|
6
|
-
// `runtime/`. The composable itself is auto-imported via addImportsDir.
|
|
7
|
-
export type {
|
|
8
|
-
UseAgentRuntime,
|
|
9
|
-
UIMessage,
|
|
10
|
-
AppInfo,
|
|
11
|
-
AppAuth,
|
|
12
|
-
AppEnvField,
|
|
13
|
-
FileEntry,
|
|
14
|
-
UiAction,
|
|
15
|
-
SendOptions,
|
|
16
|
-
ChatStatus
|
|
17
|
-
} from './runtime/composables/useAgentRuntime'
|
|
18
|
-
export type { AgentRuntimeMarkdownRenderOptions } from './runtime/composables/useAgentRuntimeMarkdown'
|
|
19
|
-
export type { AgentRuntimeFileLike, AgentRuntimeFilePreviewKind } from './runtime/utils/files'
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Module options for `@d4y/agent-runtime-nuxt`.
|
|
23
|
-
*
|
|
24
|
-
* Every field is optional at config time — unset values fall back to the
|
|
25
|
-
* matching `AGENT_RUNTIME_*` environment variable at runtime, so the same build
|
|
26
|
-
* artifact works across environments without rebuilding.
|
|
27
|
-
*/
|
|
28
|
-
export interface ModuleOptions {
|
|
29
|
-
/**
|
|
30
|
-
* Base URL of the agent-runtime HTTP server (no trailing slash required).
|
|
31
|
-
*
|
|
32
|
-
* Falls back to `process.env.AGENT_RUNTIME_URL` then `http://127.0.0.1:18791`.
|
|
33
|
-
*/
|
|
34
|
-
baseUrl?: string
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* App key used to authenticate against agent-runtime (`X-Agent-Runtime-App-Key` header).
|
|
38
|
-
*
|
|
39
|
-
* **Server-only secret.** Never exposed to the browser; the module only
|
|
40
|
-
* forwards it from the proxy routes it registers.
|
|
41
|
-
*
|
|
42
|
-
* Falls back to `process.env.AGENT_RUNTIME_APP_KEY`. Required at runtime — the
|
|
43
|
-
* module will throw a 500 from any proxy route if missing.
|
|
44
|
-
*/
|
|
45
|
-
appKey?: string
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* App ID to bind every conversation to (must match an app registered in
|
|
49
|
-
* the agent-runtime `apps/` directory).
|
|
50
|
-
*
|
|
51
|
-
* Falls back to `process.env.AGENT_RUNTIME_APP_ID` then `omnisearch`.
|
|
52
|
-
*/
|
|
53
|
-
appId?: string
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* URL prefix for the auto-registered Nitro proxy routes. Change it if you
|
|
57
|
-
* need to disambiguate from another module on the same `/api/...` namespace.
|
|
58
|
-
*
|
|
59
|
-
* Default: `/api/agent-runtime`.
|
|
60
|
-
*/
|
|
61
|
-
apiPrefix?: string
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Public runtime-config shape exposed to the browser (path data only). */
|
|
65
|
-
export interface PublicAgentRuntimeConfig {
|
|
66
|
-
/** Same value as `apiPrefix` from module options; needed by `useAgentRuntime` to build URLs. */
|
|
67
|
-
apiPrefix: string
|
|
68
|
-
/** App ID, used by the composable to scope `localStorage` keys. */
|
|
69
|
-
appId: string
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Private runtime-config shape (server-only). */
|
|
73
|
-
export interface PrivateAgentRuntimeConfig {
|
|
74
|
-
baseUrl: string
|
|
75
|
-
appKey: string
|
|
76
|
-
appId: string
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
declare module '@nuxt/schema' {
|
|
80
|
-
interface RuntimeConfig {
|
|
81
|
-
agentRuntime: PrivateAgentRuntimeConfig
|
|
82
|
-
}
|
|
83
|
-
interface PublicRuntimeConfig {
|
|
84
|
-
agentRuntime: PublicAgentRuntimeConfig
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const DEFAULTS: Required<Pick<ModuleOptions, 'baseUrl' | 'appId' | 'apiPrefix'>> = {
|
|
89
|
-
baseUrl: 'http://127.0.0.1:18791',
|
|
90
|
-
appId: 'omnisearch',
|
|
91
|
-
apiPrefix: '/api/agent-runtime'
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export default defineNuxtModule<ModuleOptions>({
|
|
95
|
-
meta: {
|
|
96
|
-
name: '@d4y/agent-runtime-nuxt',
|
|
97
|
-
configKey: 'agentRuntime',
|
|
98
|
-
compatibility: { nuxt: '>=3.13.0' }
|
|
99
|
-
},
|
|
100
|
-
defaults: {},
|
|
101
|
-
setup(userOptions, nuxt) {
|
|
102
|
-
const resolver = createResolver(import.meta.url)
|
|
103
|
-
|
|
104
|
-
const baseUrl = (userOptions.baseUrl ?? process.env.AGENT_RUNTIME_URL ?? DEFAULTS.baseUrl).replace(/\/+$/, '')
|
|
105
|
-
const appKey = userOptions.appKey ?? process.env.AGENT_RUNTIME_APP_KEY ?? ''
|
|
106
|
-
const appId = userOptions.appId ?? process.env.AGENT_RUNTIME_APP_ID ?? DEFAULTS.appId
|
|
107
|
-
const apiPrefix = (userOptions.apiPrefix ?? DEFAULTS.apiPrefix).replace(/\/+$/, '')
|
|
108
|
-
|
|
109
|
-
nuxt.options.runtimeConfig.agentRuntime = defu(nuxt.options.runtimeConfig.agentRuntime, {
|
|
110
|
-
baseUrl,
|
|
111
|
-
appKey,
|
|
112
|
-
appId
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
nuxt.options.runtimeConfig.public.agentRuntime = defu(nuxt.options.runtimeConfig.public.agentRuntime, {
|
|
116
|
-
apiPrefix,
|
|
117
|
-
appId
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
// Composable auto-import — `useAgentRuntime()` is available everywhere.
|
|
121
|
-
addImportsDir(resolver.resolve('./runtime/composables'))
|
|
122
|
-
|
|
123
|
-
// Generate a `.d.ts` shim that re-exports the public types from the
|
|
124
|
-
// module so IDEs see them without the consumer touching tsconfig paths.
|
|
125
|
-
addTypeTemplate({
|
|
126
|
-
filename: 'types/agent-runtime.d.ts',
|
|
127
|
-
getContents: () => [
|
|
128
|
-
`// Auto-generated by @d4y/agent-runtime-nuxt`,
|
|
129
|
-
`export type { UseAgentRuntime, AppInfo, AppAuth, AppEnvField, FileEntry, UiAction, SendOptions, ChatStatus } from '${resolver.resolve('./runtime/composables/useAgentRuntime')}'`,
|
|
130
|
-
`export type { AgentRuntimeMarkdownRenderOptions } from '${resolver.resolve('./runtime/composables/useAgentRuntimeMarkdown')}'`,
|
|
131
|
-
`export type { AgentRuntimeFileLike, AgentRuntimeFilePreviewKind } from '${resolver.resolve('./runtime/utils/files')}'`,
|
|
132
|
-
''
|
|
133
|
-
].join('\n')
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
// Register every proxy route under the configured prefix.
|
|
137
|
-
const route = (suffix: string, handler: string, method?: 'get' | 'post' | 'delete') =>
|
|
138
|
-
addServerHandler({
|
|
139
|
-
route: `${apiPrefix}${suffix}`,
|
|
140
|
-
method,
|
|
141
|
-
handler: resolver.resolve(`./runtime/server/api/${handler}`)
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
route('/app', 'app.get', 'get')
|
|
145
|
-
route('/conversations', 'conversations.post', 'post')
|
|
146
|
-
route('/conversations/:id', 'conversations/[id].delete', 'delete')
|
|
147
|
-
route('/conversations/:id/history', 'conversations/[id]/history.get', 'get')
|
|
148
|
-
route('/conversations/:id/stream', 'conversations/[id]/stream.get', 'get')
|
|
149
|
-
route('/conversations/:id/messages', 'conversations/[id]/messages.post', 'post')
|
|
150
|
-
route('/conversations/:id/abort', 'conversations/[id]/abort.post', 'post')
|
|
151
|
-
route('/conversations/:id/env', 'conversations/[id]/env.post', 'post')
|
|
152
|
-
route('/conversations/:id/files', 'conversations/[id]/files.get', 'get')
|
|
153
|
-
route('/conversations/:id/files/raw/**', 'conversations/[id]/files/raw/[...path].get', 'get')
|
|
154
|
-
}
|
|
155
|
-
})
|
package/src/nitro-globals.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
declare const defineEventHandler: typeof import('h3').defineEventHandler
|
|
2
|
-
declare const createError: typeof import('h3').createError
|
|
3
|
-
declare const getRouterParam: typeof import('h3').getRouterParam
|
|
4
|
-
declare const setHeader: typeof import('h3').setHeader
|
|
5
|
-
declare const sendStream: typeof import('h3').sendStream
|
|
6
|
-
declare const readBody: typeof import('h3').readBody
|
|
7
|
-
declare const setResponseStatus: typeof import('h3').setResponseStatus
|
|
8
|
-
declare const useRuntimeConfig: () => any
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed, onBeforeUnmount, ref, watch } from 'vue'
|
|
3
|
-
import { getAgentRuntimeFilePreviewKind, type AgentRuntimeFileLike } from '../utils/files'
|
|
4
|
-
|
|
5
|
-
const props = withDefaults(defineProps<{
|
|
6
|
-
file: AgentRuntimeFileLike
|
|
7
|
-
src?: string | null
|
|
8
|
-
resolveSrc?: ((relPath: string) => string | null | undefined) | null
|
|
9
|
-
textMaxChars?: number
|
|
10
|
-
}>(), {
|
|
11
|
-
src: null,
|
|
12
|
-
resolveSrc: null,
|
|
13
|
-
textMaxChars: 120_000,
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
const kind = computed(() => getAgentRuntimeFilePreviewKind(props.file))
|
|
17
|
-
const resolvedSrc = computed(() => {
|
|
18
|
-
if (props.src) {
|
|
19
|
-
return props.src
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (!props.file.relPath || !props.resolveSrc) {
|
|
23
|
-
return null
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return props.resolveSrc(props.file.relPath) ?? null
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
const textContent = ref('')
|
|
30
|
-
const textError = ref<string | null>(null)
|
|
31
|
-
const textLoading = ref(false)
|
|
32
|
-
let textAbortController: AbortController | null = null
|
|
33
|
-
|
|
34
|
-
const abortTextLoad = () => {
|
|
35
|
-
textAbortController?.abort()
|
|
36
|
-
textAbortController = null
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const loadTextPreview = async () => {
|
|
40
|
-
abortTextLoad()
|
|
41
|
-
textContent.value = ''
|
|
42
|
-
textError.value = null
|
|
43
|
-
|
|
44
|
-
if (kind.value !== 'text' || !resolvedSrc.value) {
|
|
45
|
-
textLoading.value = false
|
|
46
|
-
return
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const controller = new AbortController()
|
|
50
|
-
textAbortController = controller
|
|
51
|
-
textLoading.value = true
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const response = await fetch(resolvedSrc.value, {
|
|
55
|
-
signal: controller.signal,
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
if (!response.ok) {
|
|
59
|
-
throw new Error(`Failed to load preview (${response.status})`)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const body = await response.text()
|
|
63
|
-
textContent.value =
|
|
64
|
-
body.length > props.textMaxChars
|
|
65
|
-
? `${body.slice(0, props.textMaxChars).trimEnd()}\n\n…`
|
|
66
|
-
: body
|
|
67
|
-
} catch (error) {
|
|
68
|
-
if (controller.signal.aborted) {
|
|
69
|
-
return
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
textError.value = error instanceof Error ? error.message : 'Failed to load preview'
|
|
73
|
-
} finally {
|
|
74
|
-
if (textAbortController === controller) {
|
|
75
|
-
textAbortController = null
|
|
76
|
-
}
|
|
77
|
-
textLoading.value = false
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
watch([kind, resolvedSrc], () => {
|
|
82
|
-
void loadTextPreview()
|
|
83
|
-
}, { immediate: true })
|
|
84
|
-
|
|
85
|
-
onBeforeUnmount(() => {
|
|
86
|
-
abortTextLoad()
|
|
87
|
-
})
|
|
88
|
-
</script>
|
|
89
|
-
|
|
90
|
-
<template>
|
|
91
|
-
<div class="agent-runtime-artifact-preview">
|
|
92
|
-
<img
|
|
93
|
-
v-if="kind === 'image' && resolvedSrc"
|
|
94
|
-
:src="resolvedSrc"
|
|
95
|
-
:alt="file.name ?? file.relPath ?? 'Preview image'"
|
|
96
|
-
class="agent-runtime-artifact-preview__image">
|
|
97
|
-
|
|
98
|
-
<iframe
|
|
99
|
-
v-else-if="kind === 'pdf' && resolvedSrc"
|
|
100
|
-
:src="resolvedSrc"
|
|
101
|
-
:title="file.name ?? file.relPath ?? 'PDF preview'"
|
|
102
|
-
class="agent-runtime-artifact-preview__frame" />
|
|
103
|
-
|
|
104
|
-
<div v-else-if="kind === 'text'" class="agent-runtime-artifact-preview__text-shell">
|
|
105
|
-
<div v-if="textLoading" class="agent-runtime-artifact-preview__note">
|
|
106
|
-
Loading preview…
|
|
107
|
-
</div>
|
|
108
|
-
<div v-else-if="textError" class="agent-runtime-artifact-preview__note agent-runtime-artifact-preview__note--error">
|
|
109
|
-
{{ textError }}
|
|
110
|
-
</div>
|
|
111
|
-
<pre v-else class="agent-runtime-artifact-preview__text">{{ textContent }}</pre>
|
|
112
|
-
</div>
|
|
113
|
-
|
|
114
|
-
<div v-else class="agent-runtime-artifact-preview__fallback">
|
|
115
|
-
<p class="agent-runtime-artifact-preview__note">
|
|
116
|
-
Preview unavailable for this file type.
|
|
117
|
-
</p>
|
|
118
|
-
<a
|
|
119
|
-
v-if="resolvedSrc"
|
|
120
|
-
:href="resolvedSrc"
|
|
121
|
-
target="_blank"
|
|
122
|
-
rel="noopener noreferrer"
|
|
123
|
-
class="agent-runtime-artifact-preview__link">
|
|
124
|
-
Open file
|
|
125
|
-
</a>
|
|
126
|
-
</div>
|
|
127
|
-
</div>
|
|
128
|
-
</template>
|
|
129
|
-
|
|
130
|
-
<style scoped>
|
|
131
|
-
.agent-runtime-artifact-preview {
|
|
132
|
-
display: block;
|
|
133
|
-
width: 100%;
|
|
134
|
-
min-height: 12rem;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.agent-runtime-artifact-preview__image,
|
|
138
|
-
.agent-runtime-artifact-preview__frame {
|
|
139
|
-
display: block;
|
|
140
|
-
width: 100%;
|
|
141
|
-
border: 0;
|
|
142
|
-
border-radius: 0.875rem;
|
|
143
|
-
background: rgba(15, 23, 42, 0.04);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
.agent-runtime-artifact-preview__image {
|
|
147
|
-
max-height: min(70vh, 48rem);
|
|
148
|
-
object-fit: contain;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
.agent-runtime-artifact-preview__frame {
|
|
152
|
-
min-height: min(78vh, 52rem);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
.agent-runtime-artifact-preview__text-shell,
|
|
156
|
-
.agent-runtime-artifact-preview__fallback {
|
|
157
|
-
min-height: 18rem;
|
|
158
|
-
border-radius: 0.875rem;
|
|
159
|
-
border: 1px solid rgba(15, 23, 42, 0.1);
|
|
160
|
-
background: rgba(15, 23, 42, 0.03);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
.agent-runtime-artifact-preview__text {
|
|
164
|
-
margin: 0;
|
|
165
|
-
max-height: min(72vh, 52rem);
|
|
166
|
-
overflow: auto;
|
|
167
|
-
padding: 1rem;
|
|
168
|
-
font-family: ui-monospace, SFMono-Regular, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
|
169
|
-
font-size: 0.8rem;
|
|
170
|
-
line-height: 1.55;
|
|
171
|
-
white-space: pre-wrap;
|
|
172
|
-
word-break: break-word;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
.agent-runtime-artifact-preview__note {
|
|
176
|
-
padding: 1rem;
|
|
177
|
-
font-size: 0.875rem;
|
|
178
|
-
color: rgba(15, 23, 42, 0.72);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
.agent-runtime-artifact-preview__note--error {
|
|
182
|
-
color: rgb(185, 28, 28);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
.agent-runtime-artifact-preview__link {
|
|
186
|
-
display: inline-flex;
|
|
187
|
-
margin: 0 1rem 1rem;
|
|
188
|
-
color: inherit;
|
|
189
|
-
text-decoration: underline;
|
|
190
|
-
text-underline-offset: 0.18em;
|
|
191
|
-
}
|
|
192
|
-
</style>
|