kapi-ui 0.2.2 → 0.3.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/LICENSE +21 -0
- package/dist/browser/comments.js +531 -0
- package/dist/{hover-panel.js → browser/hover-panel.js} +63 -17
- package/dist/browser/icons.js +35 -0
- package/dist/browser/inspector.js +423 -0
- package/dist/browser/overlay.js +322 -0
- package/dist/browser/socket.js +38 -0
- package/dist/browser/styles/comments.css +246 -0
- package/dist/browser/styles/overlay.css +181 -0
- package/dist/browser/trace-record.js +33 -0
- package/dist/browser/types.js +4 -0
- package/dist/cli/setup.js +190 -0
- package/dist/cli/types.js +2 -0
- package/dist/{utils.js → cli/utils.js} +39 -11
- package/dist/node/claude-agent.js +185 -0
- package/dist/node/codex-agent.js +138 -0
- package/dist/node/nuxt-module.js +34 -0
- package/dist/node/types.js +4 -0
- package/dist/node/vite-plugin.js +213 -0
- package/package.json +13 -12
- package/dist/comments.js +0 -363
- package/dist/constants.js +0 -1
- package/dist/inspector.js +0 -168
- package/dist/location-transform.js +0 -47
- package/dist/nuxt-module.js +0 -24
- package/dist/overlay.js +0 -378
- package/dist/server.js +0 -122
- package/dist/setup.js +0 -65
- package/dist/socket.js +0 -36
- package/dist/types.js +0 -1
- package/dist/vite-plugin.js +0 -44
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Mirrors claude-agent.ts's workflow: one session is established at server
|
|
2
|
+
// start (the KAPI_SESSION_PROMPT turn) and every later submission resumes it,
|
|
3
|
+
// so all tabs share one continuous session in submission order. The one thing
|
|
4
|
+
// codex can't do that Claude can: `codex exec` is one-shot (no stdin-driven
|
|
5
|
+
// warm process), so each turn still pays process cold-start — session
|
|
6
|
+
// continuity is preserved via `exec resume <id>`, but the process isn't warm.
|
|
7
|
+
// ponytail: cold-start per turn is inherent to `codex exec`; only a codex
|
|
8
|
+
// server/stdin mode could remove it — none exists in the CLI today.
|
|
9
|
+
import { spawn } from 'child_process';
|
|
10
|
+
const KAPI_SESSION_PROMPT = "You're applying UI edits from visual comments. Each comment gives the exact file:line:col — go straight there, don't search for it. Make only the requested edit. " +
|
|
11
|
+
'Before finishing, check whether a higher-precedence rule on the same element would override the property you just changed — an inline `style` attribute, `!important`, a more specific selector, or a utility class applied after it. You have no way to see the rendered result, so if such a conflict exists, edit the highest-precedence source instead of (or in addition to) the one you found first, so the change actually takes visible effect. ' +
|
|
12
|
+
'Stay terse: no narration between steps, and finish with at most one short sentence.';
|
|
13
|
+
function startSession(cwd, prompt) {
|
|
14
|
+
return spawn('codex', ['exec', '--json', '--skip-git-repo-check', '--sandbox', 'workspace-write', '--cd', cwd, prompt], {
|
|
15
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function resumeSession(activeSession, prompt) {
|
|
19
|
+
return spawn('codex', ['exec', 'resume', activeSession.id, '--json', '--skip-git-repo-check', prompt], {
|
|
20
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function sessionIdFromEvent(event) {
|
|
24
|
+
return typeof event?.thread_id === 'string' ? event.thread_id : null;
|
|
25
|
+
}
|
|
26
|
+
function describeEvent(event) {
|
|
27
|
+
const item = event?.item;
|
|
28
|
+
if (item?.type === 'command_execution' && item.command)
|
|
29
|
+
return `Running: ${String(item.command).slice(0, 60)}`;
|
|
30
|
+
if (item?.type === 'agent_message' && item.text)
|
|
31
|
+
return String(item.text).slice(0, 120);
|
|
32
|
+
if (item?.type === 'file_change' && item.path)
|
|
33
|
+
return `Editing ${item.path}`;
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
let cwd = process.cwd();
|
|
37
|
+
let session = null;
|
|
38
|
+
let child = null;
|
|
39
|
+
let busy = false; // a turn is currently running
|
|
40
|
+
// Which client to notify when the running turn finishes. Null for the startup
|
|
41
|
+
// turn that sends KAPI_SESSION_PROMPT — there's no tab yet.
|
|
42
|
+
let activeClient = null;
|
|
43
|
+
// FIFO queue of turns waiting for the current one to finish. The first entry,
|
|
44
|
+
// queued at server startup, is the KAPI_SESSION_PROMPT itself.
|
|
45
|
+
const pendingPrompts = [];
|
|
46
|
+
function processQueue() {
|
|
47
|
+
if (busy || pendingPrompts.length === 0)
|
|
48
|
+
return;
|
|
49
|
+
const next = pendingPrompts.shift();
|
|
50
|
+
busy = true;
|
|
51
|
+
activeClient = next.client;
|
|
52
|
+
next.client?.send('kapi:processing', { status: session ? 'Continuing session...' : 'Starting Codex...' });
|
|
53
|
+
const proc = session ? resumeSession(session, next.prompt) : startSession(cwd, next.prompt);
|
|
54
|
+
child = proc;
|
|
55
|
+
let buffer = '';
|
|
56
|
+
let failed = false;
|
|
57
|
+
const handleEvent = (event) => {
|
|
58
|
+
const nextSessionId = sessionIdFromEvent(event);
|
|
59
|
+
if (nextSessionId && nextSessionId !== session?.id) {
|
|
60
|
+
session = { agent: 'codex', id: nextSessionId };
|
|
61
|
+
console.log(`[kapi] codex session started: ${nextSessionId}`);
|
|
62
|
+
}
|
|
63
|
+
const status = describeEvent(event);
|
|
64
|
+
if (status)
|
|
65
|
+
activeClient?.send('kapi:processing', { status });
|
|
66
|
+
};
|
|
67
|
+
proc.stdout.on('data', (chunk) => {
|
|
68
|
+
buffer += chunk.toString();
|
|
69
|
+
let newlineIndex;
|
|
70
|
+
while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
|
|
71
|
+
const line = buffer.slice(0, newlineIndex).trim();
|
|
72
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
73
|
+
if (!line)
|
|
74
|
+
continue;
|
|
75
|
+
try {
|
|
76
|
+
handleEvent(JSON.parse(line));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Ignore non-JSON progress from a CLI and continue reading its stream.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
proc.stderr.pipe(process.stderr);
|
|
84
|
+
proc.on('error', (error) => {
|
|
85
|
+
failed = true;
|
|
86
|
+
activeClient?.send('kapi:error', { message: error.message });
|
|
87
|
+
});
|
|
88
|
+
proc.on('close', (code) => {
|
|
89
|
+
if (buffer.trim()) {
|
|
90
|
+
try {
|
|
91
|
+
handleEvent(JSON.parse(buffer));
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// A final non-JSON line is only CLI output, not a Kapi protocol event.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (code !== 0 && !proc.killed && !failed) {
|
|
98
|
+
activeClient?.send('kapi:error', { message: `codex exited with code ${code}.` });
|
|
99
|
+
}
|
|
100
|
+
else if (!failed) {
|
|
101
|
+
activeClient?.send('kapi:done');
|
|
102
|
+
}
|
|
103
|
+
if (child === proc)
|
|
104
|
+
child = null;
|
|
105
|
+
busy = false;
|
|
106
|
+
activeClient = null;
|
|
107
|
+
processQueue();
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
export const codexAgent = {
|
|
111
|
+
start(startCwd) {
|
|
112
|
+
cwd = startCwd;
|
|
113
|
+
// Establish the session up front (before any tab connects) so the user's
|
|
114
|
+
// first real submission resumes an existing session instead of creating one.
|
|
115
|
+
pendingPrompts.push({ prompt: KAPI_SESSION_PROMPT, client: null });
|
|
116
|
+
processQueue();
|
|
117
|
+
},
|
|
118
|
+
submit(prompt, client) {
|
|
119
|
+
pendingPrompts.push({ prompt, client });
|
|
120
|
+
processQueue();
|
|
121
|
+
},
|
|
122
|
+
stop(client) {
|
|
123
|
+
// Drop anything this client queued but hasn't started.
|
|
124
|
+
for (let i = pendingPrompts.length - 1; i >= 0; i--) {
|
|
125
|
+
if (pendingPrompts[i].client === client)
|
|
126
|
+
pendingPrompts.splice(i, 1);
|
|
127
|
+
}
|
|
128
|
+
// Kill the running turn only if it's this client's. The close handler
|
|
129
|
+
// clears state and starts the next queued turn (resuming the session).
|
|
130
|
+
if (activeClient === client && child) {
|
|
131
|
+
console.log('[kapi] stopping codex process');
|
|
132
|
+
child.kill();
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
onClose(client) {
|
|
136
|
+
codexAgent.stop(client);
|
|
137
|
+
},
|
|
138
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defineNuxtModule, addVitePlugin } from '@nuxt/kit';
|
|
2
|
+
import kapiVitePlugin from './vite-plugin.js';
|
|
3
|
+
export default defineNuxtModule({
|
|
4
|
+
meta: {
|
|
5
|
+
name: 'kapi-ui',
|
|
6
|
+
configKey: 'kapi',
|
|
7
|
+
},
|
|
8
|
+
setup(options, nuxt) {
|
|
9
|
+
var _a;
|
|
10
|
+
if (!nuxt.options.dev)
|
|
11
|
+
return;
|
|
12
|
+
// The vite plugin does everything now — instrumentation, the overlay
|
|
13
|
+
// module, and wiring the agent onto Vite's HMR websocket (its
|
|
14
|
+
// configureServer hook). Registering it here means Nuxt's Vite server
|
|
15
|
+
// gets that wiring too; the overlay talks back over the same HMR channel
|
|
16
|
+
// Nitro already proxies, so there's no separate port to inject.
|
|
17
|
+
addVitePlugin(kapiVitePlugin(options));
|
|
18
|
+
// Nitro renders HTML, not Vite's transformIndexHtml, so the overlay script
|
|
19
|
+
// still has to be injected here via unhead. Nuxt mounts Vite's dev
|
|
20
|
+
// middleware under app.buildAssetsDir (e.g. /_nuxt/), not at the site
|
|
21
|
+
// root, so request it from under that prefix or Nitro's page renderer
|
|
22
|
+
// intercepts it before Vite ever sees it.
|
|
23
|
+
(_a = nuxt.options.app.head).script || (_a.script = []);
|
|
24
|
+
// Tell the overlay the agent session is off so it hides the AI button.
|
|
25
|
+
// Classic inline script runs before the deferred overlay module reads it.
|
|
26
|
+
if (options.agent === false) {
|
|
27
|
+
nuxt.options.app.head.script.push({ innerHTML: 'window.__KAPI_AGENT_ENABLED__=false' });
|
|
28
|
+
}
|
|
29
|
+
nuxt.options.app.head.script.push({
|
|
30
|
+
src: `${nuxt.options.app.buildAssetsDir}@kapi-ui/overlay`,
|
|
31
|
+
type: 'module',
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
});
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { searchForWorkspaceRoot } from 'vite';
|
|
4
|
+
import { walk } from 'estree-walker';
|
|
5
|
+
import MagicString from 'magic-string';
|
|
6
|
+
import { SourceMapConsumer } from 'source-map-js';
|
|
7
|
+
import { claudeAgent } from './claude-agent.js';
|
|
8
|
+
import { codexAgent } from './codex-agent.js';
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const overlayPath = path.resolve(__dirname, '../browser/overlay.js');
|
|
11
|
+
const traceRecordPath = path.resolve(__dirname, '../browser/trace-record.js');
|
|
12
|
+
// Names Vue's SFC compiler emits in a component's compiled render function
|
|
13
|
+
// for every vnode it creates. Wrapping calls to these — rather than parsing
|
|
14
|
+
// the original template — means coverage always matches whatever Vue itself
|
|
15
|
+
// just compiled, including dynamic `h()` calls a template-source parser would
|
|
16
|
+
// never see.
|
|
17
|
+
const VNODE_FUNCTIONS = [
|
|
18
|
+
'h',
|
|
19
|
+
'_createElementVNode',
|
|
20
|
+
'_createElementBlock',
|
|
21
|
+
'_createBlock',
|
|
22
|
+
'_createVNode',
|
|
23
|
+
'_createStaticVNode',
|
|
24
|
+
];
|
|
25
|
+
const vnodeCallRe = new RegExp(`\\b(?:${VNODE_FUNCTIONS.join('|')})\\(`);
|
|
26
|
+
const INSTRUMENTED_MARKER = '/* kapi-ui:instrumented */';
|
|
27
|
+
// Byte offset of the start of each line (line 1 starts at offset 0). Built
|
|
28
|
+
// once per file so looking up a call's line/column is a binary search
|
|
29
|
+
// instead of re-scanning from the start of the file for every vnode call.
|
|
30
|
+
function buildLineOffsets(code) {
|
|
31
|
+
const offsets = [0];
|
|
32
|
+
for (let i = 0; i < code.length; i++) {
|
|
33
|
+
if (code[i] === '\n')
|
|
34
|
+
offsets.push(i + 1);
|
|
35
|
+
}
|
|
36
|
+
return offsets;
|
|
37
|
+
}
|
|
38
|
+
function offsetToPos(lineOffsets, index) {
|
|
39
|
+
let lo = 0;
|
|
40
|
+
let hi = lineOffsets.length - 1;
|
|
41
|
+
while (lo < hi) {
|
|
42
|
+
const mid = (lo + hi + 1) >> 1;
|
|
43
|
+
if (lineOffsets[mid] <= index)
|
|
44
|
+
lo = mid;
|
|
45
|
+
else
|
|
46
|
+
hi = mid - 1;
|
|
47
|
+
}
|
|
48
|
+
return { line: lo + 1, column: index - lineOffsets[lo] };
|
|
49
|
+
}
|
|
50
|
+
function createUniqueIdentifier(base, identifiers) {
|
|
51
|
+
let identifier = base;
|
|
52
|
+
let suffix = 1;
|
|
53
|
+
while (identifiers.has(identifier)) {
|
|
54
|
+
identifier = `${base}_${suffix++}`;
|
|
55
|
+
}
|
|
56
|
+
identifiers.add(identifier);
|
|
57
|
+
return identifier;
|
|
58
|
+
}
|
|
59
|
+
// Normalize + validate the `agent` option once, up front. `agent` is required
|
|
60
|
+
// (no default): a missing or bad value throws at dev-server startup with a
|
|
61
|
+
// clear message instead of silently falling back to Claude — which would be
|
|
62
|
+
// especially confusing for a value the user meant as "off" (e.g. 'none').
|
|
63
|
+
function resolveAgent(agent) {
|
|
64
|
+
if (agent === undefined) {
|
|
65
|
+
throw new Error("[kapi-ui] The `agent` option is required. Set it to 'claude', 'codex', or false (manual copy/paste).");
|
|
66
|
+
}
|
|
67
|
+
if (agent === false)
|
|
68
|
+
return false; // manual copy/paste
|
|
69
|
+
if (agent === 'claude' || agent === 'codex')
|
|
70
|
+
return agent;
|
|
71
|
+
throw new Error(`[kapi-ui] Invalid \`agent\` option: ${JSON.stringify(agent)}. Use 'claude', 'codex', or false (manual copy/paste).`);
|
|
72
|
+
}
|
|
73
|
+
export default function kapi(options) {
|
|
74
|
+
const agent = resolveAgent(options?.agent);
|
|
75
|
+
let started = false;
|
|
76
|
+
return {
|
|
77
|
+
name: 'kapi-ui',
|
|
78
|
+
// Must run after @vitejs/plugin-vue has compiled templates into
|
|
79
|
+
// vnode-creation calls — this transform rewrites that compiled output,
|
|
80
|
+
// not the raw SFC source.
|
|
81
|
+
enforce: 'post',
|
|
82
|
+
apply: 'serve',
|
|
83
|
+
config() {
|
|
84
|
+
return {
|
|
85
|
+
server: {
|
|
86
|
+
fs: {
|
|
87
|
+
allow: [searchForWorkspaceRoot(process.cwd()), path.dirname(overlayPath)],
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
// Ride Vite's own HMR websocket instead of standing up a second server on
|
|
93
|
+
// its own port: the overlay talks to us via custom HMR events (see
|
|
94
|
+
// browser/socket.ts), and we dispatch them to the configured agent. In
|
|
95
|
+
// Nuxt this runs too, since nuxt-module.ts registers this same plugin.
|
|
96
|
+
configureServer(server) {
|
|
97
|
+
if (agent === false)
|
|
98
|
+
return; // manual copy/paste only — no agent session
|
|
99
|
+
if (started)
|
|
100
|
+
return; // configureServer can fire more than once; agent.start() must not
|
|
101
|
+
started = true;
|
|
102
|
+
const cwd = process.cwd();
|
|
103
|
+
const runtime = agent === 'codex' ? codexAgent : claudeAgent;
|
|
104
|
+
runtime.start(cwd);
|
|
105
|
+
// Vite hands the event handler a fresh WebSocketClient wrapper but a
|
|
106
|
+
// stable underlying `.socket` per connection; key one AgentClient per
|
|
107
|
+
// socket so submit/stop/close all agree on identity and share a sender.
|
|
108
|
+
const clients = new Map();
|
|
109
|
+
const clientFor = (c) => {
|
|
110
|
+
let existing = clients.get(c.socket);
|
|
111
|
+
if (!existing) {
|
|
112
|
+
existing = { send: (event, data) => c.send(event, data) };
|
|
113
|
+
clients.set(c.socket, existing);
|
|
114
|
+
}
|
|
115
|
+
return existing;
|
|
116
|
+
};
|
|
117
|
+
server.ws.on('kapi:submit', (data, client) => runtime.submit(data.prompt, clientFor(client), cwd));
|
|
118
|
+
server.ws.on('kapi:stop', (_data, client) => runtime.stop(clientFor(client)));
|
|
119
|
+
server.ws.on('connection', (socket) => {
|
|
120
|
+
socket.on('close', () => {
|
|
121
|
+
const existing = clients.get(socket);
|
|
122
|
+
if (existing) {
|
|
123
|
+
runtime.onClose(existing);
|
|
124
|
+
clients.delete(socket);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
resolveId(id) {
|
|
130
|
+
if (id === '/@kapi-ui/overlay')
|
|
131
|
+
return overlayPath;
|
|
132
|
+
if (id === '/@kapi-ui/trace-record')
|
|
133
|
+
return traceRecordPath;
|
|
134
|
+
},
|
|
135
|
+
transform(code, id) {
|
|
136
|
+
// `this.environment` only exists on Vite 6+ (the Environment API); vite
|
|
137
|
+
// is a devDependency here, not a peerDependency, so kapi has no control
|
|
138
|
+
// over the Vite version in the host project. Treat its absence as
|
|
139
|
+
// "client" so pre-6 Vite still runs the transform instead of throwing
|
|
140
|
+
// on every module.
|
|
141
|
+
if (this.environment && this.environment.name !== 'client')
|
|
142
|
+
return;
|
|
143
|
+
if (id.includes('/node_modules/'))
|
|
144
|
+
return;
|
|
145
|
+
// Only Vue SFC compiled output contains this — cheap enough to check
|
|
146
|
+
// before parsing every transformed module in the project.
|
|
147
|
+
if (!code.includes('_sfc_render('))
|
|
148
|
+
return;
|
|
149
|
+
if (!vnodeCallRe.test(code))
|
|
150
|
+
return;
|
|
151
|
+
if (code.includes(INSTRUMENTED_MARKER))
|
|
152
|
+
return;
|
|
153
|
+
const map = this.getCombinedSourcemap();
|
|
154
|
+
const consumer = new SourceMapConsumer(map);
|
|
155
|
+
const s = new MagicString(code);
|
|
156
|
+
const ast = this.parse(code);
|
|
157
|
+
const lineOffsets = buildLineOffsets(code);
|
|
158
|
+
const identifiers = new Set();
|
|
159
|
+
let hit = false;
|
|
160
|
+
walk(ast, {
|
|
161
|
+
enter(node) {
|
|
162
|
+
if (node.type === 'Identifier')
|
|
163
|
+
identifiers.add(node.name);
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
// Vue's compiled render function shares the module's lexical scope, so
|
|
167
|
+
// reserve names not used anywhere in the parsed module before injecting
|
|
168
|
+
// either the import binding or the tracer helper.
|
|
169
|
+
const recordPositionIdentifier = createUniqueIdentifier('__kapiRecordPosition', identifiers);
|
|
170
|
+
const tracerIdentifier = createUniqueIdentifier('__kapiTracer', identifiers);
|
|
171
|
+
walk(ast, {
|
|
172
|
+
enter(node) {
|
|
173
|
+
if (node.type !== 'CallExpression' || node.callee.type !== 'Identifier')
|
|
174
|
+
return;
|
|
175
|
+
if (!VNODE_FUNCTIONS.includes(node.callee.name))
|
|
176
|
+
return;
|
|
177
|
+
const { start, end } = node;
|
|
178
|
+
const original = consumer.originalPositionFor(offsetToPos(lineOffsets, start));
|
|
179
|
+
if (original.source === null)
|
|
180
|
+
return;
|
|
181
|
+
hit = true;
|
|
182
|
+
s.appendLeft(start, `${tracerIdentifier}(${original.line},${original.column},`);
|
|
183
|
+
s.appendRight(end, ')');
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
if (!hit)
|
|
187
|
+
return;
|
|
188
|
+
const relativeFile = path.relative(process.cwd(), id.split('?')[0]);
|
|
189
|
+
s.prepend(`${INSTRUMENTED_MARKER}\nimport { recordPosition as ${recordPositionIdentifier} } from "/@kapi-ui/trace-record"\n`);
|
|
190
|
+
s.append(`\nfunction ${tracerIdentifier}(line, column, vnode) { return ${recordPositionIdentifier}(${JSON.stringify(relativeFile)}, line, column, vnode) }\n`);
|
|
191
|
+
return { code: s.toString(), map: s.generateMap({ hires: true }) };
|
|
192
|
+
},
|
|
193
|
+
transformIndexHtml() {
|
|
194
|
+
const tags = [
|
|
195
|
+
{
|
|
196
|
+
tag: 'script',
|
|
197
|
+
attrs: { type: 'module', src: '/@kapi-ui/overlay' },
|
|
198
|
+
injectTo: 'body',
|
|
199
|
+
},
|
|
200
|
+
];
|
|
201
|
+
// Tell the overlay the agent session is off so it hides the AI button.
|
|
202
|
+
// Classic inline script runs before the deferred overlay module reads it.
|
|
203
|
+
if (agent === false) {
|
|
204
|
+
tags.unshift({
|
|
205
|
+
tag: 'script',
|
|
206
|
+
children: 'window.__KAPI_AGENT_ENABLED__=false',
|
|
207
|
+
injectTo: 'body',
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return tags;
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
}
|
package/package.json
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kapi-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Visually prompt your UI",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"build": "tsc",
|
|
6
|
+
"build": "tsc && npm run copy:css",
|
|
7
|
+
"copy:css": "mkdir -p dist/browser/styles && cp src/browser/styles/*.css dist/browser/styles/",
|
|
8
|
+
"dev": "npm run copy:css && (node -e \"require('fs').watch('src/browser/styles',()=>require('child_process').execSync('npm run copy:css'))\" & tsc --watch --preserveWatchOutput)",
|
|
7
9
|
"prepublishOnly": "npm run build"
|
|
8
10
|
},
|
|
9
11
|
"files": [
|
|
10
12
|
"dist"
|
|
11
13
|
],
|
|
12
14
|
"bin": {
|
|
13
|
-
"kapi-ui": "dist/setup.js"
|
|
15
|
+
"kapi-ui": "dist/cli/setup.js"
|
|
14
16
|
},
|
|
15
17
|
"exports": {
|
|
16
|
-
"./vite-plugin": "./dist/vite-plugin.js",
|
|
17
|
-
"./nuxt": "./dist/nuxt-module.js"
|
|
18
|
+
"./vite-plugin": "./dist/node/vite-plugin.js",
|
|
19
|
+
"./nuxt": "./dist/node/nuxt-module.js"
|
|
18
20
|
},
|
|
19
21
|
"repository": {
|
|
20
22
|
"type": "git",
|
|
@@ -23,10 +25,10 @@
|
|
|
23
25
|
"keywords": [
|
|
24
26
|
"ai",
|
|
25
27
|
"design",
|
|
26
|
-
"
|
|
28
|
+
"frontend"
|
|
27
29
|
],
|
|
28
30
|
"author": "James Bermudo",
|
|
29
|
-
"license": "
|
|
31
|
+
"license": "MIT",
|
|
30
32
|
"type": "module",
|
|
31
33
|
"bugs": {
|
|
32
34
|
"url": "https://github.com/jcebermudo/kapi/issues"
|
|
@@ -35,15 +37,14 @@
|
|
|
35
37
|
"devDependencies": {
|
|
36
38
|
"@nuxt/schema": "^3.14.0",
|
|
37
39
|
"@types/node": "^26.1.0",
|
|
38
|
-
"@types/prompts": "^2.4.9",
|
|
39
|
-
"@types/ws": "^8.18.1",
|
|
40
40
|
"typescript": "^6.0.3",
|
|
41
41
|
"vite": "^8.1.3"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@nuxt/kit": "^
|
|
44
|
+
"@nuxt/kit": "^4.4.8",
|
|
45
|
+
"estree-walker": "^2.0.2",
|
|
46
|
+
"magic-string": "^0.30.21",
|
|
45
47
|
"magicast": "^0.5.3",
|
|
46
|
-
"
|
|
47
|
-
"ws": "^8.21.0"
|
|
48
|
+
"source-map-js": "^1.2.1"
|
|
48
49
|
}
|
|
49
50
|
}
|