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 CHANGED
@@ -1 +1 @@
1
- 3.70.4
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
- const { cols, rows } = t
130
- session.sendResize(cols, rows)
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 p-2" />
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: 4px;
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="divide-y divide-default">
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-3 py-1.5 hover:bg-elevated/40 cursor-pointer flex items-center gap-2"
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
- <UCard :ui="{ body: 'p-0', header: 'px-4 py-3', footer: 'px-4 py-2.5' }">
437
- <template #header>
438
- <div class="flex items-center gap-3">
439
- <UIcon name="i-lucide-history" class="size-5 text-muted shrink-0" />
440
- <UInput
441
- v-model="searchQuery"
442
- placeholder="Filter command history…"
443
- size="lg"
444
- autofocus
445
- :ui="{ root: 'flex-1', base: 'border-0 shadow-none ring-0 focus:ring-0 px-0' }"
446
- @keydown="searchKeydown"
447
- />
448
- <span class="text-xs text-muted shrink-0 tabular-nums">
449
- {{ searchResults.length }} / {{ history.length }}
450
- </span>
451
- <kbd class="px-1.5 py-0.5 rounded bg-elevated/50 text-xs font-mono text-muted shrink-0">
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="p-10 text-center text-sm text-muted"
460
+ class="px-6 py-12 text-center text-sm text-muted"
461
461
  >
462
- <UIcon name="i-lucide-terminal" class="size-8 mx-auto mb-3 opacity-50" />
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="p-10 text-center text-sm text-muted"
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="divide-y divide-default">
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-4 py-2 cursor-pointer transition-colors flex items-center gap-3"
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-primary/10 border-l-2 border-primary pl-[14px]'
482
- : 'hover:bg-elevated/40 border-l-2 border-transparent'"
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-xs text-muted shrink-0 tabular-nums">
489
+ <span class="text-[11px] text-muted/70 shrink-0 tabular-nums">
495
490
  {{ relativeTime(entry.ts) }}
496
491
  </span>
497
- <kbd
498
- v-if="i === searchSelectedIdx"
499
- class="px-1.5 py-0.5 rounded bg-primary/20 text-[10px] font-mono text-primary shrink-0"
500
- >
501
- ↵ send
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
- <template #footer>
508
- <div class="text-xs text-muted flex items-center gap-4">
509
- <span class="flex items-center gap-1">
510
- <kbd class="px-1.5 py-0.5 rounded bg-elevated/50 font-mono">↑</kbd>
511
- <kbd class="px-1.5 py-0.5 rounded bg-elevated/50 font-mono">↓</kbd>
512
- navigate
513
- </span>
514
- <span class="flex items-center gap-1">
515
- <kbd class="px-1.5 py-0.5 rounded bg-elevated/50 font-mono">↵</kbd>
516
- send to active session
517
- </span>
518
- <span class="flex items-center gap-1">
519
- <kbd class="px-1.5 py-0.5 rounded bg-elevated/50 font-mono">esc</kbd>
520
- close
521
- </span>
522
- <UButton
523
- v-if="history.length > 0"
524
- size="xs"
525
- variant="ghost"
526
- color="error"
527
- icon="i-lucide-trash-2"
528
- class="ml-auto"
529
- @click="clearHistory"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkaos",
3
- "version": "3.70.4",
3
+ "version": "3.70.6",
4
4
  "description": "The Operating System for AI Agent Teams",
5
5
  "type": "module",
6
6
  "bin": {
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "arkaos-core"
3
- version = "3.70.4"
3
+ version = "3.70.6"
4
4
  description = "Core engine for ArkaOS — The Operating System for AI Agent Teams"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}