@design.estate/dees-catalog 6.6.0 → 6.7.0
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/dist_bundle/bundle.js +1481 -657
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.d.ts +41 -2
- package/dist_ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.demo.js +100 -2
- package/dist_ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.js +816 -28
- package/dist_ts_web/elements/00group-harness/interfaces.d.ts +56 -0
- package/package.json +1 -1
- package/readme.hints.md +3 -0
- package/readme.md +49 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.demo.ts +123 -1
- package/ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.ts +922 -28
- package/ts_web/elements/00group-harness/interfaces.ts +67 -0
|
@@ -280,6 +280,62 @@ export interface IHarnessSessionMeta {
|
|
|
280
280
|
/** Set false for pinned, non-sortable entries such as terminals. Defaults to true. */
|
|
281
281
|
draggable?: boolean;
|
|
282
282
|
}
|
|
283
|
+
/** Host-owned metadata for a resource that can be associated with a harness session. */
|
|
284
|
+
export interface IHarnessResourceMeta<TState extends string = string> {
|
|
285
|
+
/** Stable identity used by selection and association records. */
|
|
286
|
+
id: string;
|
|
287
|
+
/** Open resource kind supplied by the host, for example `terminal`, `file`, or `browser`. */
|
|
288
|
+
kind: string;
|
|
289
|
+
title: string;
|
|
290
|
+
/** Optional dees-icon name (e.g. `lucide:FileText`). */
|
|
291
|
+
icon?: string;
|
|
292
|
+
preview?: string;
|
|
293
|
+
createdAt?: number;
|
|
294
|
+
updatedAt?: number;
|
|
295
|
+
/** Optional host-defined display state. */
|
|
296
|
+
state?: TState;
|
|
297
|
+
}
|
|
298
|
+
/** A controlled zero-or-one session association for one resource. */
|
|
299
|
+
export interface IHarnessResourceAssociation {
|
|
300
|
+
resourceId: string;
|
|
301
|
+
sessionId: string;
|
|
302
|
+
}
|
|
303
|
+
export interface IHarnessResourceEventDetail<TState extends string = string> {
|
|
304
|
+
resource: IHarnessResourceMeta<TState>;
|
|
305
|
+
sessionId: string | null;
|
|
306
|
+
}
|
|
307
|
+
export type THarnessResourceAssociationInteractionSource = 'drop' | 'action' | 'keyboard';
|
|
308
|
+
export type THarnessResourceAssociationRequestDetail<TState extends string = string> = {
|
|
309
|
+
intent: 'attach';
|
|
310
|
+
resource: IHarnessResourceMeta<TState>;
|
|
311
|
+
fromSessionId: null;
|
|
312
|
+
toSessionId: string;
|
|
313
|
+
source: THarnessResourceAssociationInteractionSource;
|
|
314
|
+
} | {
|
|
315
|
+
intent: 'detach';
|
|
316
|
+
resource: IHarnessResourceMeta<TState>;
|
|
317
|
+
fromSessionId: string;
|
|
318
|
+
toSessionId: null;
|
|
319
|
+
source: THarnessResourceAssociationInteractionSource;
|
|
320
|
+
} | {
|
|
321
|
+
intent: 'reassign';
|
|
322
|
+
resource: IHarnessResourceMeta<TState>;
|
|
323
|
+
fromSessionId: string;
|
|
324
|
+
toSessionId: string;
|
|
325
|
+
source: THarnessResourceAssociationInteractionSource;
|
|
326
|
+
};
|
|
327
|
+
export type THarnessResourceContextSource = 'pointer' | 'action' | 'keyboard';
|
|
328
|
+
/** Detail of `harness-resource-context`; the host owns the actual menu. */
|
|
329
|
+
export interface IHarnessResourceContextDetail<TState extends string = string> extends IHarnessResourceEventDetail<TState> {
|
|
330
|
+
clientX: number;
|
|
331
|
+
clientY: number;
|
|
332
|
+
source: THarnessResourceContextSource;
|
|
333
|
+
originalEvent: MouseEvent | KeyboardEvent;
|
|
334
|
+
/** Routes a host menu choice through the component's validated controlled request event. */
|
|
335
|
+
requestAssociation: (toSessionId: string | null) => boolean;
|
|
336
|
+
/** Closes the external-menu disclosure state and restores focus to its invoking control. */
|
|
337
|
+
close: () => void;
|
|
338
|
+
}
|
|
283
339
|
/** Detail of `harness-session-context`, fired on right-click of a session item. */
|
|
284
340
|
export interface IHarnessSessionContextDetail {
|
|
285
341
|
session: IHarnessSessionMeta;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@design.estate/dees-catalog",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
|
|
6
6
|
"main": "dist_ts_web/index.js",
|
package/readme.hints.md
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
## Agent Chat Architecture
|
|
7
7
|
|
|
8
|
+
- `DeesHarnessSessionList` resource associations are controlled projections. Resources and associations remain host-owned; component-originated attach, detach, and reassign requests use `requestResourceAssociation()` and reconcile only when fresh `resourceAssociations` arrive.
|
|
9
|
+
- Resource association normalization accepts the first valid record per known resource and ignores incomplete metadata, unknown references, or later duplicate records without mutating inputs. A missing `eligibleSessionIdsByResourceId` entry means no attach/reassign target; existing authoritative associations still render and can detach.
|
|
10
|
+
- Resource pointer dragging is a separate gesture from session grip reordering. It targets an eligible whole session article, never creates a session drop slot, and never emits `harness-session-move`.
|
|
8
11
|
- `DeesHarnessMessageList` owns transcript ordering and responsive tool grouping. Consecutive tools use one generic keyed-grid pipeline partitioned by descriptor layout: compact with compact, subtask with subtask, and full-row tools spanning every column. Never move cards between active and historical owners when status changes.
|
|
9
12
|
- The transcript scroll box is the named inline-size container. Tool columns must respond to its content width so opening the session panel naturally reduces the column count.
|
|
10
13
|
- Group identity is retained through front-window eviction while any card remains. Untouched projected subtasks collapse individually on completion without adding a second grid-level disclosure; other tools keep descriptor or user disclosure state.
|
package/readme.md
CHANGED
|
@@ -1912,6 +1912,55 @@ Searchable, card-based conversation column for sidebars or a `DeesModal` (`harne
|
|
|
1912
1912
|
|
|
1913
1913
|
`IHarnessSessionMeta.state` accepts `normal`, `working`, `finished`, `attention`, or `error`; `working` remains an independent activity signal and does not change card expansion. With `enableGrouping`, sortable cards can move within and between groups. Drag from a card's focusable grip, or use its ArrowUp, ArrowDown, Home, and End keys. Pointer dragging moves a full-card preview, opens a matching drop slot, and animates surrounding cards; `harness-session-move` includes both the compatibility `index` and a stable optional `beforeSessionId` anchor. Set `draggable: false` for pinned entries such as terminals. Pass `ungroupedSessionIds` to control the order outside groups; omitted or unknown entries fall back to recency order.
|
|
1914
1914
|
|
|
1915
|
+
Generic host resources use the exported `IHarnessResourceMeta` and `IHarnessResourceAssociation` contracts. Set `.resources`, `.resourceAssociations`, `.selectedResourceId`, and `.eligibleSessionIdsByResourceId`. Unattached resources render in a labelled **Resources** list; attached resources render as compact persistent child rows inside their exact session article and stay visible while session details are collapsed. Resource metadata is presentation-only: the list never mounts a terminal, browser, file viewer, or other runtime.
|
|
1916
|
+
|
|
1917
|
+
```typescript
|
|
1918
|
+
import type {
|
|
1919
|
+
IHarnessResourceContextDetail,
|
|
1920
|
+
THarnessResourceAssociationRequestDetail,
|
|
1921
|
+
} from '@design.estate/dees-catalog';
|
|
1922
|
+
|
|
1923
|
+
const list = document.querySelector('dees-harness-session-list')!;
|
|
1924
|
+
|
|
1925
|
+
list.resources = [
|
|
1926
|
+
{ id: 'shell:build', kind: 'terminal', title: 'Build shell', icon: 'lucide:Terminal' },
|
|
1927
|
+
{ id: 'file:notes', kind: 'file', title: 'Release notes', preview: '6.7.0 draft' },
|
|
1928
|
+
];
|
|
1929
|
+
list.resourceAssociations = [
|
|
1930
|
+
{ resourceId: 'shell:build', sessionId: 'session-1' },
|
|
1931
|
+
];
|
|
1932
|
+
list.eligibleSessionIdsByResourceId = {
|
|
1933
|
+
'shell:build': ['session-1', 'session-2'],
|
|
1934
|
+
'file:notes': ['session-2'],
|
|
1935
|
+
};
|
|
1936
|
+
|
|
1937
|
+
list.addEventListener('harness-resource-association-request', async (event) => {
|
|
1938
|
+
const resourceEvent = event as CustomEvent<THarnessResourceAssociationRequestDetail>;
|
|
1939
|
+
const { resource, toSessionId } = resourceEvent.detail;
|
|
1940
|
+
await persistAssociation(resource.id, toSessionId);
|
|
1941
|
+
list.resourceAssociations = await loadAssociations();
|
|
1942
|
+
});
|
|
1943
|
+
|
|
1944
|
+
list.addEventListener('harness-resource-context', (event) => {
|
|
1945
|
+
const resourceEvent = event as CustomEvent<IHarnessResourceContextDetail>;
|
|
1946
|
+
openHostMenu({
|
|
1947
|
+
x: resourceEvent.detail.clientX,
|
|
1948
|
+
y: resourceEvent.detail.clientY,
|
|
1949
|
+
attach: (sessionId) => resourceEvent.detail.requestAssociation(sessionId),
|
|
1950
|
+
detach: () => resourceEvent.detail.requestAssociation(null),
|
|
1951
|
+
onClose: resourceEvent.detail.close,
|
|
1952
|
+
});
|
|
1953
|
+
});
|
|
1954
|
+
```
|
|
1955
|
+
|
|
1956
|
+
`harness-resource-select` and `harness-resource-open` carry `{ resource, sessionId }`. `harness-resource-context` adds viewport coordinates, the pointer/Actions/keyboard source, the original event, `requestAssociation(toSessionId)`, and `close()` for focus restoration. The visible Actions button has menu disclosure semantics; native `ContextMenu` and `Shift+F10` invoke the same host-owned path. The component does not require `DeesContextmenu` and does not own menu choices.
|
|
1957
|
+
|
|
1958
|
+
All component-originated association request paths converge on the public `requestResourceAssociation(resourceId, toSessionId, source?)` method. A valid change emits one bubbling/composed `harness-resource-association-request` discriminated by `intent: 'attach' | 'detach' | 'reassign'` and carrying `resource`, `fromSessionId`, `toSessionId`, and `source: 'drop' | 'action' | 'keyboard'`. The method returns `false` and emits nothing for unknown resources/sessions, ineligible targets, detach from an unattached resource, or the current target. It never mutates resources, associations, selection, session groups, or session order.
|
|
1959
|
+
|
|
1960
|
+
Association projection is deterministic and non-destructive: resource records without a non-empty ID, kind, or title, unknown resource/session references, and later duplicate records for a resource are ignored; the first valid association wins. Duplicate resource metadata IDs likewise use the first resource. Missing eligibility entries mean no eligible attach/reassign target. Eligibility does not hide an existing authoritative association, which remains visible and detachable. Requested changes are announced separately from completion; completion is announced only after the controlled association input reflects the requested target.
|
|
1961
|
+
|
|
1962
|
+
Search covers resource ID, kind, title, and preview. A matching attached resource reveals its parent session and only matching children; a matching session reveals all its resources; matching unattached resources remain in **Resources**. Resources render with zero sessions. Resource grip dragging edge-scrolls the list and targets only eligible whole session cards, including hits on nested card content, while never creating session reorder placeholders or emitting `harness-session-move`. Use the Actions/context path for keyboard and mobile association workflows; coarse-pointer grip and Actions targets are at least 44px.
|
|
1963
|
+
|
|
1915
1964
|
---
|
|
1916
1965
|
|
|
1917
1966
|
### Pre-built Templates
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@design.estate/dees-catalog',
|
|
6
|
-
version: '6.
|
|
6
|
+
version: '6.7.0',
|
|
7
7
|
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
|
8
8
|
}
|
package/ts_web/elements/00group-harness/dees-harness-session-list/dees-harness-session-list.demo.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { html } from '@design.estate/dees-element';
|
|
2
2
|
import type {
|
|
3
|
+
IHarnessResourceAssociation,
|
|
4
|
+
IHarnessResourceContextDetail,
|
|
5
|
+
IHarnessResourceEventDetail,
|
|
6
|
+
IHarnessResourceMeta,
|
|
3
7
|
IHarnessSessionGroup,
|
|
4
8
|
IHarnessSessionMeta,
|
|
5
9
|
IHarnessSessionMoveDetail,
|
|
10
|
+
THarnessResourceAssociationRequestDetail,
|
|
6
11
|
} from '../interfaces.js';
|
|
7
12
|
|
|
8
13
|
const demoSessions: IHarnessSessionMeta[] = Array.from({ length: 24 }, (_, index) => ({
|
|
@@ -28,8 +33,48 @@ const demoGroups: IHarnessSessionGroup[] = [
|
|
|
28
33
|
{ id: 'review', name: 'Review', sessionIds: ['session-3', 'session-4'] },
|
|
29
34
|
];
|
|
30
35
|
|
|
36
|
+
const demoResources: IHarnessResourceMeta[] = [
|
|
37
|
+
{
|
|
38
|
+
id: 'terminal:build',
|
|
39
|
+
kind: 'terminal',
|
|
40
|
+
title: 'Build shell',
|
|
41
|
+
icon: 'lucide:Terminal',
|
|
42
|
+
preview: 'pnpm run build',
|
|
43
|
+
updatedAt: Date.now() - 90_000,
|
|
44
|
+
state: 'ready',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'file:release-notes',
|
|
48
|
+
kind: 'file',
|
|
49
|
+
title: 'Release notes',
|
|
50
|
+
icon: 'lucide:FileText',
|
|
51
|
+
preview: '6.7.0 controlled resource draft',
|
|
52
|
+
updatedAt: Date.now() - 180_000,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'browser:preview',
|
|
56
|
+
kind: 'browser',
|
|
57
|
+
title: 'Documentation preview',
|
|
58
|
+
icon: 'lucide:Globe',
|
|
59
|
+
preview: 'localhost:3000',
|
|
60
|
+
updatedAt: Date.now() - 240_000,
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const demoResourceAssociations: IHarnessResourceAssociation[] = [
|
|
65
|
+
{ resourceId: 'terminal:build', sessionId: 'session-0' },
|
|
66
|
+
{ resourceId: 'file:release-notes', sessionId: 'session-0' },
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const demoResourceEligibleSessionIds = Object.fromEntries(
|
|
70
|
+
demoResources.map((resource) => [resource.id, ['session-0', 'session-1', 'session-2']]),
|
|
71
|
+
);
|
|
72
|
+
|
|
31
73
|
type TDemoSessionList = HTMLElement & {
|
|
32
74
|
groups: IHarnessSessionGroup[];
|
|
75
|
+
resources: IHarnessResourceMeta[];
|
|
76
|
+
resourceAssociations: IHarnessResourceAssociation[];
|
|
77
|
+
selectedResourceId: string;
|
|
33
78
|
selectedSessionId: string;
|
|
34
79
|
ungroupedSessionIds: string[];
|
|
35
80
|
};
|
|
@@ -57,6 +102,72 @@ const handleSessionMove = (event: CustomEvent<IHarnessSessionMoveDetail>): void
|
|
|
57
102
|
list.ungroupedSessionIds = ungroupedSessionIds;
|
|
58
103
|
};
|
|
59
104
|
|
|
105
|
+
const handleResourceAssociation = (
|
|
106
|
+
event: CustomEvent<THarnessResourceAssociationRequestDetail>,
|
|
107
|
+
): void => {
|
|
108
|
+
const list = event.currentTarget as TDemoSessionList;
|
|
109
|
+
const next = list.resourceAssociations.filter(
|
|
110
|
+
(association) => association.resourceId !== event.detail.resource.id,
|
|
111
|
+
);
|
|
112
|
+
if (event.detail.toSessionId) {
|
|
113
|
+
next.push({
|
|
114
|
+
resourceId: event.detail.resource.id,
|
|
115
|
+
sessionId: event.detail.toSessionId,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
list.resourceAssociations = next;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const handleResourceContext = async (
|
|
122
|
+
event: CustomEvent<IHarnessResourceContextDetail>,
|
|
123
|
+
): Promise<void> => {
|
|
124
|
+
const list = event.currentTarget as TDemoSessionList;
|
|
125
|
+
const { DeesContextmenu } = await import('../../00group-overlay/dees-contextmenu/dees-contextmenu.js');
|
|
126
|
+
const currentSessionId = event.detail.sessionId;
|
|
127
|
+
const eligibleSessionIds = ['session-0', 'session-1', 'session-2'];
|
|
128
|
+
const menu = await DeesContextmenu.openContextMenuWithOptions(
|
|
129
|
+
new MouseEvent('contextmenu', {
|
|
130
|
+
clientX: event.detail.clientX,
|
|
131
|
+
clientY: event.detail.clientY,
|
|
132
|
+
}),
|
|
133
|
+
[
|
|
134
|
+
...eligibleSessionIds
|
|
135
|
+
.filter((sessionId) => sessionId !== currentSessionId)
|
|
136
|
+
.map((sessionId) => ({
|
|
137
|
+
name: `${currentSessionId ? 'Reassign' : 'Attach'} to ${
|
|
138
|
+
demoSessions.find((session) => session.id === sessionId)?.title
|
|
139
|
+
}`,
|
|
140
|
+
iconName: 'lucide:Link',
|
|
141
|
+
action: async () => {
|
|
142
|
+
event.detail.requestAssociation(sessionId);
|
|
143
|
+
event.detail.close();
|
|
144
|
+
},
|
|
145
|
+
})),
|
|
146
|
+
...(currentSessionId
|
|
147
|
+
? [{
|
|
148
|
+
name: 'Detach',
|
|
149
|
+
iconName: 'lucide:Unlink',
|
|
150
|
+
action: async () => {
|
|
151
|
+
event.detail.requestAssociation(null);
|
|
152
|
+
event.detail.close();
|
|
153
|
+
},
|
|
154
|
+
}]
|
|
155
|
+
: []),
|
|
156
|
+
],
|
|
157
|
+
);
|
|
158
|
+
if (!menu) {
|
|
159
|
+
event.detail.close();
|
|
160
|
+
} else {
|
|
161
|
+
const observer = new MutationObserver(() => {
|
|
162
|
+
if (menu.isConnected) return;
|
|
163
|
+
observer.disconnect();
|
|
164
|
+
event.detail.close();
|
|
165
|
+
});
|
|
166
|
+
observer.observe(document.body, { childList: true });
|
|
167
|
+
}
|
|
168
|
+
list.selectedResourceId = event.detail.resource.id;
|
|
169
|
+
};
|
|
170
|
+
|
|
60
171
|
const openModal = async () => {
|
|
61
172
|
const { DeesModal } = await import('../../00group-overlay/dees-modal/dees-modal.js');
|
|
62
173
|
const content = document.createElement('dees-harness-session-list');
|
|
@@ -95,15 +206,26 @@ export const demoFunc = () => html`
|
|
|
95
206
|
</style>
|
|
96
207
|
<div class="sessionDemo">
|
|
97
208
|
<dees-button @clicked=${openModal}>Open in a DeesModal</dees-button>
|
|
98
|
-
<div class="demoHint">
|
|
209
|
+
<div class="demoHint">
|
|
210
|
+
Use a conversation grip to reorder sessions. Use a resource grip to associate it with a
|
|
211
|
+
session, or open Actions with click, ContextMenu, or Shift+F10 for attach, reassign, and detach.
|
|
212
|
+
</div>
|
|
99
213
|
<dees-harness-session-list
|
|
100
214
|
.sessions=${demoSessions}
|
|
101
215
|
.groups=${demoGroups}
|
|
102
216
|
.ungroupedSessionIds=${demoSessions.slice(5).map((session) => session.id)}
|
|
217
|
+
.resources=${demoResources}
|
|
218
|
+
.resourceAssociations=${demoResourceAssociations}
|
|
219
|
+
.eligibleSessionIdsByResourceId=${demoResourceEligibleSessionIds}
|
|
103
220
|
.enableGrouping=${true}
|
|
104
221
|
selectedSessionId="session-2"
|
|
105
222
|
@harness-session-select=${handleSessionSelect}
|
|
106
223
|
@harness-session-move=${handleSessionMove}
|
|
224
|
+
@harness-resource-select=${(event: CustomEvent<IHarnessResourceEventDetail>) => {
|
|
225
|
+
(event.currentTarget as TDemoSessionList).selectedResourceId = event.detail.resource.id;
|
|
226
|
+
}}
|
|
227
|
+
@harness-resource-context=${handleResourceContext}
|
|
228
|
+
@harness-resource-association-request=${handleResourceAssociation}
|
|
107
229
|
></dees-harness-session-list>
|
|
108
230
|
</div>
|
|
109
231
|
`;
|