autopreso 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.
@@ -0,0 +1,28 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Auto Preso</title>
7
+ <link rel="stylesheet" href="https://esm.sh/@excalidraw/excalidraw@0.18.0/dist/dev/index.css" />
8
+ <link rel="stylesheet" href="/style.css" />
9
+ <script>
10
+ window.EXCALIDRAW_ASSET_PATH = "https://esm.sh/@excalidraw/excalidraw@0.18.0/dist/prod/";
11
+ </script>
12
+ <script type="importmap">
13
+ {
14
+ "imports": {
15
+ "@excalidraw/excalidraw": "https://esm.sh/@excalidraw/excalidraw@0.18.0?deps=react@19.2.0,react-dom@19.2.0",
16
+ "react": "https://esm.sh/react@19.2.0",
17
+ "react-dom": "https://esm.sh/react-dom@19.2.0",
18
+ "react-dom/client": "https://esm.sh/react-dom@19.2.0/client",
19
+ "react/jsx-runtime": "https://esm.sh/react@19.2.0/jsx-runtime"
20
+ }
21
+ }
22
+ </script>
23
+ </head>
24
+ <body>
25
+ <div id="app"></div>
26
+ <script type="module" src="/app.js"></script>
27
+ </body>
28
+ </html>
@@ -0,0 +1,37 @@
1
+ export const STARTER_ELEMENTS = [
2
+ {
3
+ type: "text",
4
+ id: "title",
5
+ x: 72,
6
+ y: 68,
7
+ width: 320,
8
+ height: 34,
9
+ text: "AutoPreso",
10
+ fontSize: 28,
11
+ fontFamily: 1,
12
+ strokeColor: "#1e1e1e",
13
+ },
14
+ {
15
+ type: "rectangle",
16
+ id: "starter-card",
17
+ x: 72,
18
+ y: 112,
19
+ width: 430,
20
+ height: 88,
21
+ backgroundColor: "#fff3bf",
22
+ fillStyle: "solid",
23
+ roundness: { type: 3 },
24
+ },
25
+ {
26
+ type: "text",
27
+ id: "starter-hint",
28
+ x: 105,
29
+ y: 130,
30
+ width: 340,
31
+ height: 50,
32
+ text: "Start listening, then\ntalk through an idea.",
33
+ fontSize: 18,
34
+ fontFamily: 1,
35
+ strokeColor: "#1e1e1e",
36
+ },
37
+ ];
@@ -0,0 +1,535 @@
1
+ @font-face {
2
+ font-family: "Excalifont";
3
+ src: url("https://cdn.jsdelivr.net/npm/@excalidraw/excalidraw@0.18.0/dist/prod/fonts/Excalifont/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2") format("woff2");
4
+ font-weight: 400;
5
+ font-style: normal;
6
+ font-display: swap;
7
+ }
8
+
9
+ * {
10
+ box-sizing: border-box;
11
+ }
12
+
13
+ body {
14
+ margin: 0;
15
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
16
+ background: #f8f7f4;
17
+ color: #1e1e1e;
18
+ }
19
+
20
+ .shell {
21
+ display: grid;
22
+ grid-template-columns: minmax(0, 1fr) 360px;
23
+ height: 100vh;
24
+ width: 100vw;
25
+ }
26
+
27
+ .canvas-wrap {
28
+ min-width: 0;
29
+ height: 100vh;
30
+ position: relative;
31
+ overflow: hidden;
32
+ }
33
+
34
+ .stage-overlay {
35
+ position: absolute;
36
+ left: 0;
37
+ right: 0;
38
+ bottom: 0;
39
+ display: flex;
40
+ flex-direction: column;
41
+ align-items: center;
42
+ justify-content: flex-end;
43
+ gap: 14px;
44
+ padding: 0 8% 36px;
45
+ pointer-events: none;
46
+ opacity: 0;
47
+ transform: translateY(8px);
48
+ transition: opacity 280ms ease, transform 280ms ease;
49
+ z-index: 5;
50
+ }
51
+
52
+ .stage-overlay.visible {
53
+ opacity: 1;
54
+ transform: translateY(0);
55
+ }
56
+
57
+ .waveform-canvas {
58
+ width: 100%;
59
+ max-width: 720px;
60
+ height: 64px;
61
+ display: block;
62
+ }
63
+
64
+ .caption-pill {
65
+ pointer-events: none;
66
+ font-family: "Excalifont", "Cascadia", "Comic Sans MS", cursive, sans-serif;
67
+ font-size: 36px;
68
+ line-height: 1.3;
69
+ font-weight: 400;
70
+ letter-spacing: -0.01em;
71
+ color: #fafafa;
72
+ background: rgba(15, 15, 18, 0.78);
73
+ backdrop-filter: blur(14px) saturate(140%);
74
+ -webkit-backdrop-filter: blur(14px) saturate(140%);
75
+ border: 1px solid rgba(255, 255, 255, 0.06);
76
+ border-radius: 16px;
77
+ padding: 12px 22px;
78
+ max-width: min(880px, 84%);
79
+ text-align: center;
80
+ text-wrap: balance;
81
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35), 0 2px 8px rgba(0, 0, 0, 0.18);
82
+ animation: caption-rise 220ms ease;
83
+ }
84
+
85
+ @keyframes caption-rise {
86
+ from { opacity: 0; transform: translateY(6px); }
87
+ to { opacity: 1; transform: translateY(0); }
88
+ }
89
+
90
+ .panel {
91
+ display: flex;
92
+ flex-direction: column;
93
+ gap: 18px;
94
+ border-left: 1px solid #dedbd2;
95
+ background: #fffdf8;
96
+ padding: 22px;
97
+ overflow: auto;
98
+ position: relative;
99
+ }
100
+
101
+ .brand h1 {
102
+ margin: 0;
103
+ font-family: "Excalifont", "Cascadia", "Comic Sans MS", cursive, sans-serif;
104
+ font-size: 28px;
105
+ font-weight: 400;
106
+ letter-spacing: 0;
107
+ }
108
+
109
+ .brand p {
110
+ margin: 6px 0 0;
111
+ color: #6d675e;
112
+ font-size: 14px;
113
+ line-height: 1.4;
114
+ }
115
+
116
+ .controls {
117
+ display: flex;
118
+ flex-direction: column;
119
+ gap: 8px;
120
+ }
121
+
122
+ .start-preso {
123
+ background: #1f6feb;
124
+ border-color: #1f6feb;
125
+ font-size: 15px;
126
+ padding: 13px 14px;
127
+ letter-spacing: 0.01em;
128
+ }
129
+
130
+ .start-preso:hover:not(:disabled) {
131
+ background: #1a5fc9;
132
+ border-color: #1a5fc9;
133
+ }
134
+
135
+ .back-to-staging {
136
+ background: transparent;
137
+ color: #1e1e1e;
138
+ border: 1px solid #1e1e1e;
139
+ font-weight: 600;
140
+ font-size: 14px;
141
+ }
142
+
143
+ .back-to-staging:hover:not(:disabled) {
144
+ background: #f3f0e7;
145
+ }
146
+
147
+ .fullscreen-toggle {
148
+ position: absolute;
149
+ top: 14px;
150
+ right: 14px;
151
+ z-index: 10;
152
+ background: transparent;
153
+ color: #6d675e;
154
+ border: none;
155
+ padding: 4px 6px;
156
+ font-size: 16px;
157
+ line-height: 1;
158
+ border-radius: 6px;
159
+ cursor: pointer;
160
+ }
161
+
162
+ .fullscreen-toggle:hover:not(:disabled) {
163
+ background: #f3f0e7;
164
+ color: #1e1e1e;
165
+ }
166
+
167
+ .shell:fullscreen {
168
+ background: #f8f7f4;
169
+ width: 100vw;
170
+ height: 100vh;
171
+ }
172
+
173
+ .brand-row {
174
+ display: flex;
175
+ align-items: center;
176
+ gap: 10px;
177
+ }
178
+
179
+ .mode-toggle {
180
+ display: inline-flex;
181
+ align-items: stretch;
182
+ padding: 2px;
183
+ gap: 0;
184
+ border-radius: 999px;
185
+ background: #f1ede2;
186
+ border: 1px solid #dedbd2;
187
+ line-height: 1;
188
+ transition: background 200ms ease, border-color 200ms ease;
189
+ }
190
+
191
+ .mode-toggle-option {
192
+ font-family: inherit;
193
+ font-size: 10.5px;
194
+ font-weight: 650;
195
+ letter-spacing: 0.08em;
196
+ text-transform: uppercase;
197
+ padding: 4px 10px;
198
+ border: none;
199
+ border-radius: 999px;
200
+ background: transparent;
201
+ color: #8a8479;
202
+ cursor: pointer;
203
+ transition: background 180ms ease, color 180ms ease, box-shadow 180ms ease;
204
+ }
205
+
206
+ .mode-toggle-option:hover:not(:disabled):not(.active) {
207
+ color: #1e1e1e;
208
+ }
209
+
210
+ .mode-toggle-option:disabled {
211
+ cursor: default;
212
+ }
213
+
214
+ .mode-toggle-option.active {
215
+ cursor: default;
216
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
217
+ }
218
+
219
+ .mode-toggle-staging .mode-toggle-option.active {
220
+ background: #e7f5ff;
221
+ color: #1862ab;
222
+ }
223
+
224
+ .mode-toggle-live {
225
+ background: #fff1f1;
226
+ border-color: #fca5a5;
227
+ }
228
+
229
+ .mode-toggle-live .mode-toggle-option.active {
230
+ background: #fee2e2;
231
+ color: #b91c1c;
232
+ }
233
+
234
+ .shell.mode-staging .canvas-wrap {
235
+ background: #fbfaf5;
236
+ }
237
+
238
+
239
+ .reset-session {
240
+ background: transparent;
241
+ color: #6d675e;
242
+ border: 1px solid #dedbd2;
243
+ font-weight: 500;
244
+ font-size: 13px;
245
+ padding: 8px 12px;
246
+ }
247
+
248
+ .reset-session:hover:not(:disabled) {
249
+ background: #f3f0e7;
250
+ color: #1e1e1e;
251
+ }
252
+
253
+ .reset-session.confirming {
254
+ background: #fef2f2;
255
+ border-color: #fca5a5;
256
+ color: #b91c1c;
257
+ font-weight: 650;
258
+ animation: reset-pulse 1.6s ease-in-out infinite;
259
+ }
260
+
261
+ @keyframes reset-pulse {
262
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.25); }
263
+ 50% { box-shadow: 0 0 0 4px rgba(239, 68, 68, 0); }
264
+ }
265
+
266
+ button {
267
+ align-items: center;
268
+ border: 1px solid #1e1e1e;
269
+ border-radius: 10px;
270
+ background: #1e1e1e;
271
+ color: white;
272
+ cursor: pointer;
273
+ display: inline-flex;
274
+ gap: 8px;
275
+ justify-content: center;
276
+ font: inherit;
277
+ font-weight: 650;
278
+ padding: 11px 12px;
279
+ }
280
+
281
+ .record-toggle.recording {
282
+ background: #b91c1c;
283
+ border-color: #b91c1c;
284
+ }
285
+
286
+ .record-icon {
287
+ font-size: 12px;
288
+ line-height: 1;
289
+ }
290
+
291
+ .listen-controls {
292
+ display: flex;
293
+ flex-direction: column;
294
+ gap: 6px;
295
+ }
296
+
297
+ .warmup-skip {
298
+ background: transparent;
299
+ color: #6d675e;
300
+ border: 1px dashed #cfcabf;
301
+ font-weight: 500;
302
+ font-size: 12.5px;
303
+ padding: 6px 10px;
304
+ align-self: stretch;
305
+ }
306
+
307
+ .warmup-skip:hover:not(:disabled) {
308
+ background: #f3f0e7;
309
+ color: #1e1e1e;
310
+ border-style: solid;
311
+ }
312
+
313
+ .warmup-warning {
314
+ font-size: 12px;
315
+ color: #92400e;
316
+ background: #fef3c7;
317
+ border: 1px solid #fde68a;
318
+ border-radius: 8px;
319
+ padding: 6px 10px;
320
+ line-height: 1.4;
321
+ }
322
+
323
+ button.secondary {
324
+ background: transparent;
325
+ color: #1e1e1e;
326
+ }
327
+
328
+ button:disabled {
329
+ cursor: not-allowed;
330
+ opacity: 0.45;
331
+ }
332
+
333
+ .status-card {
334
+ border: 1px solid #e4e0d6;
335
+ border-radius: 16px;
336
+ background: white;
337
+ padding: 6px 8px;
338
+ box-shadow: 0 10px 30px rgba(30, 30, 30, 0.04);
339
+ }
340
+
341
+ .status-row-wrap + .status-row-wrap {
342
+ border-top: 1px solid #f3f0e9;
343
+ }
344
+
345
+ .status-row-wrap.expanded + .status-row-wrap,
346
+ .status-row-wrap + .status-row-wrap.expanded {
347
+ border-top-color: transparent;
348
+ }
349
+
350
+ .status-row {
351
+ display: flex;
352
+ align-items: center;
353
+ gap: 10px;
354
+ font-size: 13.5px;
355
+ padding: 8px 8px;
356
+ border-radius: 10px;
357
+ transition: background 120ms ease;
358
+ }
359
+
360
+ .status-row.clickable {
361
+ cursor: pointer;
362
+ }
363
+
364
+ .status-row.clickable:hover {
365
+ background: #f8f6ef;
366
+ }
367
+
368
+ .status-row.clickable:focus-visible {
369
+ outline: 2px solid rgba(30, 30, 30, 0.15);
370
+ outline-offset: -1px;
371
+ }
372
+
373
+ .status-row.open {
374
+ background: #f3f0e7;
375
+ }
376
+
377
+ .label {
378
+ color: #766f63;
379
+ flex-shrink: 0;
380
+ width: 50px;
381
+ }
382
+
383
+ .value {
384
+ flex: 1;
385
+ text-align: right;
386
+ font-weight: 650;
387
+ white-space: nowrap;
388
+ overflow: hidden;
389
+ text-overflow: ellipsis;
390
+ }
391
+
392
+ .chevron {
393
+ color: #9ca3af;
394
+ font-size: 16px;
395
+ line-height: 1;
396
+ flex-shrink: 0;
397
+ transition: transform 160ms ease;
398
+ display: inline-block;
399
+ }
400
+
401
+ .status-row.open .chevron {
402
+ transform: rotate(90deg);
403
+ color: #6d675e;
404
+ }
405
+
406
+ .dot {
407
+ width: 8px;
408
+ height: 8px;
409
+ border-radius: 999px;
410
+ background: #cfcabf;
411
+ flex-shrink: 0;
412
+ }
413
+
414
+ .dot.active {
415
+ background: #22c55e;
416
+ animation: dot-pulse 1.6s ease-in-out infinite;
417
+ }
418
+
419
+ .dot.error {
420
+ background: #ef4444;
421
+ box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.15);
422
+ }
423
+
424
+ @keyframes dot-pulse {
425
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.5); }
426
+ 50% { box-shadow: 0 0 0 6px rgba(34, 197, 94, 0); }
427
+ }
428
+
429
+ .editor {
430
+ margin: 2px 8px 10px 26px;
431
+ padding: 10px 12px;
432
+ border-left: 2px solid #e4e0d6;
433
+ background: #fbfaf5;
434
+ border-radius: 0 10px 10px 0;
435
+ }
436
+
437
+ .editor-grid {
438
+ display: flex;
439
+ flex-direction: column;
440
+ gap: 10px;
441
+ font-size: 13px;
442
+ }
443
+
444
+ .field {
445
+ display: flex;
446
+ flex-direction: column;
447
+ gap: 4px;
448
+ }
449
+
450
+ .field-label {
451
+ color: #766f63;
452
+ font-size: 11.5px;
453
+ text-transform: uppercase;
454
+ letter-spacing: 0.04em;
455
+ }
456
+
457
+ .editor select,
458
+ .editor input[type="text"],
459
+ .editor input[type="password"] {
460
+ font: inherit;
461
+ padding: 7px 10px;
462
+ border: 1px solid #dedbd2;
463
+ border-radius: 8px;
464
+ background: #fffdf8;
465
+ color: #1e1e1e;
466
+ }
467
+
468
+ .editor select {
469
+ appearance: none;
470
+ -webkit-appearance: none;
471
+ -moz-appearance: none;
472
+ padding-right: 30px;
473
+ background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'><path d='M3 4.5l3 3 3-3' stroke='%239ca3af' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
474
+ background-repeat: no-repeat;
475
+ background-position: right 12px center;
476
+ background-size: 11px;
477
+ cursor: pointer;
478
+ }
479
+
480
+ .editor select:focus,
481
+ .editor input:focus {
482
+ outline: 2px solid rgba(30, 30, 30, 0.15);
483
+ outline-offset: 1px;
484
+ }
485
+
486
+ .editor-hint {
487
+ color: #6d675e;
488
+ font-size: 12px;
489
+ background: #fff7e0;
490
+ border: 1px solid #f0e1b6;
491
+ border-radius: 8px;
492
+ padding: 8px 10px;
493
+ display: flex;
494
+ align-items: center;
495
+ }
496
+
497
+ .editor-error {
498
+ color: #b91c1c;
499
+ font-size: 12px;
500
+ }
501
+
502
+ .editor-actions {
503
+ display: flex;
504
+ gap: 8px;
505
+ justify-content: flex-end;
506
+ margin-top: 4px;
507
+ }
508
+
509
+ .editor-actions button {
510
+ padding: 6px 12px;
511
+ font-size: 13px;
512
+ }
513
+
514
+ .error {
515
+ color: #b91c1c;
516
+ font-size: 13px;
517
+ line-height: 1.4;
518
+ }
519
+
520
+ @media (max-width: 860px) {
521
+ .shell {
522
+ grid-template-columns: 1fr;
523
+ grid-template-rows: minmax(0, 1fr) auto;
524
+ }
525
+
526
+ .canvas-wrap {
527
+ height: 62vh;
528
+ }
529
+
530
+ .panel {
531
+ height: 38vh;
532
+ border-left: 0;
533
+ border-top: 1px solid #dedbd2;
534
+ }
535
+ }
@@ -0,0 +1,10 @@
1
+ const MAX_COMMITTED_TRANSCRIPTS = 20;
2
+
3
+ export function appendCommittedTranscript(items, text, limit = MAX_COMMITTED_TRANSCRIPTS) {
4
+ return [...items, text].filter(Boolean).slice(-limit);
5
+ }
6
+
7
+ export function scrollTranscriptToBottom(element) {
8
+ if (!element) return;
9
+ element.scrollTop = element.scrollHeight;
10
+ }
@@ -0,0 +1,106 @@
1
+ import { createOpenAI } from "@ai-sdk/openai";
2
+
3
+ import { DEFAULT_CODEX_BASE_URL, createCodexFetch, readCodexCliAuthSync } from "./codex-auth.js";
4
+
5
+ const DEFAULT_OPENAI_AGENT_MODEL = "gpt-5.5";
6
+ const DEFAULT_OPENAI_REASONING_EFFORT = "low";
7
+ const DEFAULT_OLLAMA_BASE_URL = "http://localhost:11434/v1";
8
+ const OPENAI_REASONING_EFFORTS = new Set(["none", "low", "medium", "high", "xhigh"]);
9
+
10
+ export function defaultWhiteboardAgentProvider(options = {}) {
11
+ return {
12
+ provider: "openai",
13
+ model: DEFAULT_OPENAI_AGENT_MODEL,
14
+ apiKey: options.openaiApiKey,
15
+ reasoningEffort: DEFAULT_OPENAI_REASONING_EFFORT,
16
+ };
17
+ }
18
+
19
+ export function resolveAgentProviderFromSettings({ settings, env = process.env }) {
20
+ const provider = settings.agent.provider;
21
+
22
+ if (provider === "ollama") {
23
+ const model = (settings.agent.ollama.model ?? "").trim();
24
+ if (!model) throw new Error("Ollama model is not configured. Set it in the agent settings.");
25
+ return {
26
+ provider: "ollama",
27
+ model,
28
+ baseURL: withoutTrailingSlash(settings.agent.ollama.baseURL ?? DEFAULT_OLLAMA_BASE_URL),
29
+ apiKey: "ollama",
30
+ };
31
+ }
32
+
33
+ if (provider === "codex") {
34
+ const codexAuth = readCodexCliAuthSync(env);
35
+ if (!codexAuth) throw new Error("Codex CLI auth not found. Run `codex` and sign in with ChatGPT.");
36
+ const codexModel = resolveCodexModel(settings.agent.codex.model || DEFAULT_OPENAI_AGENT_MODEL);
37
+ return {
38
+ provider: "codex",
39
+ ...codexModel,
40
+ baseURL: withoutTrailingSlash(settings.agent.codex.baseURL ?? DEFAULT_CODEX_BASE_URL),
41
+ apiKey: codexAuth.accessToken,
42
+ reasoningEffort: validateReasoningEffort(settings.agent.openai.reasoningEffort),
43
+ };
44
+ }
45
+
46
+ const apiKey = (settings.apiKeys?.openai ?? "").trim() || cleanEnvValue(env.OPENAI_API_KEY);
47
+ if (!apiKey) throw new Error("OpenAI API key is not configured. Add it in the agent settings.");
48
+ return {
49
+ provider: "openai",
50
+ model: settings.agent.openai.model || DEFAULT_OPENAI_AGENT_MODEL,
51
+ apiKey,
52
+ reasoningEffort: validateReasoningEffort(settings.agent.openai.reasoningEffort),
53
+ };
54
+ }
55
+
56
+ function validateReasoningEffort(reasoningEffort) {
57
+ const value = reasoningEffort || DEFAULT_OPENAI_REASONING_EFFORT;
58
+ if (!OPENAI_REASONING_EFFORTS.has(value)) {
59
+ throw new Error(`Unsupported reasoning effort "${value}". Use none, low, medium, high, or xhigh.`);
60
+ }
61
+ return value;
62
+ }
63
+
64
+ export function createWhiteboardAgentModel(agentProvider) {
65
+ if (agentProvider.provider === "ollama") {
66
+ const ollama = createOpenAI({
67
+ name: "ollama",
68
+ baseURL: agentProvider.baseURL,
69
+ apiKey: agentProvider.apiKey,
70
+ });
71
+ return ollama.chat(agentProvider.model);
72
+ }
73
+
74
+ if (agentProvider.provider === "codex") {
75
+ const codex = createOpenAI({
76
+ name: "openai-codex",
77
+ baseURL: agentProvider.baseURL,
78
+ apiKey: agentProvider.apiKey,
79
+ fetch: createCodexFetch(),
80
+ });
81
+ return codex.responses(agentProvider.model);
82
+ }
83
+
84
+ const openai = createOpenAI({ apiKey: agentProvider.apiKey });
85
+ return openai(agentProvider.model);
86
+ }
87
+
88
+ function cleanEnvValue(value) {
89
+ const trimmedValue = value?.trim();
90
+ return trimmedValue || undefined;
91
+ }
92
+
93
+ function withoutTrailingSlash(value) {
94
+ return value.replace(/\/+$/, "");
95
+ }
96
+
97
+ function resolveCodexModel(requestedModel) {
98
+ if (requestedModel.endsWith("-fast")) {
99
+ return {
100
+ model: requestedModel.slice(0, -"-fast".length),
101
+ requestedModel,
102
+ serviceTier: "priority",
103
+ };
104
+ }
105
+ return { model: requestedModel };
106
+ }