xterm-input-panel 1.2.1 → 1.2.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # xterm-input-panel
2
2
 
3
+ ## 1.2.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 6dcad78: Upgrade the Vite toolchain to Vite 8 and align the related React, Storybook, and Vitest integrations used by local build and browser-test workflows.
8
+
9
+ ## 1.2.2
10
+
11
+ ### Patch Changes
12
+
13
+ - 143b916: Add hosted app distribution support across the CLI, server, and web runtime.
14
+ - add `openspecui --app` with configurable hosted app base URLs and local hosted-app dev mode
15
+ - expose hosted session/bootstrap helpers so versioned frontend entries can reconnect to the correct backend
16
+ - include hosted-app settings and faster dashboard overview loading for the web UI
17
+ - scope xterm input-panel persisted state by hosted session to avoid cross-tab leakage
18
+
3
19
  ## 1.2.1
4
20
 
5
21
  ### Patch Changes
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "xterm-input-panel",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
+ "scripts": {
7
+ "dev": "storybook dev -p 6007",
8
+ "test:browser": "vitest run --retry 2 --config vitest.storybook.config.ts",
9
+ "typecheck": "tsc --noEmit"
10
+ },
6
11
  "dependencies": {
7
12
  "lit": "^3.3.2",
8
13
  "lucide": "^0.472.0",
@@ -17,21 +22,20 @@
17
22
  }
18
23
  },
19
24
  "devDependencies": {
20
- "@storybook/addon-vitest": "^10.2.8",
21
- "@storybook/web-components": "^10.2.8",
22
- "@storybook/web-components-vite": "^10.2.8",
23
- "@vitest/browser": "^4.0.18",
24
- "@vitest/browser-playwright": "^4.0.18",
25
+ "@storybook/addon-vitest": "^10.2.19",
26
+ "@storybook/web-components": "^10.2.19",
27
+ "@storybook/web-components-vite": "^10.2.19",
28
+ "@vitest/browser": "^4.1.0",
29
+ "@vitest/browser-playwright": "^4.1.0",
25
30
  "@xterm/xterm": "6.1.0-beta.167",
26
31
  "playwright": "^1.58.2",
27
- "storybook": "^10.2.8",
32
+ "storybook": "^10.2.19",
28
33
  "typescript": "^5.7.2",
29
- "vite": "^7.3.1",
30
- "vitest": "^4.0.18"
34
+ "vite": "^8.0.0",
35
+ "vitest": "^4.1.0"
31
36
  },
32
- "scripts": {
33
- "dev": "storybook dev -p 6007",
34
- "test:browser": "vitest run --retry 2 --config vitest.storybook.config.ts",
35
- "typecheck": "tsc --noEmit"
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/jixoai/openspecui"
36
40
  }
37
- }
41
+ }
@@ -0,0 +1,22 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { getSessionScopedStorageKey } from './storage-namespace'
3
+
4
+ describe('storage namespace helpers', () => {
5
+ it('scopes hosted version entries by session', () => {
6
+ expect(
7
+ getSessionScopedStorageKey('xtermInputPanelState', {
8
+ pathname: '/versions/v2.1/index.html',
9
+ search: '?session=session-a',
10
+ })
11
+ ).toBe('hosted-session:session-a:xtermInputPanelState')
12
+ })
13
+
14
+ it('keeps non-hosted keys unchanged', () => {
15
+ expect(
16
+ getSessionScopedStorageKey('xtermInputPanelState', {
17
+ pathname: '/dashboard',
18
+ search: '?session=session-a',
19
+ })
20
+ ).toBe('xtermInputPanelState')
21
+ })
22
+ })
@@ -0,0 +1,17 @@
1
+ const HOSTED_VERSION_PATH_RE = /^\/versions\/[^/]+(?:\/|$)/
2
+
3
+ function getHostedSessionId(locationLike: Pick<Location, 'pathname' | 'search'>): string | null {
4
+ if (!HOSTED_VERSION_PATH_RE.test(locationLike.pathname)) {
5
+ return null
6
+ }
7
+ const value = new URLSearchParams(locationLike.search).get('session')?.trim()
8
+ return value ? value : null
9
+ }
10
+
11
+ export function getSessionScopedStorageKey(
12
+ baseKey: string,
13
+ locationLike: Pick<Location, 'pathname' | 'search'> = window.location
14
+ ): string {
15
+ const sessionId = getHostedSessionId(locationLike)
16
+ return sessionId ? `hosted-session:${sessionId}:${baseKey}` : baseKey
17
+ }
@@ -129,7 +129,8 @@ export const DoubleTapEvent: StoryObj = {
129
129
  pointer(canvas, 'pointerdown', cx, cy)
130
130
  pointer(canvas, 'pointerup', cx, cy)
131
131
 
132
- await new Promise((resolve) => setTimeout(resolve, 50))
132
+ // Fire the second tap immediately so CI scheduler jitter cannot push the gesture outside
133
+ // the double-tap window.
133
134
 
134
135
  // Second tap (within 300ms)
135
136
  pointer(canvas, 'pointerdown', cx, cy)
@@ -194,7 +195,8 @@ export const DragEvent: StoryObj = {
194
195
  pointer(canvas, 'pointerdown', cx, cy)
195
196
  pointer(canvas, 'pointerup', cx, cy)
196
197
 
197
- await new Promise((resolve) => setTimeout(resolve, 50))
198
+ // Start the second touch immediately so the drag sequence stays inside the same
199
+ // second-touch detection window in slower CI browsers.
198
200
 
199
201
  // Second: tap-and-hold then drag (within 300ms of first tap)
200
202
  pointer(canvas, 'pointerdown', cx, cy)
@@ -232,7 +234,7 @@ export const DragMoveDeltas: StoryObj = {
232
234
  // Tap first
233
235
  pointer(canvas, 'pointerdown', cx, cy)
234
236
  pointer(canvas, 'pointerup', cx, cy)
235
- await new Promise((resolve) => setTimeout(resolve, 50))
237
+ // Start the drag immediately so the second touch stays within the same gesture window.
236
238
 
237
239
  // Tap-and-drag
238
240
  pointer(canvas, 'pointerdown', cx, cy)
@@ -7,8 +7,19 @@ import { InputPanelAddon } from './xterm-addon.js'
7
7
  // Register all custom elements (critical — xterm-addon.ts does NOT import these)
8
8
  import './index.js'
9
9
 
10
+ const storyCleanups = new Set<() => void>()
11
+
10
12
  /** Reset singleton state between stories */
11
13
  function resetAddonState() {
14
+ for (const cleanup of storyCleanups) {
15
+ try {
16
+ cleanup()
17
+ } catch {
18
+ /* ignore test cleanup failures */
19
+ }
20
+ }
21
+ storyCleanups.clear()
22
+
12
23
  // Force-close any active instance
13
24
  const active = InputPanelAddon.activeInstance
14
25
  if (active) active.close()
@@ -33,6 +44,10 @@ function setupTerminal(container: HTMLElement, opts?: { stateKey?: string }) {
33
44
  const addon = new InputPanelAddon({ onInput: inputHandler, stateKey: opts?.stateKey })
34
45
  terminal.loadAddon(addon)
35
46
  terminal.open(container)
47
+ storyCleanups.add(() => {
48
+ addon.dispose()
49
+ terminal.dispose()
50
+ })
36
51
 
37
52
  return { terminal, addon, inputHandler }
38
53
  }
@@ -2,12 +2,12 @@ import type { ITerminalAddon, Terminal } from '@xterm/xterm'
2
2
  import { iconKeyboard, iconMousePointer2 } from './icons.js'
3
3
  import type { InputPanelLayout, InputPanelTab } from './input-panel.js'
4
4
  import type { HostPlatform } from './platform.js'
5
+ import { getSessionScopedStorageKey } from './storage-namespace.js'
5
6
 
6
7
  const SENSITIVITY = 1.5
7
8
  const EDGE_SCROLL_ZONE = 30
8
9
  const EDGE_SCROLL_INTERVAL = 50
9
10
  const EDGE_SCROLL_OVERSHOOT = 15
10
- const STATE_STORAGE_KEY = 'xtermInputPanelState'
11
11
 
12
12
  function isTouchDevice(): boolean {
13
13
  return 'ontouchstart' in window || navigator.maxTouchPoints > 0
@@ -60,7 +60,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
60
60
 
61
61
  function loadPanelStateStore(): InputPanelStateStore {
62
62
  try {
63
- const raw = localStorage.getItem(STATE_STORAGE_KEY)
63
+ const raw = localStorage.getItem(getSessionScopedStorageKey('xtermInputPanelState'))
64
64
  if (!raw) return {}
65
65
  const parsed = JSON.parse(raw)
66
66
  return isRecord(parsed) ? (parsed as InputPanelStateStore) : {}
@@ -71,7 +71,7 @@ function loadPanelStateStore(): InputPanelStateStore {
71
71
 
72
72
  function savePanelStateStore(store: InputPanelStateStore): void {
73
73
  try {
74
- localStorage.setItem(STATE_STORAGE_KEY, JSON.stringify(store))
74
+ localStorage.setItem(getSessionScopedStorageKey('xtermInputPanelState'), JSON.stringify(store))
75
75
  } catch {
76
76
  /* ignore */
77
77
  }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 OpenSpecUI Contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.