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 CHANGED
@@ -1 +1 @@
1
- 3.70.3
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
- 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;
@@ -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-72 shrink-0 rounded-lg border border-default bg-elevated/10 overflow-hidden flex flex-col"
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 text-xs uppercase tracking-wide text-muted">
325
- Command history
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
- <div class="flex-1 overflow-auto text-xs font-mono">
328
- <button
329
- v-for="(entry, i) in history"
330
- :key="i"
331
- class="w-full text-left px-3 py-1.5 hover:bg-default/40 truncate"
332
- :title="entry.cmd"
333
- @click="activeTab?.session.sendInput(entry.cmd)"
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
- {{ entry.cmd }}
336
- </button>
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkaos",
3
- "version": "3.70.3",
3
+ "version": "3.70.5",
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.3"
3
+ version = "3.70.5"
4
4
  description = "Core engine for ArkaOS — The Operating System for AI Agent Teams"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}