arkaos 3.70.4 → 3.70.6
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 +94 -77
- package/package.json +1 -1
- package/pyproject.toml +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.70.
|
|
1
|
+
3.70.6
|
|
@@ -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;
|
|
@@ -101,10 +101,17 @@ watch(searchResults, () => {
|
|
|
101
101
|
searchSelectedIdx.value = 0
|
|
102
102
|
})
|
|
103
103
|
|
|
104
|
+
const searchInputEl = ref<HTMLInputElement | null>(null)
|
|
105
|
+
|
|
104
106
|
function openSearch() {
|
|
105
107
|
searchOpen.value = true
|
|
106
108
|
searchQuery.value = ''
|
|
107
109
|
searchSelectedIdx.value = 0
|
|
110
|
+
// autofocus on the bare input only fires on initial mount; the modal
|
|
111
|
+
// is mounted persistently, so we focus explicitly each time it opens.
|
|
112
|
+
nextTick(() => {
|
|
113
|
+
requestAnimationFrame(() => searchInputEl.value?.focus())
|
|
114
|
+
})
|
|
108
115
|
}
|
|
109
116
|
|
|
110
117
|
function pickFromSearch(cmd: string) {
|
|
@@ -321,6 +328,7 @@ const showHistory = ref(false)
|
|
|
321
328
|
v-show="activeId === tab.id"
|
|
322
329
|
:session="tab.session"
|
|
323
330
|
:on-input-line="recordCommand"
|
|
331
|
+
:active="activeId === tab.id"
|
|
324
332
|
class="absolute inset-0"
|
|
325
333
|
/>
|
|
326
334
|
</template>
|
|
@@ -391,22 +399,18 @@ const showHistory = ref(false)
|
|
|
391
399
|
No matches for
|
|
392
400
|
<span class="font-mono text-default">{{ sidebarFilter }}</span>.
|
|
393
401
|
</div>
|
|
394
|
-
<ul v-else class="
|
|
402
|
+
<ul v-else class="py-1">
|
|
395
403
|
<li
|
|
396
404
|
v-for="entry in visibleHistory"
|
|
397
405
|
:key="entry.ts"
|
|
398
|
-
class="group px-
|
|
406
|
+
class="group mx-1 px-2.5 py-1 rounded-md cursor-pointer flex items-center gap-2 hover:bg-elevated/40 transition-colors"
|
|
399
407
|
:title="`${entry.cmd} — ${relativeTime(entry.ts)}`"
|
|
400
408
|
@click="sendToActive(entry.cmd)"
|
|
401
409
|
>
|
|
402
|
-
<UIcon
|
|
403
|
-
name="i-lucide-chevron-right"
|
|
404
|
-
class="size-3 shrink-0 text-muted group-hover:text-primary"
|
|
405
|
-
/>
|
|
406
410
|
<span class="flex-1 min-w-0 font-mono text-xs truncate">
|
|
407
411
|
{{ entry.cmd }}
|
|
408
412
|
</span>
|
|
409
|
-
<span class="text-[10px] text-muted shrink-0 tabular-nums opacity-0 group-hover:opacity-100 transition-opacity">
|
|
413
|
+
<span class="text-[10px] text-muted/70 shrink-0 tabular-nums opacity-0 group-hover:opacity-100 transition-opacity">
|
|
410
414
|
{{ relativeTime(entry.ts) }}
|
|
411
415
|
</span>
|
|
412
416
|
<UIcon
|
|
@@ -430,112 +434,125 @@ const showHistory = ref(false)
|
|
|
430
434
|
|
|
431
435
|
<UModal
|
|
432
436
|
v-model:open="searchOpen"
|
|
433
|
-
:ui="{ content: 'max-w-2xl' }"
|
|
437
|
+
:ui="{ content: 'max-w-2xl ring-0 shadow-2xl' }"
|
|
434
438
|
>
|
|
435
439
|
<template #content>
|
|
436
|
-
<
|
|
437
|
-
<
|
|
438
|
-
<
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
esc
|
|
453
|
-
</kbd>
|
|
454
|
-
</div>
|
|
455
|
-
</template>
|
|
440
|
+
<div class="rounded-xl bg-default overflow-hidden">
|
|
441
|
+
<div class="flex items-center gap-3 px-4 py-3 border-b border-default/60">
|
|
442
|
+
<UIcon name="i-lucide-history" class="size-4 text-muted shrink-0" />
|
|
443
|
+
<input
|
|
444
|
+
ref="searchInputEl"
|
|
445
|
+
v-model="searchQuery"
|
|
446
|
+
type="text"
|
|
447
|
+
autofocus
|
|
448
|
+
placeholder="Filter command history…"
|
|
449
|
+
class="palette-input flex-1 bg-transparent text-default placeholder:text-muted/70 focus:outline-none border-0 ring-0 text-sm"
|
|
450
|
+
@keydown="searchKeydown"
|
|
451
|
+
>
|
|
452
|
+
<span class="text-[11px] text-muted/70 shrink-0 tabular-nums">
|
|
453
|
+
{{ searchResults.length }} of {{ history.length }}
|
|
454
|
+
</span>
|
|
455
|
+
</div>
|
|
456
456
|
|
|
457
457
|
<div class="max-h-[60vh] overflow-y-auto">
|
|
458
458
|
<div
|
|
459
459
|
v-if="history.length === 0"
|
|
460
|
-
class="
|
|
460
|
+
class="px-6 py-12 text-center text-sm text-muted"
|
|
461
461
|
>
|
|
462
|
-
<UIcon name="i-lucide-terminal" class="size-
|
|
462
|
+
<UIcon name="i-lucide-terminal" class="size-7 mx-auto mb-3 opacity-30" />
|
|
463
463
|
<p>No commands yet.</p>
|
|
464
|
-
<p class="text-xs mt-1">
|
|
464
|
+
<p class="text-xs mt-1 opacity-70">
|
|
465
465
|
Run something in the terminal — it'll show up here.
|
|
466
466
|
</p>
|
|
467
467
|
</div>
|
|
468
468
|
<div
|
|
469
469
|
v-else-if="searchResults.length === 0"
|
|
470
|
-
class="
|
|
470
|
+
class="px-6 py-12 text-center text-sm text-muted"
|
|
471
471
|
>
|
|
472
472
|
No match for
|
|
473
473
|
<span class="font-mono text-default">{{ searchQuery }}</span>.
|
|
474
474
|
</div>
|
|
475
|
-
<ul v-else class="
|
|
475
|
+
<ul v-else class="py-1">
|
|
476
476
|
<li
|
|
477
477
|
v-for="(entry, i) in searchResults"
|
|
478
478
|
:key="entry.ts"
|
|
479
|
-
class="px-
|
|
479
|
+
class="mx-1 px-3 py-1.5 rounded-md cursor-pointer flex items-center gap-3 transition-colors"
|
|
480
480
|
:class="i === searchSelectedIdx
|
|
481
|
-
? 'bg-
|
|
482
|
-
: 'hover:bg-elevated/
|
|
481
|
+
? 'bg-elevated/70'
|
|
482
|
+
: 'hover:bg-elevated/30'"
|
|
483
483
|
@click="pickFromSearch(entry.cmd)"
|
|
484
484
|
@mouseenter="searchSelectedIdx = i"
|
|
485
485
|
>
|
|
486
|
-
<UIcon
|
|
487
|
-
name="i-lucide-chevron-right"
|
|
488
|
-
class="size-3.5 shrink-0"
|
|
489
|
-
:class="i === searchSelectedIdx ? 'text-primary' : 'text-muted'"
|
|
490
|
-
/>
|
|
491
486
|
<span class="flex-1 min-w-0 font-mono text-sm truncate">
|
|
492
487
|
{{ entry.cmd }}
|
|
493
488
|
</span>
|
|
494
|
-
<span class="text-
|
|
489
|
+
<span class="text-[11px] text-muted/70 shrink-0 tabular-nums">
|
|
495
490
|
{{ relativeTime(entry.ts) }}
|
|
496
491
|
</span>
|
|
497
|
-
<
|
|
498
|
-
|
|
499
|
-
class="
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
</kbd>
|
|
492
|
+
<UIcon
|
|
493
|
+
name="i-lucide-corner-down-left"
|
|
494
|
+
class="size-3.5 shrink-0 transition-opacity"
|
|
495
|
+
:class="i === searchSelectedIdx ? 'text-default opacity-100' : 'text-muted opacity-0'"
|
|
496
|
+
/>
|
|
503
497
|
</li>
|
|
504
498
|
</ul>
|
|
505
499
|
</div>
|
|
506
500
|
|
|
507
|
-
<
|
|
508
|
-
<
|
|
509
|
-
<
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
</
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
>
|
|
531
|
-
Clear all
|
|
532
|
-
</UButton>
|
|
533
|
-
</div>
|
|
534
|
-
</template>
|
|
535
|
-
</UCard>
|
|
501
|
+
<div class="px-4 py-2.5 border-t border-default/60 flex items-center gap-4 text-[11px] text-muted/80">
|
|
502
|
+
<span class="flex items-center gap-1.5">
|
|
503
|
+
<kbd class="palette-kbd">↑</kbd><kbd class="palette-kbd">↓</kbd>
|
|
504
|
+
navigate
|
|
505
|
+
</span>
|
|
506
|
+
<span class="flex items-center gap-1.5">
|
|
507
|
+
<kbd class="palette-kbd">↵</kbd>
|
|
508
|
+
send
|
|
509
|
+
</span>
|
|
510
|
+
<span class="flex items-center gap-1.5">
|
|
511
|
+
<kbd class="palette-kbd">esc</kbd>
|
|
512
|
+
close
|
|
513
|
+
</span>
|
|
514
|
+
<button
|
|
515
|
+
v-if="history.length > 0"
|
|
516
|
+
class="ml-auto text-muted/80 hover:text-red-400 transition-colors flex items-center gap-1"
|
|
517
|
+
@click="clearHistory"
|
|
518
|
+
>
|
|
519
|
+
<UIcon name="i-lucide-trash-2" class="size-3" />
|
|
520
|
+
Clear
|
|
521
|
+
</button>
|
|
522
|
+
</div>
|
|
523
|
+
</div>
|
|
536
524
|
</template>
|
|
537
525
|
</UModal>
|
|
538
526
|
</div>
|
|
539
527
|
</template>
|
|
540
528
|
</UDashboardPanel>
|
|
541
529
|
</template>
|
|
530
|
+
|
|
531
|
+
<style scoped>
|
|
532
|
+
.palette-input {
|
|
533
|
+
/* Defensive: kill any inherited ring/border from Tailwind base */
|
|
534
|
+
box-shadow: none !important;
|
|
535
|
+
outline: none !important;
|
|
536
|
+
}
|
|
537
|
+
.palette-input:focus,
|
|
538
|
+
.palette-input:focus-visible {
|
|
539
|
+
outline: none !important;
|
|
540
|
+
box-shadow: none !important;
|
|
541
|
+
border-color: transparent !important;
|
|
542
|
+
}
|
|
543
|
+
.palette-kbd {
|
|
544
|
+
display: inline-flex;
|
|
545
|
+
align-items: center;
|
|
546
|
+
justify-content: center;
|
|
547
|
+
min-width: 1.1rem;
|
|
548
|
+
padding: 0 0.3rem;
|
|
549
|
+
height: 1.1rem;
|
|
550
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
551
|
+
font-size: 10px;
|
|
552
|
+
line-height: 1;
|
|
553
|
+
border-radius: 4px;
|
|
554
|
+
background-color: rgb(var(--ui-bg-elevated) / 0.5);
|
|
555
|
+
color: rgb(var(--ui-text-muted));
|
|
556
|
+
border: 1px solid rgb(var(--ui-border) / 0.4);
|
|
557
|
+
}
|
|
558
|
+
</style>
|
package/package.json
CHANGED