Skip to content

Commit 7df3648

Browse files
committed
fix(hub): address panel state review feedback
1 parent db3a5a3 commit 7df3648

5 files changed

Lines changed: 44 additions & 31 deletions

File tree

‎docs/content/1.guide/17.client-context.md‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,21 @@ The context carries the [RPC client](/guide/client) (`rpc`) and the page's `clie
3838

3939
### Tracking panel state
4040

41-
`ctx.panel.state` is the current dock-panel snapshot. It contains `state: 'open' | 'closed' | 'hidden'` and includes `selectedDockId` while a dock is selected. Subscribe to `ctx.panel.events` for later changes:
41+
`ctx.panel.state` is the current dock panel snapshot. It contains `state: 'open' | 'closed' | 'hidden'` and includes `selectedDockId` while a dock is selected. Subscribe to [`ctx.panel.events`](/references/events#client-context-events) for later changes:
4242

4343
```ts
4444
import type { DockClientScriptContext } from '@devframes/hub/client'
4545
import { HUB_EVENTS } from '@devframes/hub/constants'
4646

4747
export default function setup(context: DockClientScriptContext) {
48-
void context.rpc.call('my-devframe:panel-state', context.panel.state)
48+
const reportPanelState = (panelState: typeof context.panel.state) => {
49+
void context.rpc.call('my-devframe:panel-state', panelState).catch(error => console.error(error))
50+
}
51+
52+
reportPanelState(context.panel.state)
4953
context.panel.events.on(
5054
HUB_EVENTS.client.docksPanelStateChanged,
51-
panelState => void context.rpc.call('my-devframe:panel-state', panelState),
55+
reportPanelState,
5256
)
5357
}
5458
```

‎docs/content/8.references/6.hub-api.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ The properties of `DevframeClientContext` — [The client context](/guide/client
105105
| `rpc` | The [RPC client](/guide/client) — server/client functions, shared state. |
106106
| `clientType` | `'embedded'` (inside the user app) or `'standalone'` (independent hub page). |
107107
| `docks` | `entries`, `selected`, `groupedEntries`, `switchEntry()`, `toggleEntry()`, `getStateById()`, `register()` / `update()` for [client-only docks](/guide/client-context#client-only-docks). |
108-
| `panel` | Dock panel state: position, size, drag/resize. |
108+
| `panel` | Current `state`, local `events`, session, position, size, and drag/resize state for the dock panel. |
109109
| `commands` | Command palette: `register()`, `execute()`, `getKeybindings()`. |
110110
| `renderers` | Dock-renderer registry — `register()`, `get()`, `has()`, `mount(entry, container)`. Routes a dock `type` to a renderer (local boot or the hub's [manifest](/guide/hub-initiate#renderer-modules); local wins). `mount()` resolves a `status`: `mounted` (with `dispose`), `missing-renderer`, or `load-error` (with `error`). |
111111
| `when` | The [when-clause](/references/when-clauses) context. |

‎packages/hub-ui/src/client/state/context.test.ts‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,11 @@ async function flushRestore(): Promise<void> {
7272
await Promise.resolve()
7373
await Promise.resolve()
7474
await nextTick()
75-
await Promise.resolve()
76-
await Promise.resolve()
7775
}
7876

7977
describe('createDocksContext', () => {
8078
it('exposes restored panel state and emits selected, hidden, and closed changes', async () => {
81-
expect.assertions(8)
79+
expect.assertions(9)
8280

8381
const { rpc, sharedStates, trust } = createStubRpc()
8482
const panelVisible = ref(false)
@@ -94,6 +92,13 @@ describe('createDocksContext', () => {
9492
panelState => panelStates.push(panelState),
9593
)
9694

95+
panelVisible.value = true
96+
await nextTick()
97+
expect(panelStates).toEqual([{ state: 'open', selectedDockId: 'git' }])
98+
panelVisible.value = false
99+
await nextTick()
100+
panelStates.length = 0
101+
97102
trust()
98103
sharedStates.get('devframe:docks')!.push([gitEntry])
99104
sharedStates.get('devframe:dock-renderers')!.push({})
@@ -129,11 +134,15 @@ describe('createDocksContext', () => {
129134
})
130135

131136
it('mounts a restored dock once after all initial server state arrives', async () => {
132-
expect.assertions(7)
137+
expect.assertions(8)
133138

134139
const { rpc, sharedStates, trust } = createStubRpc()
135140
const executeSetupScriptMock = vi.mocked(executeSetupScript)
136141
executeSetupScriptMock.mockClear()
142+
let setupPanelState: DevframeDockPanelState | undefined
143+
executeSetupScriptMock.mockImplementationOnce(async (_dockEntry, scriptContext) => {
144+
setupPanelState = scriptContext.panel.state
145+
})
137146
const session = ref<DockSessionStorage>({
138147
open: true,
139148
selectedDockId: 'git',
@@ -159,6 +168,7 @@ describe('createDocksContext', () => {
159168

160169
expect(context.docks.selected?.id).toBe('git')
161170
expect(session.value.open).toBe(true)
171+
expect(setupPanelState).toEqual({ state: 'open', selectedDockId: 'git' })
162172
expect(executeSetupScriptMock).toHaveBeenCalledOnce()
163173
})
164174

‎packages/hub-ui/src/client/state/context.ts‎

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,10 @@ export async function createDocksContext(
272272
return false
273273
}
274274

275+
initialRestorePending.value = false
276+
selectedDockId.value = entry.id
277+
sessionStore.value.open = true
278+
275279
// If has import script, run it
276280
if (
277281
(entry.type === 'action')
@@ -302,9 +306,6 @@ export async function createDocksContext(
302306
if (entry.groupId)
303307
(sessionStore.value.groupLastChildIds ??= {})[entry.groupId] = entry.id
304308

305-
initialRestorePending.value = false
306-
selectedDockId.value = entry.id
307-
sessionStore.value.open = true
308309
// Only an iframe dock owns an address-bar route; ViewIframe keeps
309310
// `session.selectedDockRoute` current for it. Clear it for anything else so a stale
310311
// route from a previous iframe isn't persisted against a non-iframe dock.
@@ -687,26 +688,24 @@ export async function createDocksContext(
687688
initialRestorePending.value = false
688689
await switchEntry(restoreDockId)
689690
}
690-
const startPanelStateEvents = (): void => {
691-
let previousPanelState = docksContext.panel.state
692-
watch(
693-
[panelVisible, () => sessionStore.value.open, selectedDockId],
694-
() => {
695-
const panelState = docksContext.panel.state
696-
if (
697-
panelState.state === previousPanelState.state
698-
&& panelState.selectedDockId === previousPanelState.selectedDockId
699-
) {
700-
return
701-
}
691+
let previousPanelState = docksContext.panel.state
692+
watch(
693+
[panelVisible, () => sessionStore.value.open, selectedDockId],
694+
() => {
695+
const panelState = docksContext.panel.state
696+
if (
697+
panelState.state === previousPanelState.state
698+
&& panelState.selectedDockId === previousPanelState.selectedDockId
699+
) {
700+
return
701+
}
702702

703-
previousPanelState = panelState
704-
panelEvents.emit(HUB_EVENTS.client.docksPanelStateChanged, panelState)
705-
},
706-
{ flush: 'post' },
707-
)
708-
}
709-
void restoreAfterInitialization().then(startPanelStateEvents, startPanelStateEvents)
703+
previousPanelState = panelState
704+
panelEvents.emit(HUB_EVENTS.client.docksPanelStateChanged, panelState)
705+
},
706+
{ flush: 'post' },
707+
)
708+
void restoreAfterInitialization()
710709

711710
docksContextByRpc.set(rpc, docksContext)
712711
return docksContext

‎packages/hub/src/client/docks.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ export interface WhenClauseContext {
126126
export type DevframeClientContext = DocksContext
127127

128128
export interface DocksPanelEvents {
129-
'panel:state:changed': (state: DevframeDockPanelState) => void
129+
'panel:state:changed': (panelState: DevframeDockPanelState) => void
130130
}
131131

132132
export interface DocksPanelContext {

0 commit comments

Comments
 (0)