arkaos 3.70.3 → 3.70.5
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/VERSION +1 -1
- package/dashboard/app/components/Terminal.vue +50 -6
- package/dashboard/app/pages/terminal.vue +97 -14
- package/package.json +1 -1
- package/pyproject.toml +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.70.
|
|
1
|
+
3.70.5
|
|
@@ -16,6 +16,7 @@ interface Props {
|
|
|
16
16
|
session?: ReturnType<typeof useTerminalSession>
|
|
17
17
|
onInputLine?: (line: string) => void
|
|
18
18
|
theme?: XtermTheme
|
|
19
|
+
active?: boolean
|
|
19
20
|
}
|
|
20
21
|
const props = defineProps<Props>()
|
|
21
22
|
|
|
@@ -58,12 +59,23 @@ onMounted(async () => {
|
|
|
58
59
|
t.loadAddon(new WebLinksAddon())
|
|
59
60
|
t.loadAddon(searchAddon)
|
|
60
61
|
t.open(container.value)
|
|
61
|
-
fitAddon.fit()
|
|
62
62
|
|
|
63
63
|
term.value = t
|
|
64
64
|
fit.value = fitAddon
|
|
65
65
|
search.value = searchAddon
|
|
66
66
|
|
|
67
|
+
// v3.70.5 — wait for paint so the container has its final width
|
|
68
|
+
// before fit() reads it. Without this, fit() ran with a 0x0 box on
|
|
69
|
+
// first mount and shells started at the default 80 cols, leaving
|
|
70
|
+
// empty space on the right of the canvas.
|
|
71
|
+
await nextTick()
|
|
72
|
+
await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()))
|
|
73
|
+
try {
|
|
74
|
+
fitAddon.fit()
|
|
75
|
+
} catch (_e) {
|
|
76
|
+
// dom layout not ready — ResizeObserver below will recover
|
|
77
|
+
}
|
|
78
|
+
|
|
67
79
|
await session.open()
|
|
68
80
|
|
|
69
81
|
unsubscribeOutput = session.onOutput((chunk) => {
|
|
@@ -123,11 +135,17 @@ onMounted(async () => {
|
|
|
123
135
|
session.sendInput(data)
|
|
124
136
|
})
|
|
125
137
|
|
|
126
|
-
// Initial size sync once the WS is open.
|
|
138
|
+
// Initial size sync once the WS is open. v3.70.5 — fit *again* on
|
|
139
|
+
// open because the layout might have changed during the WS round-
|
|
140
|
+
// trip (sidebar collapse, tab switch, etc.).
|
|
127
141
|
watch(session.status, (s) => {
|
|
128
142
|
if (s === 'open') {
|
|
129
|
-
|
|
130
|
-
|
|
143
|
+
requestAnimationFrame(() => {
|
|
144
|
+
try {
|
|
145
|
+
fitAddon.fit()
|
|
146
|
+
} catch (_e) { /* layout not ready */ }
|
|
147
|
+
session.sendResize(t.cols, t.rows)
|
|
148
|
+
})
|
|
131
149
|
}
|
|
132
150
|
}, { immediate: true })
|
|
133
151
|
|
|
@@ -140,8 +158,33 @@ onMounted(async () => {
|
|
|
140
158
|
}
|
|
141
159
|
})
|
|
142
160
|
resizeObserver.observe(container.value)
|
|
161
|
+
|
|
162
|
+
// v3.70.5 — refit when this tab becomes the active one. v-show keeps
|
|
163
|
+
// inactive tabs mounted with display:none, where ResizeObserver
|
|
164
|
+
// doesn't fire. Switching back requires an explicit refit.
|
|
165
|
+
watch(() => props.active, (isActive) => {
|
|
166
|
+
if (!isActive || !term.value || !fit.value) return
|
|
167
|
+
nextTick(() => {
|
|
168
|
+
requestAnimationFrame(() => {
|
|
169
|
+
try {
|
|
170
|
+
fit.value?.fit()
|
|
171
|
+
session.sendResize(term.value!.cols, term.value!.rows)
|
|
172
|
+
} catch (_e) { /* layout not ready */ }
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
})
|
|
143
176
|
})
|
|
144
177
|
|
|
178
|
+
function refit() {
|
|
179
|
+
if (!term.value || !fit.value) return
|
|
180
|
+
try {
|
|
181
|
+
fit.value.fit()
|
|
182
|
+
session.sendResize(term.value.cols, term.value.rows)
|
|
183
|
+
} catch (_e) {
|
|
184
|
+
// layout not ready
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
145
188
|
onBeforeUnmount(async () => {
|
|
146
189
|
unsubscribeOutput?.()
|
|
147
190
|
resizeObserver?.disconnect()
|
|
@@ -157,6 +200,7 @@ defineExpose({
|
|
|
157
200
|
status: session.status,
|
|
158
201
|
error: session.error,
|
|
159
202
|
meta: session.meta,
|
|
203
|
+
refit,
|
|
160
204
|
})
|
|
161
205
|
</script>
|
|
162
206
|
|
|
@@ -180,7 +224,7 @@ defineExpose({
|
|
|
180
224
|
</span>
|
|
181
225
|
<span v-else class="text-muted">closed</span>
|
|
182
226
|
</div>
|
|
183
|
-
<div ref="container" class="absolute inset-0
|
|
227
|
+
<div ref="container" class="absolute inset-0" />
|
|
184
228
|
</div>
|
|
185
229
|
</template>
|
|
186
230
|
|
|
@@ -188,7 +232,7 @@ defineExpose({
|
|
|
188
232
|
:deep(.xterm) {
|
|
189
233
|
height: 100%;
|
|
190
234
|
width: 100%;
|
|
191
|
-
padding:
|
|
235
|
+
padding: 8px 12px;
|
|
192
236
|
}
|
|
193
237
|
:deep(.xterm-viewport) {
|
|
194
238
|
background-color: transparent !important;
|
|
@@ -112,6 +112,20 @@ function pickFromSearch(cmd: string) {
|
|
|
112
112
|
searchOpen.value = false
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
// v3.70.4 — inline filter for the side panel.
|
|
116
|
+
const sidebarFilter = ref('')
|
|
117
|
+
|
|
118
|
+
const visibleHistory = computed(() => {
|
|
119
|
+
const q = sidebarFilter.value.trim().toLowerCase()
|
|
120
|
+
const filtered = history.value.filter((e) => isPlausibleCommand(e.cmd))
|
|
121
|
+
if (!q) return filtered
|
|
122
|
+
return filtered.filter((e) => e.cmd.toLowerCase().includes(q))
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
function sendToActive(cmd: string) {
|
|
126
|
+
activeTab.value?.session.sendInput(cmd)
|
|
127
|
+
}
|
|
128
|
+
|
|
115
129
|
function searchKeydown(e: KeyboardEvent) {
|
|
116
130
|
const total = searchResults.value.length
|
|
117
131
|
if (total === 0) return
|
|
@@ -307,6 +321,7 @@ const showHistory = ref(false)
|
|
|
307
321
|
v-show="activeId === tab.id"
|
|
308
322
|
:session="tab.session"
|
|
309
323
|
:on-input-line="recordCommand"
|
|
324
|
+
:active="activeId === tab.id"
|
|
310
325
|
class="absolute inset-0"
|
|
311
326
|
/>
|
|
312
327
|
</template>
|
|
@@ -319,24 +334,92 @@ const showHistory = ref(false)
|
|
|
319
334
|
</div>
|
|
320
335
|
<aside
|
|
321
336
|
v-if="showHistory"
|
|
322
|
-
class="w-
|
|
337
|
+
class="w-80 shrink-0 rounded-lg border border-default bg-elevated/10 overflow-hidden flex flex-col"
|
|
323
338
|
>
|
|
324
|
-
<div class="px-3 py-2 border-b border-default
|
|
325
|
-
|
|
339
|
+
<div class="px-3 py-2.5 border-b border-default flex items-center gap-2">
|
|
340
|
+
<UIcon name="i-lucide-history" class="size-4 text-muted shrink-0" />
|
|
341
|
+
<span class="text-sm font-semibold">History</span>
|
|
342
|
+
<UBadge :label="String(visibleHistory.length)" size="xs" variant="subtle" />
|
|
343
|
+
<div class="ml-auto flex items-center gap-1">
|
|
344
|
+
<UButton
|
|
345
|
+
size="xs"
|
|
346
|
+
variant="ghost"
|
|
347
|
+
icon="i-lucide-search"
|
|
348
|
+
title="Open full search (⌃R)"
|
|
349
|
+
@click="openSearch"
|
|
350
|
+
/>
|
|
351
|
+
<UButton
|
|
352
|
+
v-if="history.length > 0"
|
|
353
|
+
size="xs"
|
|
354
|
+
variant="ghost"
|
|
355
|
+
color="error"
|
|
356
|
+
icon="i-lucide-trash-2"
|
|
357
|
+
title="Clear all"
|
|
358
|
+
@click="clearHistory"
|
|
359
|
+
/>
|
|
360
|
+
<UButton
|
|
361
|
+
size="xs"
|
|
362
|
+
variant="ghost"
|
|
363
|
+
icon="i-lucide-x"
|
|
364
|
+
title="Close panel"
|
|
365
|
+
@click="showHistory = false"
|
|
366
|
+
/>
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
|
|
370
|
+
<div class="px-3 py-2 border-b border-default">
|
|
371
|
+
<UInput
|
|
372
|
+
v-model="sidebarFilter"
|
|
373
|
+
size="xs"
|
|
374
|
+
placeholder="Filter…"
|
|
375
|
+
icon="i-lucide-search"
|
|
376
|
+
class="w-full"
|
|
377
|
+
/>
|
|
326
378
|
</div>
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
class="
|
|
332
|
-
|
|
333
|
-
|
|
379
|
+
|
|
380
|
+
<div class="flex-1 overflow-y-auto">
|
|
381
|
+
<div
|
|
382
|
+
v-if="history.length === 0"
|
|
383
|
+
class="p-6 text-center text-xs text-muted"
|
|
384
|
+
>
|
|
385
|
+
<UIcon name="i-lucide-terminal" class="size-6 mx-auto mb-2 opacity-50" />
|
|
386
|
+
<p>No commands yet.</p>
|
|
387
|
+
</div>
|
|
388
|
+
<div
|
|
389
|
+
v-else-if="visibleHistory.length === 0"
|
|
390
|
+
class="p-6 text-center text-xs text-muted"
|
|
334
391
|
>
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
<div v-if="history.length === 0" class="px-3 py-4 text-muted text-center">
|
|
338
|
-
No commands yet
|
|
392
|
+
No matches for
|
|
393
|
+
<span class="font-mono text-default">{{ sidebarFilter }}</span>.
|
|
339
394
|
</div>
|
|
395
|
+
<ul v-else class="divide-y divide-default">
|
|
396
|
+
<li
|
|
397
|
+
v-for="entry in visibleHistory"
|
|
398
|
+
:key="entry.ts"
|
|
399
|
+
class="group px-3 py-1.5 hover:bg-elevated/40 cursor-pointer flex items-center gap-2"
|
|
400
|
+
:title="`${entry.cmd} — ${relativeTime(entry.ts)}`"
|
|
401
|
+
@click="sendToActive(entry.cmd)"
|
|
402
|
+
>
|
|
403
|
+
<UIcon
|
|
404
|
+
name="i-lucide-chevron-right"
|
|
405
|
+
class="size-3 shrink-0 text-muted group-hover:text-primary"
|
|
406
|
+
/>
|
|
407
|
+
<span class="flex-1 min-w-0 font-mono text-xs truncate">
|
|
408
|
+
{{ entry.cmd }}
|
|
409
|
+
</span>
|
|
410
|
+
<span class="text-[10px] text-muted shrink-0 tabular-nums opacity-0 group-hover:opacity-100 transition-opacity">
|
|
411
|
+
{{ relativeTime(entry.ts) }}
|
|
412
|
+
</span>
|
|
413
|
+
<UIcon
|
|
414
|
+
name="i-lucide-corner-down-left"
|
|
415
|
+
class="size-3 shrink-0 text-muted opacity-0 group-hover:opacity-100 transition-opacity"
|
|
416
|
+
/>
|
|
417
|
+
</li>
|
|
418
|
+
</ul>
|
|
419
|
+
</div>
|
|
420
|
+
|
|
421
|
+
<div class="px-3 py-2 border-t border-default text-[10px] text-muted">
|
|
422
|
+
Click a command to send it to the active session.
|
|
340
423
|
</div>
|
|
341
424
|
</aside>
|
|
342
425
|
</div>
|
package/package.json
CHANGED