instruckt 0.4.26 → 0.4.28
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/README.md +223 -13
- package/dist/instruckt.cjs.js +104 -26
- package/dist/instruckt.cjs.js.map +1 -1
- package/dist/instruckt.d.cts +196 -0
- package/dist/instruckt.d.mts +7 -1
- package/dist/instruckt.d.ts +21 -1
- package/dist/instruckt.esm.js +104 -26
- package/dist/instruckt.esm.js.map +1 -1
- package/dist/instruckt.iife.js +17 -17
- package/dist/instruckt.iife.js.map +1 -1
- package/dist/vite.cjs.js +218 -0
- package/dist/vite.cjs.js.map +1 -0
- package/dist/vite.d.cts +45 -0
- package/dist/vite.d.mts +28 -0
- package/dist/vite.d.ts +45 -0
- package/dist/vite.esm.js +197 -0
- package/dist/vite.esm.js.map +1 -0
- package/package.json +18 -2
package/README.md
CHANGED
|
@@ -10,13 +10,106 @@ Framework-agnostic JS core with adapters for Livewire, Vue, Svelte, and React.
|
|
|
10
10
|
npm install instruckt
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### Vite Plugin
|
|
16
|
+
|
|
17
|
+
The easiest way to use instruckt is with the Vite plugin. It handles client injection and provides a built-in dev API server — no backend required.
|
|
14
18
|
|
|
15
|
-
```
|
|
16
|
-
|
|
19
|
+
```js
|
|
20
|
+
// vite.config.ts
|
|
21
|
+
import instruckt from 'instruckt/vite'
|
|
22
|
+
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
plugins: [instruckt()],
|
|
25
|
+
})
|
|
17
26
|
```
|
|
18
27
|
|
|
19
|
-
|
|
28
|
+
That's it for SPA apps (Vue, React, Svelte with Vite). The plugin auto-injects the client via `transformIndexHtml`.
|
|
29
|
+
|
|
30
|
+
### Laravel
|
|
31
|
+
|
|
32
|
+
Use the **[instruckt-laravel](https://github.com/joshcirre/instruckt-laravel)** package — it provides the backend API, MCP tools, JSON file storage, and handles install/uninstall automatically:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
composer require joshcirre/instruckt-laravel --dev
|
|
36
|
+
php artisan instruckt:install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The install command adds the Vite plugin to your `vite.config.js` with `server: false` (Laravel owns the backend), configures MCP for your AI agent, and adds the virtual import to your JS entry point.
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
// vite.config.js (added automatically by install command)
|
|
43
|
+
import instruckt from 'instruckt/vite'
|
|
44
|
+
|
|
45
|
+
export default defineConfig({
|
|
46
|
+
plugins: [
|
|
47
|
+
laravel({ input: ['resources/js/app.js'] }),
|
|
48
|
+
instruckt({
|
|
49
|
+
server: false,
|
|
50
|
+
adapters: ['livewire', 'blade'],
|
|
51
|
+
mcp: true,
|
|
52
|
+
}),
|
|
53
|
+
],
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### SSR Frameworks (SvelteKit, Nuxt, etc.)
|
|
58
|
+
|
|
59
|
+
For frameworks that don't use `index.html`, import the virtual module in your layout:
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
// SvelteKit: src/routes/+layout.svelte
|
|
63
|
+
import 'virtual:instruckt'
|
|
64
|
+
|
|
65
|
+
// Nuxt: plugins/instruckt.client.ts
|
|
66
|
+
import 'virtual:instruckt'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The virtual module is SSR-safe — it only initializes in the browser.
|
|
70
|
+
|
|
71
|
+
### Astro
|
|
72
|
+
|
|
73
|
+
See **[instruckt-astro](https://github.com/sgasser/instruckt-astro)** for a community-maintained Astro integration.
|
|
74
|
+
|
|
75
|
+
## Vite Plugin Options
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
instruckt({
|
|
79
|
+
// Framework adapters to activate (default: auto-detect)
|
|
80
|
+
adapters: ['svelte'],
|
|
81
|
+
|
|
82
|
+
// Theme: 'light' | 'dark' | 'auto' (default: 'auto')
|
|
83
|
+
theme: 'auto',
|
|
84
|
+
|
|
85
|
+
// Toolbar position (default: 'bottom-right')
|
|
86
|
+
position: 'bottom-right',
|
|
87
|
+
|
|
88
|
+
// Customize marker pin colors
|
|
89
|
+
colors: { default: '#6366f1', screenshot: '#22c55e', dismissed: '#71717a' },
|
|
90
|
+
|
|
91
|
+
// Customize keyboard shortcuts
|
|
92
|
+
keys: { annotate: 'a', freeze: 'f', screenshot: 'c', clearPage: 'x' },
|
|
93
|
+
|
|
94
|
+
// Storage directory for annotations + screenshots (default: '.instruckt')
|
|
95
|
+
dir: '.instruckt',
|
|
96
|
+
|
|
97
|
+
// API endpoint prefix (default: '/instruckt')
|
|
98
|
+
endpoint: '/instruckt',
|
|
99
|
+
|
|
100
|
+
// Enable built-in dev API server (default: true)
|
|
101
|
+
// Set to false when your framework provides its own backend (e.g. Laravel)
|
|
102
|
+
server: true,
|
|
103
|
+
|
|
104
|
+
// Show MCP tool instructions in clipboard markdown (default: false)
|
|
105
|
+
// Set to true when using with a backend that registers MCP tools
|
|
106
|
+
mcp: false,
|
|
107
|
+
})
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Manual Setup
|
|
111
|
+
|
|
112
|
+
If you're not using Vite, you can initialize instruckt directly:
|
|
20
113
|
|
|
21
114
|
```js
|
|
22
115
|
import { Instruckt } from 'instruckt'
|
|
@@ -26,15 +119,114 @@ const instruckt = new Instruckt({
|
|
|
26
119
|
})
|
|
27
120
|
```
|
|
28
121
|
|
|
29
|
-
|
|
122
|
+
### Framework-Specific Manual Setup
|
|
123
|
+
|
|
124
|
+
instruckt is a browser-only library. In SSR frameworks without the Vite plugin, make sure it only loads on the client.
|
|
125
|
+
|
|
126
|
+
<details>
|
|
127
|
+
<summary>SvelteKit</summary>
|
|
128
|
+
|
|
129
|
+
```svelte
|
|
130
|
+
<!-- src/lib/InstrucktProvider.svelte -->
|
|
131
|
+
<script>
|
|
132
|
+
import { onMount } from 'svelte';
|
|
133
|
+
|
|
134
|
+
onMount(async () => {
|
|
135
|
+
const { Instruckt } = await import('instruckt');
|
|
136
|
+
const instruckt = new Instruckt({
|
|
137
|
+
endpoint: '/api/annotations',
|
|
138
|
+
adapters: ['svelte'],
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return () => instruckt.destroy();
|
|
142
|
+
});
|
|
143
|
+
</script>
|
|
144
|
+
```
|
|
30
145
|
|
|
31
|
-
```
|
|
32
|
-
|
|
146
|
+
```svelte
|
|
147
|
+
<!-- src/routes/+layout.svelte -->
|
|
33
148
|
<script>
|
|
34
|
-
|
|
149
|
+
import { browser } from '$app/environment';
|
|
150
|
+
|
|
151
|
+
let { children } = $props();
|
|
35
152
|
</script>
|
|
153
|
+
|
|
154
|
+
{#if browser}
|
|
155
|
+
{#await import('$lib/InstrucktProvider.svelte') then { default: InstrucktProvider }}
|
|
156
|
+
<InstrucktProvider />
|
|
157
|
+
{/await}
|
|
158
|
+
{/if}
|
|
159
|
+
|
|
160
|
+
{@render children()}
|
|
36
161
|
```
|
|
37
162
|
|
|
163
|
+
</details>
|
|
164
|
+
|
|
165
|
+
<details>
|
|
166
|
+
<summary>Nuxt</summary>
|
|
167
|
+
|
|
168
|
+
```vue
|
|
169
|
+
<!-- plugins/instruckt.client.ts -->
|
|
170
|
+
<script>
|
|
171
|
+
// The .client.ts suffix ensures Nuxt only runs this in the browser
|
|
172
|
+
export default defineNuxtPlugin(async () => {
|
|
173
|
+
const { Instruckt } = await import('instruckt')
|
|
174
|
+
|
|
175
|
+
const instruckt = new Instruckt({
|
|
176
|
+
endpoint: '/api/annotations',
|
|
177
|
+
adapters: ['vue'],
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
</script>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
</details>
|
|
184
|
+
|
|
185
|
+
<details>
|
|
186
|
+
<summary>Next.js (App Router)</summary>
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
// components/InstrucktProvider.tsx
|
|
190
|
+
'use client'
|
|
191
|
+
|
|
192
|
+
import { useEffect } from 'react'
|
|
193
|
+
|
|
194
|
+
export function InstrucktProvider() {
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
let instruckt: any
|
|
197
|
+
|
|
198
|
+
import('instruckt').then(({ Instruckt }) => {
|
|
199
|
+
instruckt = new Instruckt({
|
|
200
|
+
endpoint: '/api/annotations',
|
|
201
|
+
adapters: ['react'],
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
return () => instruckt?.destroy()
|
|
206
|
+
}, [])
|
|
207
|
+
|
|
208
|
+
return null
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
// app/layout.tsx
|
|
214
|
+
import { InstrucktProvider } from '@/components/InstrucktProvider'
|
|
215
|
+
|
|
216
|
+
export default function RootLayout({ children }) {
|
|
217
|
+
return (
|
|
218
|
+
<html>
|
|
219
|
+
<body>
|
|
220
|
+
{children}
|
|
221
|
+
<InstrucktProvider />
|
|
222
|
+
</body>
|
|
223
|
+
</html>
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
</details>
|
|
229
|
+
|
|
38
230
|
## How It Works
|
|
39
231
|
|
|
40
232
|
1. A floating toolbar appears in your app
|
|
@@ -56,7 +248,7 @@ Or with the IIFE build:
|
|
|
56
248
|
- Element: `button.btn-primary` in `pages::auth.login`
|
|
57
249
|
- Classes: `btn btn-primary`
|
|
58
250
|
- Text: "Submit Login"
|
|
59
|
-
- Screenshot:
|
|
251
|
+
- Screenshot: `.instruckt/screenshots/01JWXYZ.png`
|
|
60
252
|
|
|
61
253
|
## 2. Make the login card have rounded corners
|
|
62
254
|
- Element: `div.bg-white` in `pages::auth.login`
|
|
@@ -67,7 +259,7 @@ Or with the IIFE build:
|
|
|
67
259
|
|
|
68
260
|
```js
|
|
69
261
|
new Instruckt({
|
|
70
|
-
// Required — URL to your instruckt API (provided by the Laravel package or your own backend)
|
|
262
|
+
// Required — URL to your instruckt API (provided by the Vite plugin, Laravel package, or your own backend)
|
|
71
263
|
endpoint: '/instruckt',
|
|
72
264
|
|
|
73
265
|
// Framework adapters to activate (default: all)
|
|
@@ -94,6 +286,10 @@ new Instruckt({
|
|
|
94
286
|
clearPage: 'x', // clear annotations on this page
|
|
95
287
|
},
|
|
96
288
|
|
|
289
|
+
// Whether MCP tools are available (default: false)
|
|
290
|
+
// Set to true when using with Laravel or another backend that registers MCP tools
|
|
291
|
+
mcp: false,
|
|
292
|
+
|
|
97
293
|
// Callbacks
|
|
98
294
|
onAnnotationAdd: (annotation) => {},
|
|
99
295
|
})
|
|
@@ -118,7 +314,7 @@ Default shortcuts (customizable via `keys` config):
|
|
|
118
314
|
- **Shadow DOM isolation** — all UI renders in shadow roots so it never conflicts with your styles
|
|
119
315
|
- **Copy as markdown** — annotations auto-copy as structured markdown optimized for AI agents
|
|
120
316
|
- **Freeze mode** — pause animations, freeze popovers/dropdowns, and block all navigation
|
|
121
|
-
- **Annotation persistence** — annotations survive page reloads
|
|
317
|
+
- **Annotation persistence** — annotations survive page reloads via localStorage; with a backend (Vite plugin or Laravel), annotations are stored on disk as JSON
|
|
122
318
|
- **Minimize** — collapse to a small floating button with annotation count badge
|
|
123
319
|
- **Page-scoped markers** — annotation pins reposition on scroll/resize and only appear on the page where they were created
|
|
124
320
|
- **Clear controls** — clear current page (`X` key or trash icon), or clear all pages via flyout
|
|
@@ -139,9 +335,23 @@ instruckt.destroy()
|
|
|
139
335
|
|
|
140
336
|
## Backend
|
|
141
337
|
|
|
142
|
-
|
|
338
|
+
### Vite Plugin (Built-in)
|
|
339
|
+
|
|
340
|
+
The Vite plugin includes a dev API server that saves annotations and screenshots to disk (`.instruckt/` directory). No external backend needed. Screenshots are saved as files instead of base64, keeping clipboard markdown small.
|
|
341
|
+
|
|
342
|
+
### Laravel
|
|
143
343
|
|
|
144
|
-
|
|
344
|
+
**[instruckt-laravel](https://github.com/joshcirre/instruckt-laravel)** — Laravel package with JSON file storage, MCP tools, Blade component, and API routes. Includes `artisan instruckt:install` which auto-configures the Vite plugin, MCP, and agent skills.
|
|
345
|
+
|
|
346
|
+
### Custom Backend
|
|
347
|
+
|
|
348
|
+
instruckt expects these endpoints:
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
GET {endpoint}/annotations → list annotations
|
|
352
|
+
POST {endpoint}/annotations → create annotation
|
|
353
|
+
PATCH {endpoint}/annotations/{id} → update annotation
|
|
354
|
+
```
|
|
145
355
|
|
|
146
356
|
## License
|
|
147
357
|
|
package/dist/instruckt.cjs.js
CHANGED
|
@@ -549,7 +549,7 @@ var ICONS = {
|
|
|
549
549
|
logo: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z"/></svg>`
|
|
550
550
|
};
|
|
551
551
|
var Toolbar = class {
|
|
552
|
-
constructor(position, callbacks, keys) {
|
|
552
|
+
constructor(position, callbacks, keys, tools) {
|
|
553
553
|
this.position = position;
|
|
554
554
|
this.callbacks = callbacks;
|
|
555
555
|
this.fabBadge = null;
|
|
@@ -560,9 +560,15 @@ var Toolbar = class {
|
|
|
560
560
|
this.dragging = false;
|
|
561
561
|
this.dragOffset = { x: 0, y: 0 };
|
|
562
562
|
this.keys = keys != null ? keys : {};
|
|
563
|
+
this.tools = tools != null ? tools : {};
|
|
563
564
|
this.build();
|
|
564
565
|
this.setupDrag();
|
|
565
566
|
}
|
|
567
|
+
/** Whether a built-in tool should be shown (default true if not specified). */
|
|
568
|
+
show(id) {
|
|
569
|
+
const v = this.tools[id];
|
|
570
|
+
return v !== false;
|
|
571
|
+
}
|
|
566
572
|
build() {
|
|
567
573
|
var _a2, _b, _c, _d, _e;
|
|
568
574
|
this.host = document.createElement("div");
|
|
@@ -621,17 +627,18 @@ var Toolbar = class {
|
|
|
621
627
|
d.className = "divider";
|
|
622
628
|
return d;
|
|
623
629
|
};
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
);
|
|
630
|
+
const toAppend = [];
|
|
631
|
+
const add = (el) => {
|
|
632
|
+
if (toAppend.length > 0) toAppend.push(mkDiv());
|
|
633
|
+
toAppend.push(el);
|
|
634
|
+
};
|
|
635
|
+
if (this.show("annotate")) add(this.annotateBtn);
|
|
636
|
+
if (this.show("screenshot")) add(screenshotBtn);
|
|
637
|
+
if (this.show("freeze")) add(this.freezeBtn);
|
|
638
|
+
if (this.show("copy")) add(this.copyBtn);
|
|
639
|
+
if (this.show("clear_page") || this.show("clear_all")) add(clearWrap);
|
|
640
|
+
if (this.show("minimize")) add(minimizeBtn);
|
|
641
|
+
this.toolbarEl.append(...toAppend);
|
|
635
642
|
this.shadow.appendChild(this.toolbarEl);
|
|
636
643
|
this.fab = document.createElement("button");
|
|
637
644
|
this.fab.className = "fab";
|
|
@@ -3185,6 +3192,7 @@ var _Instruckt = class _Instruckt {
|
|
|
3185
3192
|
this.highlightLocked = false;
|
|
3186
3193
|
this.pollTimer = null;
|
|
3187
3194
|
this.initialLoadDone = false;
|
|
3195
|
+
this.hasBackend = false;
|
|
3188
3196
|
this.boundReposition = () => {
|
|
3189
3197
|
var _a2;
|
|
3190
3198
|
(_a2 = this.markers) == null ? void 0 : _a2.reposition(this.annotations);
|
|
@@ -3304,7 +3312,7 @@ var _Instruckt = class _Instruckt {
|
|
|
3304
3312
|
onClearPage: () => this.clearPage(),
|
|
3305
3313
|
onClearAll: () => this.clearEverything(),
|
|
3306
3314
|
onMinimize: (min) => this.onMinimize(min)
|
|
3307
|
-
}, this.config.keys);
|
|
3315
|
+
}, this.config.keys, this.config.tools);
|
|
3308
3316
|
this.highlight = new ElementHighlight();
|
|
3309
3317
|
this.popup = new AnnotationPopup();
|
|
3310
3318
|
this.markers = new AnnotationMarkers((annotation) => this.onMarkerClick(annotation));
|
|
@@ -3343,7 +3351,7 @@ var _Instruckt = class _Instruckt {
|
|
|
3343
3351
|
this.isAnnotating = false;
|
|
3344
3352
|
this.isFrozen = false;
|
|
3345
3353
|
document.querySelectorAll("[data-instruckt]").forEach((el) => el.remove());
|
|
3346
|
-
this.toolbar = new Toolbar(this.config.position, this.makeToolbarCallbacks());
|
|
3354
|
+
this.toolbar = new Toolbar(this.config.position, this.makeToolbarCallbacks(), this.config.keys, this.config.tools);
|
|
3347
3355
|
if (wasMinimized) this.toolbar.minimize();
|
|
3348
3356
|
this.markers = new AnnotationMarkers((annotation) => this.onMarkerClick(annotation));
|
|
3349
3357
|
this.highlight = new ElementHighlight();
|
|
@@ -3368,29 +3376,97 @@ var _Instruckt = class _Instruckt {
|
|
|
3368
3376
|
(_e = this.markers) == null ? void 0 : _e.setVisible(true);
|
|
3369
3377
|
}
|
|
3370
3378
|
}
|
|
3379
|
+
// ── Persistence ─────────────────────────────────────────────────
|
|
3380
|
+
static get STORAGE_KEY() {
|
|
3381
|
+
return `instruckt:${window.location.origin}:annotations`;
|
|
3382
|
+
}
|
|
3371
3383
|
async loadAnnotations() {
|
|
3372
3384
|
this.loadFromStorage();
|
|
3373
3385
|
try {
|
|
3374
3386
|
const remote = await this.api.getAnnotations();
|
|
3387
|
+
this.hasBackend = true;
|
|
3375
3388
|
const remoteIds = new Set(remote.map((a) => a.id));
|
|
3376
3389
|
const localOnly = this.annotations.filter((a) => !remoteIds.has(a.id));
|
|
3377
3390
|
this.annotations = [...remote, ...localOnly];
|
|
3378
3391
|
this.saveToStorage();
|
|
3379
3392
|
} catch (e) {
|
|
3393
|
+
this.hasBackend = false;
|
|
3380
3394
|
}
|
|
3381
3395
|
this.initialLoadDone = true;
|
|
3382
3396
|
this.syncMarkers();
|
|
3383
3397
|
}
|
|
3384
3398
|
saveToStorage() {
|
|
3385
3399
|
try {
|
|
3386
|
-
|
|
3400
|
+
const screenshotMap = /* @__PURE__ */ new Map();
|
|
3401
|
+
const stripped = this.annotations.map((a) => {
|
|
3402
|
+
var _a2;
|
|
3403
|
+
if ((_a2 = a.screenshot) == null ? void 0 : _a2.startsWith("data:")) {
|
|
3404
|
+
screenshotMap.set(a.id, a.screenshot);
|
|
3405
|
+
return __spreadProps(__spreadValues({}, a), { screenshot: `idb:${a.id}` });
|
|
3406
|
+
}
|
|
3407
|
+
return a;
|
|
3408
|
+
});
|
|
3409
|
+
localStorage.setItem(_Instruckt.STORAGE_KEY, JSON.stringify(stripped));
|
|
3410
|
+
if (screenshotMap.size > 0) this.saveScreenshotsToIdb(screenshotMap);
|
|
3387
3411
|
} catch (e) {
|
|
3388
3412
|
}
|
|
3389
3413
|
}
|
|
3390
3414
|
loadFromStorage() {
|
|
3391
3415
|
try {
|
|
3392
3416
|
const raw = localStorage.getItem(_Instruckt.STORAGE_KEY);
|
|
3393
|
-
if (raw)
|
|
3417
|
+
if (raw) {
|
|
3418
|
+
this.annotations = JSON.parse(raw);
|
|
3419
|
+
const idbRefs = this.annotations.filter((a) => {
|
|
3420
|
+
var _a2;
|
|
3421
|
+
return (_a2 = a.screenshot) == null ? void 0 : _a2.startsWith("idb:");
|
|
3422
|
+
});
|
|
3423
|
+
if (idbRefs.length > 0) this.loadScreenshotsFromIdb(idbRefs);
|
|
3424
|
+
}
|
|
3425
|
+
} catch (e) {
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
openIdb() {
|
|
3429
|
+
return new Promise((resolve, reject) => {
|
|
3430
|
+
const req = indexedDB.open(_Instruckt.IDB_NAME, 1);
|
|
3431
|
+
req.onupgradeneeded = () => {
|
|
3432
|
+
const db = req.result;
|
|
3433
|
+
if (!db.objectStoreNames.contains(_Instruckt.IDB_STORE)) {
|
|
3434
|
+
db.createObjectStore(_Instruckt.IDB_STORE);
|
|
3435
|
+
}
|
|
3436
|
+
};
|
|
3437
|
+
req.onsuccess = () => resolve(req.result);
|
|
3438
|
+
req.onerror = () => reject(req.error);
|
|
3439
|
+
});
|
|
3440
|
+
}
|
|
3441
|
+
async saveScreenshotsToIdb(screenshots) {
|
|
3442
|
+
try {
|
|
3443
|
+
const db = await this.openIdb();
|
|
3444
|
+
const tx = db.transaction(_Instruckt.IDB_STORE, "readwrite");
|
|
3445
|
+
const store = tx.objectStore(_Instruckt.IDB_STORE);
|
|
3446
|
+
for (const [id, dataUri] of screenshots) {
|
|
3447
|
+
store.put(dataUri, id);
|
|
3448
|
+
}
|
|
3449
|
+
db.close();
|
|
3450
|
+
} catch (e) {
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
async loadScreenshotsFromIdb(annotations) {
|
|
3454
|
+
try {
|
|
3455
|
+
const db = await this.openIdb();
|
|
3456
|
+
const tx = db.transaction(_Instruckt.IDB_STORE, "readonly");
|
|
3457
|
+
const store = tx.objectStore(_Instruckt.IDB_STORE);
|
|
3458
|
+
for (const a of annotations) {
|
|
3459
|
+
const id = a.screenshot.replace("idb:", "");
|
|
3460
|
+
const req = store.get(id);
|
|
3461
|
+
req.onsuccess = () => {
|
|
3462
|
+
if (req.result) a.screenshot = req.result;
|
|
3463
|
+
};
|
|
3464
|
+
}
|
|
3465
|
+
await new Promise((resolve) => {
|
|
3466
|
+
tx.oncomplete = () => resolve();
|
|
3467
|
+
});
|
|
3468
|
+
db.close();
|
|
3469
|
+
this.syncMarkers();
|
|
3394
3470
|
} catch (e) {
|
|
3395
3471
|
}
|
|
3396
3472
|
}
|
|
@@ -3863,19 +3939,21 @@ No open annotations.`;
|
|
|
3863
3939
|
const screenshotPath = (_e = this.config.screenshotPath) != null ? _e : "storage/app/_instruckt/";
|
|
3864
3940
|
lines.push(`- Screenshot: \`${screenshotPath}${a.screenshot}\``);
|
|
3865
3941
|
} else {
|
|
3866
|
-
lines.push(`- Screenshot:
|
|
3942
|
+
lines.push(`- Screenshot: `);
|
|
3867
3943
|
}
|
|
3868
3944
|
}
|
|
3869
3945
|
lines.push("");
|
|
3870
3946
|
});
|
|
3871
3947
|
}
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3948
|
+
if (this.config.mcp) {
|
|
3949
|
+
const hasScreenshots = pending.some((a) => a.screenshot && !a.screenshot.startsWith("data:"));
|
|
3950
|
+
lines.push("---");
|
|
3951
|
+
lines.push("");
|
|
3952
|
+
if (hasScreenshots) {
|
|
3953
|
+
lines.push("Use the `instruckt.get_screenshot` MCP tool to view screenshots. After making changes, use `instruckt.resolve` to mark each annotation as resolved.");
|
|
3954
|
+
} else {
|
|
3955
|
+
lines.push("After making changes, use the `instruckt.resolve` MCP tool to mark each annotation as resolved.");
|
|
3956
|
+
}
|
|
3879
3957
|
}
|
|
3880
3958
|
return lines.join("\n").trim();
|
|
3881
3959
|
}
|
|
@@ -3898,8 +3976,8 @@ No open annotations.`;
|
|
|
3898
3976
|
if (this.pollTimer !== null) clearInterval(this.pollTimer);
|
|
3899
3977
|
}
|
|
3900
3978
|
};
|
|
3901
|
-
|
|
3902
|
-
_Instruckt.
|
|
3979
|
+
_Instruckt.IDB_NAME = "instruckt";
|
|
3980
|
+
_Instruckt.IDB_STORE = "screenshots";
|
|
3903
3981
|
var Instruckt = _Instruckt;
|
|
3904
3982
|
|
|
3905
3983
|
// src/index.ts
|