@hybridlabor-api/aos 4.4.2-beta.7 → 4.4.2-beta.9
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/CLAUDE.md +0 -104
- package/installer.js +97 -13
- package/package.json +1 -1
- package/skills/global_config/live-preview-canvas/SKILL.md +19 -0
- package/skills/global_config/live-preview-canvas/examples/preview_interactive_dashboard.html +406 -0
- package/skills/global_config/live-preview-canvas/resources/template.html +291 -0
- package/skills/global_config/live-preview-canvas/scripts/base_template.html +178 -0
- package/skills/global_config/openwiki-skill/scripts/install_daemon.ps1 +81 -28
- package/skills/global_config/openwiki-skill/scripts/install_daemon.sh +71 -25
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" class="dark">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Live Preview Canvas — Interactive Mockup Template</title>
|
|
7
|
+
<!-- Tailwind CSS via CDN for rapid, consistent styling -->
|
|
8
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
9
|
+
<script>
|
|
10
|
+
tailwind.config = {
|
|
11
|
+
darkMode: 'class',
|
|
12
|
+
theme: {
|
|
13
|
+
extend: {
|
|
14
|
+
colors: {
|
|
15
|
+
brand: {
|
|
16
|
+
50: '#f5f7ff',
|
|
17
|
+
500: '#4f46e5',
|
|
18
|
+
600: '#4338ca',
|
|
19
|
+
700: '#3730a3'
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
</script>
|
|
26
|
+
<style>
|
|
27
|
+
/* Baseline styles & reset */
|
|
28
|
+
body {
|
|
29
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
30
|
+
margin: 0;
|
|
31
|
+
padding: 0;
|
|
32
|
+
background-color: #0b0f19;
|
|
33
|
+
color: #e2e8f0;
|
|
34
|
+
min-height: 100vh;
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
}
|
|
38
|
+
/* Stable highlighting for Plan Canvas element selection */
|
|
39
|
+
[data-preview-anchor]:hover {
|
|
40
|
+
outline: 2px dashed rgba(99, 102, 241, 0.4);
|
|
41
|
+
outline-offset: 2px;
|
|
42
|
+
}
|
|
43
|
+
</style>
|
|
44
|
+
</head>
|
|
45
|
+
<body class="bg-slate-950 text-slate-100 antialiased selection:bg-indigo-500 selection:text-white pb-24">
|
|
46
|
+
|
|
47
|
+
<!-- =========================================================================
|
|
48
|
+
1. Top Header Bar
|
|
49
|
+
Semantic IDs and data-component attributes enable crystal-clear annotations
|
|
50
|
+
========================================================================= -->
|
|
51
|
+
<header id="preview-header" data-component="header" class="border-b border-slate-800 bg-slate-900/80 backdrop-blur sticky top-0 z-40 px-6 py-4 flex items-center justify-between">
|
|
52
|
+
<div class="flex items-center space-x-3">
|
|
53
|
+
<div class="h-8 w-8 rounded-lg bg-indigo-600 flex items-center justify-center font-bold text-white shadow-lg shadow-indigo-500/30">
|
|
54
|
+
P
|
|
55
|
+
</div>
|
|
56
|
+
<div>
|
|
57
|
+
<h1 class="text-base font-semibold text-white leading-tight">Feature Preview: <span id="feature-title" class="text-indigo-400">Component Mockup</span></h1>
|
|
58
|
+
<p class="text-xs text-slate-400">Interactive live prototype for visual review & red-pinning</p>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<!-- State Indicator / Action pill -->
|
|
63
|
+
<div class="flex items-center space-x-3">
|
|
64
|
+
<span id="active-state-badge" class="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-emerald-950 text-emerald-300 border border-emerald-800">
|
|
65
|
+
<span class="w-1.5 h-1.5 mr-1.5 rounded-full bg-emerald-400 animate-pulse"></span>
|
|
66
|
+
Interactive (In-Memory)
|
|
67
|
+
</span>
|
|
68
|
+
<button id="btn-reset-mock" onclick="resetMockState()" class="text-xs bg-slate-800 hover:bg-slate-700 text-slate-300 px-3 py-1.5 rounded border border-slate-700 transition">
|
|
69
|
+
Reset State
|
|
70
|
+
</button>
|
|
71
|
+
</div>
|
|
72
|
+
</header>
|
|
73
|
+
|
|
74
|
+
<!-- =========================================================================
|
|
75
|
+
2. Main Canvas Content Area
|
|
76
|
+
========================================================================= -->
|
|
77
|
+
<main class="flex-1 max-w-5xl w-full mx-auto px-6 py-8">
|
|
78
|
+
|
|
79
|
+
<!-- Information banner for reviewers -->
|
|
80
|
+
<section id="review-banner" data-component="instruction-banner" class="mb-6 p-4 rounded-xl bg-slate-900 border border-slate-800 flex items-start justify-between">
|
|
81
|
+
<div>
|
|
82
|
+
<h2 class="text-sm font-semibold text-slate-200">Visual Audit Mode Active</h2>
|
|
83
|
+
<p class="text-xs text-slate-400 mt-1 leading-relaxed">
|
|
84
|
+
Click any element to add a pinned comment, or use the bottom switcher to evaluate different layout variants and error states.
|
|
85
|
+
</p>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="text-right text-xs text-slate-500 font-mono">
|
|
88
|
+
Hotkeys: <kbd class="px-1.5 py-0.5 bg-slate-800 rounded border border-slate-700 text-slate-300">←</kbd> <kbd class="px-1.5 py-0.5 bg-slate-800 rounded border border-slate-700 text-slate-300">→</kbd> to cycle
|
|
89
|
+
</div>
|
|
90
|
+
</section>
|
|
91
|
+
|
|
92
|
+
<!-- VARIANT A: Standard View -->
|
|
93
|
+
<div id="view-variant-a" class="preview-variant space-y-6">
|
|
94
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
95
|
+
<!-- Card 1 -->
|
|
96
|
+
<article id="card-stats-1" data-component="stat-card" class="p-5 rounded-xl bg-slate-900 border border-slate-800 shadow-sm hover:border-slate-700 transition">
|
|
97
|
+
<div class="text-xs font-medium text-slate-400 uppercase tracking-wider">Metric Alpha</div>
|
|
98
|
+
<div class="text-2xl font-bold text-white mt-2">1,482</div>
|
|
99
|
+
<div class="mt-2 text-xs text-emerald-400 flex items-center font-medium">
|
|
100
|
+
<span>↑ +12.4%</span>
|
|
101
|
+
<span class="text-slate-500 ml-1.5">vs last week</span>
|
|
102
|
+
</div>
|
|
103
|
+
</article>
|
|
104
|
+
|
|
105
|
+
<!-- Card 2 -->
|
|
106
|
+
<article id="card-stats-2" data-component="stat-card" class="p-5 rounded-xl bg-slate-900 border border-slate-800 shadow-sm hover:border-slate-700 transition">
|
|
107
|
+
<div class="text-xs font-medium text-slate-400 uppercase tracking-wider">Latency / Cook Cost</div>
|
|
108
|
+
<div class="text-2xl font-bold text-white mt-2">4.2 ms</div>
|
|
109
|
+
<div class="mt-2 text-xs text-indigo-400 flex items-center font-medium">
|
|
110
|
+
<span>Optimal 60 FPS</span>
|
|
111
|
+
</div>
|
|
112
|
+
</article>
|
|
113
|
+
|
|
114
|
+
<!-- Card 3 -->
|
|
115
|
+
<article id="card-stats-3" data-component="stat-card" class="p-5 rounded-xl bg-slate-900 border border-slate-800 shadow-sm hover:border-slate-700 transition">
|
|
116
|
+
<div class="text-xs font-medium text-slate-400 uppercase tracking-wider">Active Handlers</div>
|
|
117
|
+
<div class="text-2xl font-bold text-white mt-2" id="counter-value">9</div>
|
|
118
|
+
<div class="mt-2 flex space-x-2">
|
|
119
|
+
<button onclick="adjustCounter(1)" class="text-xs bg-indigo-600/30 hover:bg-indigo-600 text-indigo-200 px-2.5 py-1 rounded transition">+ Increment</button>
|
|
120
|
+
<button onclick="adjustCounter(-1)" class="text-xs bg-slate-800 hover:bg-slate-700 text-slate-300 px-2.5 py-1 rounded transition">- Decrement</button>
|
|
121
|
+
</div>
|
|
122
|
+
</article>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<!-- Action Panel / Primary Form -->
|
|
126
|
+
<section id="main-panel" data-component="action-panel" class="p-6 rounded-xl bg-slate-900 border border-slate-800">
|
|
127
|
+
<h3 class="text-base font-semibold text-white mb-4">Interactive Controls & Configuration</h3>
|
|
128
|
+
<div class="space-y-4">
|
|
129
|
+
<div>
|
|
130
|
+
<label for="input-channel" class="block text-xs font-medium text-slate-300 mb-1.5">Channel Name</label>
|
|
131
|
+
<input type="text" id="input-channel" value="live-preview/stream-01" class="w-full bg-slate-950 border border-slate-700 rounded-lg px-3.5 py-2 text-sm text-slate-100 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition">
|
|
132
|
+
</div>
|
|
133
|
+
<div class="flex items-center space-x-4">
|
|
134
|
+
<button id="btn-primary-action" onclick="simulateAction()" class="bg-indigo-600 hover:bg-indigo-500 text-white text-sm font-medium px-4 py-2 rounded-lg shadow-md shadow-indigo-600/20 transition flex items-center space-x-2">
|
|
135
|
+
<span>Execute Action</span>
|
|
136
|
+
</button>
|
|
137
|
+
<span id="action-status" class="text-xs text-slate-400">Ready</span>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</section>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<!-- VARIANT B: Alternative Layout (Dense List) -->
|
|
144
|
+
<div id="view-variant-b" class="preview-variant hidden space-y-4">
|
|
145
|
+
<div id="dense-list-container" data-component="dense-list" class="rounded-xl bg-slate-900 border border-slate-800 divide-y divide-slate-800">
|
|
146
|
+
<div class="p-4 flex items-center justify-between">
|
|
147
|
+
<div>
|
|
148
|
+
<span class="text-sm font-medium text-white">Item #1: Audio Reactive Engine</span>
|
|
149
|
+
<p class="text-xs text-slate-400">FFT Peak extraction on CHOP chain 4</p>
|
|
150
|
+
</div>
|
|
151
|
+
<span class="text-xs bg-emerald-950 text-emerald-400 px-2 py-0.5 rounded border border-emerald-800">Active</span>
|
|
152
|
+
</div>
|
|
153
|
+
<div class="p-4 flex items-center justify-between">
|
|
154
|
+
<div>
|
|
155
|
+
<span class="text-sm font-medium text-white">Item #2: DMX Fixture Router</span>
|
|
156
|
+
<p class="text-xs text-slate-400">Universe 1-4 mapped to ArtNet node</p>
|
|
157
|
+
</div>
|
|
158
|
+
<span class="text-xs bg-slate-800 text-slate-400 px-2 py-0.5 rounded">Standby</span>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<!-- VARIANT C: Error / Edge-Case State -->
|
|
164
|
+
<div id="view-variant-c" class="preview-variant hidden">
|
|
165
|
+
<div id="error-boundary-mock" data-component="error-state" class="text-center py-12 px-6 rounded-xl bg-slate-900 border border-rose-900/40">
|
|
166
|
+
<div class="inline-flex h-12 w-12 rounded-full bg-rose-950/80 text-rose-400 items-center justify-center mb-3 border border-rose-800/60">
|
|
167
|
+
⚠️
|
|
168
|
+
</div>
|
|
169
|
+
<h3 class="text-base font-semibold text-white">Connection Stream Interrupted</h3>
|
|
170
|
+
<p class="text-xs text-slate-400 max-w-sm mx-auto mt-1.5 mb-6">
|
|
171
|
+
The upstream WebRTC signal timed out after 3000ms. Check loopback connection or proxy gateway.
|
|
172
|
+
</p>
|
|
173
|
+
<button onclick="switchVariant('A')" class="bg-rose-600 hover:bg-rose-500 text-white text-xs font-semibold px-4 py-2 rounded-lg transition shadow-md shadow-rose-600/20">
|
|
174
|
+
Retry & Return to Variant A
|
|
175
|
+
</button>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
</main>
|
|
180
|
+
|
|
181
|
+
<!-- =========================================================================
|
|
182
|
+
3. Floating Prototype Switcher Bar (Prototype Skill UI.md convention)
|
|
183
|
+
Allows immediate flipping between variants and states
|
|
184
|
+
========================================================================= -->
|
|
185
|
+
<footer id="prototype-switcher-bar" data-component="switcher-bar" class="fixed bottom-4 left-1/2 -translate-x-1/2 z-50 bg-slate-900/90 backdrop-blur-md border border-slate-700/80 shadow-2xl rounded-full px-3 py-2 flex items-center space-x-2">
|
|
186
|
+
<button onclick="cycleVariant(-1)" aria-label="Previous Variant" class="p-1.5 text-slate-400 hover:text-white hover:bg-slate-800 rounded-full transition">
|
|
187
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/></svg>
|
|
188
|
+
</button>
|
|
189
|
+
|
|
190
|
+
<div class="flex items-center space-x-1">
|
|
191
|
+
<button onclick="switchVariant('A')" id="tab-btn-A" class="switcher-pill px-3 py-1 rounded-full text-xs font-medium transition bg-indigo-600 text-white shadow-sm">
|
|
192
|
+
Variant A (Grid)
|
|
193
|
+
</button>
|
|
194
|
+
<button onclick="switchVariant('B')" id="tab-btn-B" class="switcher-pill px-3 py-1 rounded-full text-xs font-medium transition text-slate-400 hover:text-slate-200">
|
|
195
|
+
Variant B (List)
|
|
196
|
+
</button>
|
|
197
|
+
<button onclick="switchVariant('C')" id="tab-btn-C" class="switcher-pill px-3 py-1 rounded-full text-xs font-medium transition text-slate-400 hover:text-slate-200">
|
|
198
|
+
Variant C (Error)
|
|
199
|
+
</button>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<button onclick="cycleVariant(1)" aria-label="Next Variant" class="p-1.5 text-slate-400 hover:text-white hover:bg-slate-800 rounded-full transition">
|
|
203
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
|
|
204
|
+
</button>
|
|
205
|
+
</footer>
|
|
206
|
+
|
|
207
|
+
<!-- =========================================================================
|
|
208
|
+
4. In-Memory Mock Interaction Logic
|
|
209
|
+
========================================================================= -->
|
|
210
|
+
<script>
|
|
211
|
+
const VARIANTS = ['A', 'B', 'C'];
|
|
212
|
+
let currentVariantIndex = 0;
|
|
213
|
+
let counter = 9;
|
|
214
|
+
|
|
215
|
+
function getVariantFromUrl() {
|
|
216
|
+
const params = new URLSearchParams(window.location.search);
|
|
217
|
+
const v = params.get('variant');
|
|
218
|
+
return (v && VARIANTS.includes(v.toUpperCase())) ? v.toUpperCase() : 'A';
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function switchVariant(variantKey) {
|
|
222
|
+
const normalized = variantKey.toUpperCase();
|
|
223
|
+
currentVariantIndex = VARIANTS.indexOf(normalized);
|
|
224
|
+
if (currentVariantIndex === -1) currentVariantIndex = 0;
|
|
225
|
+
|
|
226
|
+
// Update visibility
|
|
227
|
+
document.querySelectorAll('.preview-variant').forEach(el => el.classList.add('hidden'));
|
|
228
|
+
const activeEl = document.getElementById(`view-variant-${normalized.toLowerCase()}`);
|
|
229
|
+
if (activeEl) activeEl.classList.remove('hidden');
|
|
230
|
+
|
|
231
|
+
// Update pills
|
|
232
|
+
document.querySelectorAll('.switcher-pill').forEach(pill => {
|
|
233
|
+
pill.classList.remove('bg-indigo-600', 'text-white', 'shadow-sm');
|
|
234
|
+
pill.classList.add('text-slate-400');
|
|
235
|
+
});
|
|
236
|
+
const activePill = document.getElementById(`tab-btn-${normalized}`);
|
|
237
|
+
if (activePill) {
|
|
238
|
+
activePill.classList.add('bg-indigo-600', 'text-white', 'shadow-sm');
|
|
239
|
+
activePill.classList.remove('text-slate-400');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Sync URL search param cleanly without reloading
|
|
243
|
+
const url = new URL(window.location);
|
|
244
|
+
url.searchParams.set('variant', normalized);
|
|
245
|
+
window.history.replaceState({}, '', url);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function cycleVariant(delta) {
|
|
249
|
+
currentVariantIndex = (currentVariantIndex + delta + VARIANTS.length) % VARIANTS.length;
|
|
250
|
+
switchVariant(VARIANTS[currentVariantIndex]);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function adjustCounter(delta) {
|
|
254
|
+
counter += delta;
|
|
255
|
+
document.getElementById('counter-value').textContent = counter;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function simulateAction() {
|
|
259
|
+
const statusEl = document.getElementById('action-status');
|
|
260
|
+
statusEl.textContent = 'Processing in memory...';
|
|
261
|
+
statusEl.className = 'text-xs text-amber-400 animate-pulse';
|
|
262
|
+
setTimeout(() => {
|
|
263
|
+
statusEl.textContent = 'Action executed successfully!';
|
|
264
|
+
statusEl.className = 'text-xs text-emerald-400 font-medium';
|
|
265
|
+
}, 400);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function resetMockState() {
|
|
269
|
+
counter = 9;
|
|
270
|
+
document.getElementById('counter-value').textContent = counter;
|
|
271
|
+
const statusEl = document.getElementById('action-status');
|
|
272
|
+
statusEl.textContent = 'Ready';
|
|
273
|
+
statusEl.className = 'text-xs text-slate-400';
|
|
274
|
+
switchVariant('A');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Keyboard navigation (Arrow keys cycle variants when not typing in inputs)
|
|
278
|
+
window.addEventListener('keydown', (e) => {
|
|
279
|
+
const tag = document.activeElement ? document.activeElement.tagName.toLowerCase() : '';
|
|
280
|
+
if (tag === 'input' || tag === 'textarea' || tag === 'select') return;
|
|
281
|
+
if (e.key === 'ArrowRight') cycleVariant(1);
|
|
282
|
+
if (e.key === 'ArrowLeft') cycleVariant(-1);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Initialize variant on page load
|
|
286
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
287
|
+
switchVariant(getVariantFromUrl());
|
|
288
|
+
});
|
|
289
|
+
</script>
|
|
290
|
+
</body>
|
|
291
|
+
</html>
|
|
@@ -0,0 +1,178 @@
|
|
|
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>Live Preview Canvas</title>
|
|
7
|
+
<style>
|
|
8
|
+
/* Inject Prototype CSS Here */
|
|
9
|
+
body {
|
|
10
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 0;
|
|
13
|
+
background-color: #f9fafb;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.prototype-container {
|
|
17
|
+
/* Put your prototype content here */
|
|
18
|
+
min-height: 100vh;
|
|
19
|
+
padding: 2rem;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* --- Feedback Layer Styles --- */
|
|
23
|
+
#feedback-layer {
|
|
24
|
+
position: fixed;
|
|
25
|
+
top: 0;
|
|
26
|
+
left: 0;
|
|
27
|
+
width: 100vw;
|
|
28
|
+
height: 100vh;
|
|
29
|
+
pointer-events: none;
|
|
30
|
+
z-index: 9998;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.feedback-dot {
|
|
34
|
+
position: absolute;
|
|
35
|
+
width: 16px;
|
|
36
|
+
height: 16px;
|
|
37
|
+
background-color: #ef4444;
|
|
38
|
+
border: 2px solid white;
|
|
39
|
+
border-radius: 50%;
|
|
40
|
+
transform: translate(-50%, -50%);
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
pointer-events: auto;
|
|
43
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.feedback-dot:hover::after {
|
|
47
|
+
content: attr(data-comment);
|
|
48
|
+
position: absolute;
|
|
49
|
+
top: 100%;
|
|
50
|
+
left: 50%;
|
|
51
|
+
transform: translateX(-50%);
|
|
52
|
+
background: white;
|
|
53
|
+
padding: 8px;
|
|
54
|
+
border-radius: 4px;
|
|
55
|
+
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
56
|
+
white-space: nowrap;
|
|
57
|
+
color: black;
|
|
58
|
+
font-size: 12px;
|
|
59
|
+
z-index: 9999;
|
|
60
|
+
margin-top: 4px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
#feedback-controls {
|
|
64
|
+
position: fixed;
|
|
65
|
+
bottom: 20px;
|
|
66
|
+
right: 20px;
|
|
67
|
+
background: white;
|
|
68
|
+
padding: 16px;
|
|
69
|
+
border-radius: 8px;
|
|
70
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
71
|
+
z-index: 9999;
|
|
72
|
+
pointer-events: auto;
|
|
73
|
+
display: flex;
|
|
74
|
+
flex-direction: column;
|
|
75
|
+
gap: 8px;
|
|
76
|
+
border: 1px solid #e5e7eb;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
#feedback-controls button {
|
|
80
|
+
padding: 8px 16px;
|
|
81
|
+
background: #3b82f6;
|
|
82
|
+
color: white;
|
|
83
|
+
border: none;
|
|
84
|
+
border-radius: 4px;
|
|
85
|
+
cursor: pointer;
|
|
86
|
+
font-weight: 500;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
#feedback-controls button:hover {
|
|
90
|
+
background: #2563eb;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
#feedback-output {
|
|
94
|
+
display: none;
|
|
95
|
+
width: 300px;
|
|
96
|
+
height: 150px;
|
|
97
|
+
font-family: monospace;
|
|
98
|
+
font-size: 12px;
|
|
99
|
+
resize: none;
|
|
100
|
+
padding: 8px;
|
|
101
|
+
border: 1px solid #d1d5db;
|
|
102
|
+
border-radius: 4px;
|
|
103
|
+
}
|
|
104
|
+
</style>
|
|
105
|
+
</head>
|
|
106
|
+
<body>
|
|
107
|
+
|
|
108
|
+
<!-- Inject Prototype HTML Here -->
|
|
109
|
+
<div class="prototype-container">
|
|
110
|
+
<h1>Prototype Area</h1>
|
|
111
|
+
<p>Click anywhere to leave a comment.</p>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<!-- Feedback Layer -->
|
|
115
|
+
<div id="feedback-layer"></div>
|
|
116
|
+
<div id="feedback-controls">
|
|
117
|
+
<button id="export-btn">Export Feedback</button>
|
|
118
|
+
<textarea id="feedback-output" readonly placeholder="Exported JSON will appear here..."></textarea>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<script>
|
|
122
|
+
const comments = [];
|
|
123
|
+
const layer = document.getElementById('feedback-layer');
|
|
124
|
+
const exportBtn = document.getElementById('export-btn');
|
|
125
|
+
const output = document.getElementById('feedback-output');
|
|
126
|
+
const controls = document.getElementById('feedback-controls');
|
|
127
|
+
|
|
128
|
+
// Handle clicks on the document to add feedback
|
|
129
|
+
document.addEventListener('click', (e) => {
|
|
130
|
+
// Ignore clicks inside the feedback controls or on existing dots
|
|
131
|
+
if (controls.contains(e.target) || e.target.classList.contains('feedback-dot')) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const text = prompt('Enter your feedback for this element:');
|
|
136
|
+
if (!text) return;
|
|
137
|
+
|
|
138
|
+
const x = e.clientX;
|
|
139
|
+
const y = e.clientY;
|
|
140
|
+
|
|
141
|
+
// Save to state
|
|
142
|
+
const comment = { id: Date.now(), x, y, text };
|
|
143
|
+
comments.push(comment);
|
|
144
|
+
|
|
145
|
+
// Draw dot
|
|
146
|
+
const dot = document.createElement('div');
|
|
147
|
+
dot.className = 'feedback-dot';
|
|
148
|
+
dot.style.left = `${x}px`;
|
|
149
|
+
dot.style.top = `${y}px`;
|
|
150
|
+
dot.setAttribute('data-comment', text);
|
|
151
|
+
|
|
152
|
+
// Allow deleting comment on right click
|
|
153
|
+
dot.addEventListener('contextmenu', (evt) => {
|
|
154
|
+
evt.preventDefault();
|
|
155
|
+
if(confirm('Delete this comment?')) {
|
|
156
|
+
const idx = comments.findIndex(c => c.id === comment.id);
|
|
157
|
+
if(idx > -1) comments.splice(idx, 1);
|
|
158
|
+
dot.remove();
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
layer.appendChild(dot);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Export functionality
|
|
166
|
+
exportBtn.addEventListener('click', () => {
|
|
167
|
+
output.style.display = 'block';
|
|
168
|
+
output.value = JSON.stringify(comments, null, 2);
|
|
169
|
+
output.select();
|
|
170
|
+
document.execCommand('copy');
|
|
171
|
+
exportBtn.textContent = 'Copied to Clipboard!';
|
|
172
|
+
setTimeout(() => {
|
|
173
|
+
exportBtn.textContent = 'Export Feedback';
|
|
174
|
+
}, 2000);
|
|
175
|
+
});
|
|
176
|
+
</script>
|
|
177
|
+
</body>
|
|
178
|
+
</html>
|
|
@@ -26,35 +26,84 @@ try {
|
|
|
26
26
|
Write-Host "Warning: pip install failed. Install google-genai manually." -ForegroundColor Yellow
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
# 3. Resolve API key
|
|
30
|
-
$
|
|
31
|
-
if ([string]::IsNullOrWhiteSpace($
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
$
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
29
|
+
# 3. Resolve provider and API key
|
|
30
|
+
$OpenwikiProvider = $env:OPENWIKI_PROVIDER
|
|
31
|
+
if ([string]::IsNullOrWhiteSpace($OpenwikiProvider)) {
|
|
32
|
+
$OpenwikiProvider = "google"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Map provider to its API key environment variable name
|
|
36
|
+
$ApiKeyVar = switch ($OpenwikiProvider) {
|
|
37
|
+
"google" { "GEMINI_API_KEY" }
|
|
38
|
+
"groq" { "GROQ_API_KEY" }
|
|
39
|
+
"grok" { "XAI_API_KEY" }
|
|
40
|
+
"xai" { "XAI_API_KEY" }
|
|
41
|
+
"nvidia" { "NVIDIA_API_KEY" }
|
|
42
|
+
"openrouter" { "OPENROUTER_API_KEY" }
|
|
43
|
+
"openai" { "OPENAI_API_KEY" }
|
|
44
|
+
"custom" { "OPENWIKI_API_KEY" }
|
|
45
|
+
"ollama" { $null }
|
|
46
|
+
default { "GEMINI_API_KEY"; $OpenwikiProvider = "google" }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# For Google provider, use the existing interactive flow with verification
|
|
50
|
+
if ($OpenwikiProvider -eq "google") {
|
|
51
|
+
$ApiKey = $env:GEMINI_API_KEY
|
|
52
|
+
if ([string]::IsNullOrWhiteSpace($ApiKey)) {
|
|
53
|
+
Write-Host ""
|
|
54
|
+
$ApiKey = Read-Host "Enter your Gemini API key (or press Enter to skip)"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (-not [string]::IsNullOrWhiteSpace($ApiKey)) {
|
|
58
|
+
Write-Host "Verifying API key..." -ForegroundColor Yellow
|
|
59
|
+
# verify_api_key.py reads GEMINI_API_KEY from its own process environment.
|
|
60
|
+
# A key typed at the Read-Host prompt above exists only in $ApiKey, so it
|
|
61
|
+
# has to be published to this process before python is started - otherwise
|
|
62
|
+
# the child sees nothing and every interactively entered key fails the check
|
|
63
|
+
# and is discarded below.
|
|
64
|
+
$env:GEMINI_API_KEY = $ApiKey
|
|
65
|
+
$VerifyScript = Join-Path $PSScriptRoot "verify_api_key.py"
|
|
66
|
+
$VerifyOutput = & python $VerifyScript 2>&1
|
|
67
|
+
$VerifyCode = $LASTEXITCODE
|
|
68
|
+
if ($VerifyCode -eq 0 -and ($VerifyOutput -match "VERIFIED_OK")) {
|
|
69
|
+
Write-Host " -> API key verified." -ForegroundColor Green
|
|
70
|
+
[System.Environment]::SetEnvironmentVariable("GEMINI_API_KEY", $ApiKey, "User")
|
|
71
|
+
} else {
|
|
72
|
+
Write-Host " -> WARNING: API key verification failed (with retry + TLS fallback)." -ForegroundColor Yellow
|
|
73
|
+
$VerifyOutput | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
|
|
74
|
+
Write-Host " The daemon will run in collect-only mode until a valid key is set." -ForegroundColor Yellow
|
|
75
|
+
$ApiKey = ""
|
|
76
|
+
}
|
|
50
77
|
} else {
|
|
51
|
-
Write-Host "
|
|
52
|
-
$VerifyOutput | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray }
|
|
53
|
-
Write-Host " The daemon will run in collect-only mode until a valid key is set." -ForegroundColor Yellow
|
|
54
|
-
$GeminiKey = ""
|
|
78
|
+
Write-Host "No API key provided. Daemon will run in collect-only mode." -ForegroundColor Yellow
|
|
55
79
|
}
|
|
56
80
|
} else {
|
|
57
|
-
|
|
81
|
+
# For non-Google providers, skip verification and use the configured key
|
|
82
|
+
if ([string]::IsNullOrWhiteSpace($ApiKeyVar)) {
|
|
83
|
+
$ApiKey = ""
|
|
84
|
+
} else {
|
|
85
|
+
$ApiKey = (Get-Item -Path "env:$ApiKeyVar" -ErrorAction SilentlyContinue).Value
|
|
86
|
+
if ([string]::IsNullOrWhiteSpace($ApiKey)) {
|
|
87
|
+
$ApiKey = ""
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if ([string]::IsNullOrWhiteSpace($ApiKey)) {
|
|
91
|
+
Write-Host "No API key provided for provider '$OpenwikiProvider'. Daemon will run in collect-only mode." -ForegroundColor Yellow
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# Persist provider and model configuration
|
|
96
|
+
[System.Environment]::SetEnvironmentVariable("OPENWIKI_PROVIDER", $OpenwikiProvider, "User")
|
|
97
|
+
if (-not [string]::IsNullOrWhiteSpace($env:OPENWIKI_MODEL)) {
|
|
98
|
+
[System.Environment]::SetEnvironmentVariable("OPENWIKI_MODEL", $env:OPENWIKI_MODEL, "User")
|
|
99
|
+
}
|
|
100
|
+
if (-not [string]::IsNullOrWhiteSpace($env:OPENWIKI_BASE_URL)) {
|
|
101
|
+
[System.Environment]::SetEnvironmentVariable("OPENWIKI_BASE_URL", $env:OPENWIKI_BASE_URL, "User")
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Persist the appropriate API key
|
|
105
|
+
if (-not [string]::IsNullOrWhiteSpace($ApiKey) -and -not [string]::IsNullOrWhiteSpace($ApiKeyVar)) {
|
|
106
|
+
[System.Environment]::SetEnvironmentVariable($ApiKeyVar, $ApiKey, "User")
|
|
58
107
|
}
|
|
59
108
|
|
|
60
109
|
# 4. Ensure log directory
|
|
@@ -171,8 +220,12 @@ if ($Registered) {
|
|
|
171
220
|
Write-Host " -> Projects config: $DaemonLogDir\projects.json" -ForegroundColor Yellow
|
|
172
221
|
}
|
|
173
222
|
|
|
174
|
-
if (-not [string]::IsNullOrWhiteSpace($
|
|
175
|
-
|
|
223
|
+
if (-not [string]::IsNullOrWhiteSpace($ApiKey)) {
|
|
224
|
+
if (-not [string]::IsNullOrWhiteSpace($ApiKeyVar)) {
|
|
225
|
+
Write-Host " -> API key stored as the $ApiKeyVar user environment variable" -ForegroundColor Green
|
|
226
|
+
} else {
|
|
227
|
+
Write-Host " -> Provider configured: $OpenwikiProvider" -ForegroundColor Green
|
|
228
|
+
}
|
|
176
229
|
Write-Host " (not embedded in the task or the startup file)." -ForegroundColor Green
|
|
177
230
|
}
|
|
178
231
|
|