@design.estate/dees-catalog 9.3.0 → 9.4.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 +1372 -2727
- package/dist_bundle/bundle.js.map +1 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/00group-button/dees-button/dees-button.js +2 -1
- package/dist_ts_web/elements/00group-input/dees-input-quantityselector/dees-input-quantityselector.js +4 -2
- package/dist_ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.d.ts +11 -9
- package/dist_ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.demo.d.ts +2 -2
- package/dist_ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.demo.js +148 -319
- package/dist_ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.js +60 -279
- package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.d.ts +25 -10
- package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.demo.d.ts +3 -4
- package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.demo.js +229 -388
- package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.js +253 -813
- package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.demo.d.ts +2 -0
- package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.demo.js +150 -389
- package/dist_ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.js +67 -11
- package/package.json +1 -1
- package/readme.hints.md +26 -0
- package/readme.md +76 -15
- package/scripts/bdtheme-baseline.json +0 -2
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00group-button/dees-button/dees-button.ts +1 -0
- package/ts_web/elements/00group-input/dees-input-quantityselector/dees-input-quantityselector.ts +3 -1
- package/ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.demo.ts +67 -332
- package/ts_web/elements/00group-simple/dees-shopping-productcard/dees-shopping-productcard.ts +65 -322
- package/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.demo.ts +136 -382
- package/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.ts +224 -872
- package/ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.demo.ts +116 -421
- package/ts_web/elements/00group-simple/dees-simple-login/dees-simple-login.ts +66 -10
|
@@ -1,27 +1,13 @@
|
|
|
1
|
+
import { customElement, html, DeesElement, property, state, cssManager, css, type TemplateResult } from '@design.estate/dees-element';
|
|
1
2
|
import { demoFunc } from './dees-simple-appdash.demo.js';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
customElement,
|
|
5
|
-
html,
|
|
6
|
-
DeesElement,
|
|
7
|
-
property,
|
|
8
|
-
type TemplateResult,
|
|
9
|
-
cssManager,
|
|
10
|
-
css,
|
|
11
|
-
unsafeCSS,
|
|
12
|
-
type CSSResult,
|
|
13
|
-
state,
|
|
14
|
-
domtools,
|
|
15
|
-
} from '@design.estate/dees-element';
|
|
3
|
+
import { themeDefaultStyles } from '../../00theme.js';
|
|
16
4
|
import '../../00group-utility/dees-icon/dees-icon.js';
|
|
5
|
+
import '../../00group-button/dees-button/dees-button.js';
|
|
6
|
+
import '../../00group-overlay/dees-speechbubble/dees-speechbubble.js';
|
|
17
7
|
import type { DeesWorkspaceTerminal } from '../../00group-workspace/dees-workspace-terminal/dees-workspace-terminal.js';
|
|
18
|
-
import {
|
|
8
|
+
import type { IExecutionEnvironment } from '../../00group-runtime/index.js';
|
|
19
9
|
|
|
20
|
-
declare global {
|
|
21
|
-
interface HTMLElementTagNameMap {
|
|
22
|
-
'dees-simple-appdash': DeesSimpleAppDash;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
10
|
+
declare global { interface HTMLElementTagNameMap { 'dees-simple-appdash': DeesSimpleAppDash; } }
|
|
25
11
|
|
|
26
12
|
export interface IView {
|
|
27
13
|
name: string;
|
|
@@ -29,15 +15,8 @@ export interface IView {
|
|
|
29
15
|
element?: DeesElement['constructor']['prototype'];
|
|
30
16
|
subViews?: IView[];
|
|
31
17
|
}
|
|
32
|
-
|
|
33
18
|
export type TGlobalMessageType = 'info' | 'success' | 'warning' | 'error';
|
|
34
|
-
|
|
35
|
-
export interface IGlobalMessageAction {
|
|
36
|
-
name: string;
|
|
37
|
-
iconName?: string;
|
|
38
|
-
action: () => void | Promise<void>;
|
|
39
|
-
}
|
|
40
|
-
|
|
19
|
+
export interface IGlobalMessageAction { name: string; iconName?: string; action: () => void | Promise<void>; }
|
|
41
20
|
export interface IGlobalMessage {
|
|
42
21
|
id: string;
|
|
43
22
|
type: TGlobalMessageType;
|
|
@@ -47,878 +26,251 @@ export interface IGlobalMessage {
|
|
|
47
26
|
actions?: IGlobalMessageAction[];
|
|
48
27
|
}
|
|
49
28
|
|
|
29
|
+
/** A compact app shell. Views, notices and the terminal share one bounded workspace. */
|
|
50
30
|
@customElement('dees-simple-appdash')
|
|
51
31
|
export class DeesSimpleAppDash extends DeesElement {
|
|
52
|
-
// STATIC
|
|
53
32
|
public static demo = demoFunc;
|
|
54
33
|
public static demoGroups = ['Simple'];
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@property()
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
@property({ type: Array })
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
@
|
|
64
|
-
accessor
|
|
65
|
-
|
|
66
|
-
@
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
color: var(--dees-color-text-primary);
|
|
82
|
-
user-select: none;
|
|
83
|
-
display: block;
|
|
84
|
-
overflow: hidden;
|
|
85
|
-
position: relative;
|
|
86
|
-
height: 100%;
|
|
87
|
-
width: 100%;
|
|
88
|
-
/* scroll-fade gradient pairs — neutral shadow tints, stronger in dark
|
|
89
|
-
(dark defaults, bright override below) */
|
|
90
|
-
--appdash-fade-strong: rgba(0, 0, 0, 0.4);
|
|
91
|
-
--appdash-fade-soft: rgba(0, 0, 0, 0.12);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
:host([gobright]) {
|
|
95
|
-
--appdash-fade-strong: rgba(0, 0, 0, 0.08);
|
|
96
|
-
--appdash-fade-soft: rgba(0, 0, 0, 0.03);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
.maincontainer {
|
|
100
|
-
position: absolute;
|
|
101
|
-
top: 0px;
|
|
102
|
-
left: 0px;
|
|
103
|
-
right: 0px;
|
|
104
|
-
bottom: 0px;
|
|
105
|
-
overflow: hidden;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.appbar {
|
|
109
|
-
position: absolute;
|
|
110
|
-
top: 0px;
|
|
111
|
-
left: 0px;
|
|
112
|
-
height: calc(100% - 24px);
|
|
113
|
-
width: 240px;
|
|
114
|
-
background: var(--dees-color-bg-secondary);
|
|
115
|
-
border-right: 1px solid var(--dees-color-border-default);
|
|
116
|
-
font-size: var(--dees-font-control-size-sm);
|
|
117
|
-
font-family: var(--dees-font-family);
|
|
118
|
-
z-index: 2;
|
|
119
|
-
display: grid;
|
|
120
|
-
grid-template-rows: auto 1fr min-content;
|
|
121
|
-
overflow: hidden;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
.sidebar-header {
|
|
125
|
-
padding: var(--dees-spacing-lg);
|
|
126
|
-
border-bottom: 1px solid var(--dees-color-border-default);
|
|
127
|
-
display: flex;
|
|
128
|
-
align-items: center;
|
|
129
|
-
gap: var(--dees-spacing-md);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.header-icon-wrapper {
|
|
133
|
-
display: flex;
|
|
134
|
-
align-items: center;
|
|
135
|
-
justify-content: center;
|
|
136
|
-
width: 36px;
|
|
137
|
-
height: 36px;
|
|
138
|
-
border-radius: var(--dees-radius-lg);
|
|
139
|
-
corner-shape: var(--dees-corner-shape);
|
|
140
|
-
background: var(--dees-color-bg-tertiary);
|
|
141
|
-
box-shadow: var(--dees-shadow-xs);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
.header-icon-wrapper dees-icon {
|
|
145
|
-
font-size: 18px;
|
|
146
|
-
color: var(--dees-color-text-primary);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
.appName {
|
|
150
|
-
font-size: var(--dees-font-subhead-size);
|
|
151
|
-
font-weight: 600;
|
|
152
|
-
color: var(--dees-color-text-primary);
|
|
153
|
-
white-space: nowrap;
|
|
154
|
-
overflow: hidden;
|
|
155
|
-
text-overflow: ellipsis;
|
|
156
|
-
letter-spacing: -0.02em;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.viewTabs-container {
|
|
160
|
-
overflow-y: auto;
|
|
161
|
-
padding: 12px 8px;
|
|
162
|
-
scrollbar-width: thin;
|
|
163
|
-
scrollbar-color: var(--dees-color-scrollbar-thumb) transparent;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
.viewTabs-container::-webkit-scrollbar {
|
|
167
|
-
width: 6px;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
.viewTabs-container::-webkit-scrollbar-track {
|
|
171
|
-
background: transparent;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.viewTabs-container::-webkit-scrollbar-thumb {
|
|
175
|
-
background: var(--dees-color-scrollbar-thumb);
|
|
176
|
-
border-radius: 3px;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
.viewTabs-container::-webkit-scrollbar-thumb:hover {
|
|
180
|
-
background: var(--dees-color-scrollbar-thumb-hover);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
.section-label {
|
|
184
|
-
font-size: var(--dees-font-caption2-size);
|
|
185
|
-
font-weight: 600;
|
|
186
|
-
text-transform: uppercase;
|
|
187
|
-
letter-spacing: 0.05em;
|
|
188
|
-
color: var(--dees-color-text-secondary);
|
|
189
|
-
padding: var(--dees-spacing-sm) var(--dees-spacing-md);
|
|
190
|
-
margin-bottom: var(--dees-spacing-xs);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
.viewTabs {
|
|
194
|
-
display: flex;
|
|
195
|
-
flex-direction: column;
|
|
196
|
-
gap: 2px;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
.viewTab {
|
|
200
|
-
display: flex;
|
|
201
|
-
align-items: center;
|
|
202
|
-
gap: 10px;
|
|
203
|
-
padding: 10px 12px;
|
|
204
|
-
cursor: default;
|
|
205
|
-
transition: all var(--dees-transition-fast) var(--dees-ease-standard);
|
|
206
|
-
color: var(--dees-color-text-secondary);
|
|
207
|
-
user-select: none;
|
|
208
|
-
position: relative;
|
|
209
|
-
border-radius: var(--dees-radius-md);
|
|
210
|
-
corner-shape: var(--dees-corner-shape);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
.viewTab:hover {
|
|
214
|
-
background: var(--dees-color-hover);
|
|
215
|
-
color: var(--dees-color-text-primary);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
.viewTab:active {
|
|
219
|
-
background: var(--dees-color-active);
|
|
220
|
-
transform: scale(0.99);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
.viewTab.selected {
|
|
224
|
-
background: color-mix(in srgb, var(--dees-color-accent-primary) 12%, transparent);
|
|
225
|
-
color: var(--dees-color-accent-primary);
|
|
226
|
-
font-weight: 500;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
.viewTab.selected::before {
|
|
230
|
-
content: '';
|
|
231
|
-
position: absolute;
|
|
232
|
-
left: 0;
|
|
233
|
-
top: 8px;
|
|
234
|
-
bottom: 8px;
|
|
235
|
-
width: 3px;
|
|
236
|
-
border-radius: 0 2px 2px 0;
|
|
237
|
-
background: var(--dees-color-accent-primary);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
.viewTab dees-icon {
|
|
241
|
-
font-size: 16px;
|
|
242
|
-
opacity: 0.55;
|
|
243
|
-
transition: all var(--dees-transition-fast) var(--dees-ease-standard);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
.viewTab:hover dees-icon {
|
|
247
|
-
opacity: 0.75;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
.viewTab.selected dees-icon {
|
|
251
|
-
opacity: 1;
|
|
252
|
-
color: var(--dees-color-accent-primary);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
.viewTab span {
|
|
256
|
-
flex: 1;
|
|
257
|
-
overflow: hidden;
|
|
258
|
-
text-overflow: ellipsis;
|
|
259
|
-
white-space: nowrap;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
.viewTab .chevron {
|
|
263
|
-
flex: 0 0 auto;
|
|
264
|
-
font-size: 14px;
|
|
265
|
-
opacity: 0.5;
|
|
266
|
-
transform: rotate(-90deg);
|
|
267
|
-
transition: transform 0.2s ease, opacity 0.15s ease;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
.viewTab.hasSubs:hover .chevron {
|
|
271
|
-
opacity: 0.75;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
.viewTab.hasSubs.groupActive .chevron {
|
|
275
|
-
transform: rotate(0deg);
|
|
276
|
-
opacity: 0.9;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
.subViews {
|
|
280
|
-
display: grid;
|
|
281
|
-
grid-template-rows: 0fr;
|
|
282
|
-
margin-left: 12px;
|
|
283
|
-
position: relative;
|
|
284
|
-
transition:
|
|
285
|
-
grid-template-rows 0.25s var(--dees-ease-standard),
|
|
286
|
-
margin-top 0.25s var(--dees-ease-standard),
|
|
287
|
-
margin-bottom 0.25s var(--dees-ease-standard);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
.subViews.expanded {
|
|
291
|
-
grid-template-rows: 1fr;
|
|
292
|
-
margin-top: 2px;
|
|
293
|
-
margin-bottom: 4px;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
.subViews::before {
|
|
297
|
-
content: '';
|
|
298
|
-
position: absolute;
|
|
299
|
-
left: 0;
|
|
300
|
-
top: 4px;
|
|
301
|
-
bottom: 4px;
|
|
302
|
-
width: 1px;
|
|
303
|
-
background: var(--dees-color-border-default);
|
|
304
|
-
opacity: 0;
|
|
305
|
-
transition: opacity 0.2s ease;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
.subViews.expanded::before {
|
|
309
|
-
opacity: 1;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
.subViews-inner {
|
|
313
|
-
min-height: 0;
|
|
314
|
-
overflow: hidden;
|
|
315
|
-
display: flex;
|
|
316
|
-
flex-direction: column;
|
|
317
|
-
gap: 2px;
|
|
318
|
-
padding-left: 12px;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
.viewTab.sub {
|
|
322
|
-
padding: var(--dees-spacing-sm) var(--dees-spacing-md);
|
|
323
|
-
font-size: var(--dees-font-caption1-size);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
.viewTab.sub dees-icon {
|
|
327
|
-
font-size: 14px;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
.viewTab.sub.selected::before {
|
|
331
|
-
left: -12px;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
.appActions {
|
|
335
|
-
padding: 12px 8px;
|
|
336
|
-
border-top: 1px solid var(--dees-color-border-default);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
.action {
|
|
340
|
-
display: flex;
|
|
341
|
-
align-items: center;
|
|
342
|
-
gap: 10px;
|
|
343
|
-
padding: 10px 12px;
|
|
344
|
-
border-radius: var(--dees-radius-md);
|
|
345
|
-
corner-shape: var(--dees-corner-shape);
|
|
346
|
-
cursor: default;
|
|
347
|
-
transition: all var(--dees-transition-fast) var(--dees-ease-standard);
|
|
348
|
-
color: var(--dees-color-text-secondary);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
.action:hover {
|
|
352
|
-
background: color-mix(in srgb, var(--dees-color-accent-error) 10%, transparent);
|
|
353
|
-
color: var(--dees-color-text-error);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
.action dees-icon {
|
|
357
|
-
font-size: 16px;
|
|
358
|
-
opacity: 0.6;
|
|
359
|
-
transition: all var(--dees-transition-fast) var(--dees-ease-standard);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
.action:hover dees-icon {
|
|
363
|
-
opacity: 0.9;
|
|
364
|
-
color: var(--dees-color-text-error);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
.appcontent {
|
|
368
|
-
z-index: 1;
|
|
369
|
-
position: absolute;
|
|
370
|
-
top: 0px;
|
|
371
|
-
right: 0px;
|
|
372
|
-
height: calc(100% - 24px);
|
|
373
|
-
bottom: 24px;
|
|
374
|
-
width: calc(100% - 240px);
|
|
375
|
-
overflow: auto;
|
|
376
|
-
scrollbar-gutter: stable;
|
|
377
|
-
background: var(--dees-color-bg-canvas);
|
|
378
|
-
overscroll-behavior: contain;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
.appcontent::before {
|
|
382
|
-
content: '';
|
|
383
|
-
position: sticky;
|
|
384
|
-
top: 0;
|
|
385
|
-
left: 0;
|
|
386
|
-
right: 0;
|
|
387
|
-
display: block;
|
|
388
|
-
height: 8px;
|
|
389
|
-
margin-bottom: -8px;
|
|
390
|
-
z-index: 10;
|
|
391
|
-
pointer-events: none;
|
|
392
|
-
background: linear-gradient(
|
|
393
|
-
to bottom,
|
|
394
|
-
var(--appdash-fade-strong),
|
|
395
|
-
var(--appdash-fade-soft),
|
|
396
|
-
transparent
|
|
397
|
-
);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
.controlbar::before {
|
|
401
|
-
content: '';
|
|
402
|
-
position: absolute;
|
|
403
|
-
top: -8px;
|
|
404
|
-
left: 240px;
|
|
405
|
-
right: 0;
|
|
406
|
-
height: 8px;
|
|
407
|
-
pointer-events: none;
|
|
408
|
-
background: linear-gradient(
|
|
409
|
-
to top,
|
|
410
|
-
var(--appdash-fade-strong),
|
|
411
|
-
var(--appdash-fade-soft),
|
|
412
|
-
transparent
|
|
413
|
-
);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
.controlbar {
|
|
417
|
-
color: var(--dees-color-text-muted);
|
|
418
|
-
position: absolute;
|
|
419
|
-
bottom: 0px;
|
|
420
|
-
left: 0px;
|
|
421
|
-
width: 100%;
|
|
422
|
-
height: 24px;
|
|
423
|
-
background: var(--dees-color-bg-tertiary);
|
|
424
|
-
border-top: 1px solid var(--dees-color-border-default);
|
|
425
|
-
z-index: 11;
|
|
426
|
-
display: flex;
|
|
427
|
-
justify-content: flex-end;
|
|
428
|
-
align-items: center;
|
|
429
|
-
flex-direction: row;
|
|
430
|
-
font-size: var(--dees-font-caption2-size);
|
|
431
|
-
font-weight: 500;
|
|
432
|
-
letter-spacing: 0.01em;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
.control {
|
|
436
|
-
display: flex;
|
|
437
|
-
align-items: center;
|
|
438
|
-
gap: 6px;
|
|
439
|
-
padding: 0 12px;
|
|
440
|
-
height: 100%;
|
|
441
|
-
white-space: nowrap;
|
|
442
|
-
cursor: default;
|
|
443
|
-
color: var(--dees-color-text-muted);
|
|
444
|
-
transition: all var(--dees-transition-fast) var(--dees-ease-standard);
|
|
445
|
-
border-left: 1px solid var(--dees-color-border-strong);
|
|
446
|
-
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
.control:first-child {
|
|
450
|
-
border-left: none;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
.control:hover {
|
|
454
|
-
background: var(--dees-color-hover);
|
|
455
|
-
color: var(--dees-color-text-primary);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
.control dees-icon {
|
|
459
|
-
font-size: 13px;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
.control.status-connected dees-icon {
|
|
463
|
-
color: var(--dees-color-text-success);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
.control.status-terminal dees-icon {
|
|
467
|
-
color: var(--dees-color-text-warning);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
/* Global Message Banners */
|
|
471
|
-
.messageBannerArea {
|
|
472
|
-
position: absolute;
|
|
473
|
-
top: 0;
|
|
474
|
-
left: 240px;
|
|
475
|
-
right: 0;
|
|
476
|
-
z-index: 3;
|
|
477
|
-
display: flex;
|
|
478
|
-
flex-direction: column;
|
|
479
|
-
transition: height 0.25s var(--dees-ease-standard);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
.messageBanner {
|
|
483
|
-
display: flex;
|
|
484
|
-
align-items: center;
|
|
485
|
-
gap: var(--dees-spacing-md);
|
|
486
|
-
padding: 10px var(--dees-spacing-lg);
|
|
487
|
-
font-size: var(--dees-font-footnote-size);
|
|
488
|
-
font-family: var(--dees-font-family);
|
|
489
|
-
font-weight: 500;
|
|
490
|
-
border-bottom: 1px solid var(--dees-color-border-default);
|
|
491
|
-
animation: bannerSlideDown 0.25s var(--dees-ease-standard) forwards;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
.messageBanner.dismissing {
|
|
495
|
-
animation: bannerSlideUp 0.2s var(--dees-ease-standard) forwards;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
@keyframes bannerSlideDown {
|
|
499
|
-
from {
|
|
500
|
-
opacity: 0;
|
|
501
|
-
transform: translateY(-100%);
|
|
502
|
-
}
|
|
503
|
-
to {
|
|
504
|
-
opacity: 1;
|
|
505
|
-
transform: translateY(0);
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
@keyframes bannerSlideUp {
|
|
510
|
-
from {
|
|
511
|
-
opacity: 1;
|
|
512
|
-
transform: translateY(0);
|
|
513
|
-
}
|
|
514
|
-
to {
|
|
515
|
-
opacity: 0;
|
|
516
|
-
transform: translateY(-100%);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
.messageBanner dees-icon {
|
|
521
|
-
font-size: 16px;
|
|
522
|
-
flex-shrink: 0;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
.messageBanner-text {
|
|
526
|
-
flex: 1;
|
|
527
|
-
overflow: hidden;
|
|
528
|
-
text-overflow: ellipsis;
|
|
529
|
-
white-space: nowrap;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
.messageBanner-actions {
|
|
533
|
-
display: flex;
|
|
534
|
-
align-items: center;
|
|
535
|
-
gap: 6px;
|
|
536
|
-
flex-shrink: 0;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
.messageBanner-action {
|
|
540
|
-
display: flex;
|
|
541
|
-
align-items: center;
|
|
542
|
-
gap: 6px;
|
|
543
|
-
padding: var(--dees-spacing-xs) var(--dees-spacing-md);
|
|
544
|
-
border-radius: var(--dees-radius-sm);
|
|
545
|
-
corner-shape: var(--dees-corner-shape);
|
|
546
|
-
cursor: default;
|
|
547
|
-
font-size: var(--dees-font-caption1-size);
|
|
548
|
-
font-weight: 600;
|
|
549
|
-
letter-spacing: 0.01em;
|
|
550
|
-
transition: all var(--dees-transition-fast) var(--dees-ease-standard);
|
|
551
|
-
white-space: nowrap;
|
|
552
|
-
background: var(--dees-color-pressed);
|
|
553
|
-
color: inherit;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
.messageBanner-action:hover {
|
|
557
|
-
background: color-mix(in srgb, var(--dees-color-text-primary) 15%, transparent);
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
.messageBanner-action:active {
|
|
561
|
-
transform: scale(0.97);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
.messageBanner-action dees-icon {
|
|
565
|
-
font-size: 13px;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
.messageBanner-dismiss {
|
|
569
|
-
flex-shrink: 0;
|
|
570
|
-
display: flex;
|
|
571
|
-
align-items: center;
|
|
572
|
-
justify-content: center;
|
|
573
|
-
width: 24px;
|
|
574
|
-
height: 24px;
|
|
575
|
-
border-radius: var(--dees-radius-sm);
|
|
576
|
-
corner-shape: var(--dees-corner-shape);
|
|
577
|
-
cursor: default;
|
|
578
|
-
opacity: 0.5;
|
|
579
|
-
transition: all var(--dees-transition-fast) var(--dees-ease-standard);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
.messageBanner-dismiss:hover {
|
|
583
|
-
opacity: 1;
|
|
584
|
-
background: var(--dees-color-pressed);
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
/* Message type: info */
|
|
588
|
-
.messageBanner-info {
|
|
589
|
-
background: color-mix(in srgb, var(--dees-color-accent-primary) 12%, transparent);
|
|
590
|
-
color: var(--dees-color-accent-primary);
|
|
591
|
-
border-left: 3px solid var(--dees-color-accent-primary);
|
|
592
|
-
}
|
|
593
|
-
.messageBanner-info dees-icon {
|
|
594
|
-
color: var(--dees-color-accent-primary);
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
/* Message type: success */
|
|
598
|
-
.messageBanner-success {
|
|
599
|
-
background: var(--dees-color-badge-success-bg);
|
|
600
|
-
color: var(--dees-color-badge-success-fg);
|
|
601
|
-
border-left: 3px solid var(--dees-color-accent-success);
|
|
602
|
-
}
|
|
603
|
-
.messageBanner-success dees-icon {
|
|
604
|
-
color: var(--dees-color-accent-success);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
/* Message type: warning */
|
|
608
|
-
.messageBanner-warning {
|
|
609
|
-
background: var(--dees-color-badge-warning-bg);
|
|
610
|
-
color: var(--dees-color-badge-warning-fg);
|
|
611
|
-
border-left: 3px solid var(--dees-color-accent-warning);
|
|
612
|
-
}
|
|
613
|
-
.messageBanner-warning dees-icon {
|
|
614
|
-
color: var(--dees-color-accent-warning);
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
/* Message type: error */
|
|
618
|
-
.messageBanner-error {
|
|
619
|
-
background: var(--dees-color-badge-error-bg);
|
|
620
|
-
color: var(--dees-color-badge-error-fg);
|
|
621
|
-
border-left: 3px solid var(--dees-color-accent-error);
|
|
622
|
-
}
|
|
623
|
-
.messageBanner-error dees-icon {
|
|
624
|
-
color: var(--dees-color-accent-error);
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
.messageBanner-dismiss:hover {
|
|
628
|
-
background: var(--dees-color-pressed);
|
|
629
|
-
}
|
|
34
|
+
@property() accessor name = 'Application Dashboard';
|
|
35
|
+
@property({ type: Array }) accessor viewTabs: IView[] = [];
|
|
36
|
+
@property() accessor terminalSetupCommand = 'echo "Terminal ready"';
|
|
37
|
+
/** Consumer-owned runtime used by the embedded terminal. */
|
|
38
|
+
@property({ type: Object }) accessor executionEnvironment: IExecutionEnvironment | null = null;
|
|
39
|
+
@property({ type: Array }) accessor globalMessages: IGlobalMessage[] = [];
|
|
40
|
+
/** Collapse to an icon rail. Narrow containers use the rail automatically. */
|
|
41
|
+
@property({ type: Boolean }) accessor collapsed = false;
|
|
42
|
+
@state() accessor selectedView!: IView;
|
|
43
|
+
@state() accessor _activeMessages: IGlobalMessage[] = [];
|
|
44
|
+
@state() private accessor compact = false;
|
|
45
|
+
@state() private accessor terminalOpen = false;
|
|
46
|
+
public currentTerminal: DeesWorkspaceTerminal | null = null;
|
|
47
|
+
private currentView?: DeesElement;
|
|
48
|
+
private terminalRequest = 0;
|
|
49
|
+
|
|
50
|
+
constructor() {
|
|
51
|
+
super();
|
|
52
|
+
this.registerStartupFunction(async signal => {
|
|
53
|
+
await this.updateComplete;
|
|
54
|
+
if (signal.aborted) return;
|
|
55
|
+
const observer = new ResizeObserver(entries => { this.compact = entries[0].contentRect.width < 640; });
|
|
56
|
+
observer.observe(this);
|
|
57
|
+
signal.addEventListener('abort', () => observer.disconnect(), { once: true });
|
|
58
|
+
});
|
|
59
|
+
}
|
|
630
60
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
61
|
+
public static styles = [themeDefaultStyles, cssManager.defaultStyles, css`
|
|
62
|
+
:host { display: block; position: relative; width: 100%; height: 100%; overflow: hidden; color: var(--dees-color-text-primary); font: 13px/1.5 var(--dees-font-family); }
|
|
63
|
+
* { box-sizing: border-box; }
|
|
64
|
+
.maincontainer { position: absolute; inset: 0; display: grid; grid-template-columns: 224px minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) 32px; background: var(--dees-color-bg-canvas); }
|
|
65
|
+
.maincontainer.collapsed { grid-template-columns: 64px minmax(0, 1fr); }
|
|
66
|
+
.appbar { display: grid; grid-template-rows: 64px minmax(0, 1fr) auto; min-height: 0; background: var(--dees-color-bg-secondary); border-right: 1px solid var(--dees-color-border-subtle); }
|
|
67
|
+
.sidebar-header { display: flex; align-items: center; gap: 10px; padding: 0 16px; min-width: 0; }
|
|
68
|
+
.appName { font-size: 15px; font-weight: 600; letter-spacing: -.02em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
69
|
+
.sidebar-header > dees-icon { flex-shrink: 0; }
|
|
70
|
+
button { font: inherit; color: inherit; border: 0; background: transparent; cursor: pointer; }
|
|
71
|
+
button:focus-visible { outline: 2px solid var(--dees-color-focus-ring); outline-offset: -2px; }
|
|
72
|
+
.collapseToggle { margin-left: auto; flex-shrink: 0; width: 28px; height: 32px; display: grid; place-items: center; padding: 0; border-radius: var(--dees-radius-md); color: var(--dees-color-text-secondary); }
|
|
73
|
+
.viewTabs-container { overflow: auto; min-height: 0; padding: 12px 8px; scrollbar-width: thin; }
|
|
74
|
+
.section-label { padding: 0 12px 8px; color: var(--dees-color-text-secondary); font-size: 11px; font-weight: 500; }
|
|
75
|
+
.viewTabs { display: grid; gap: 3px; }
|
|
76
|
+
.viewTab, .action { display: flex; width: 100%; min-height: 38px; align-items: center; gap: 10px; padding: 8px 12px; text-align: left; border-radius: var(--dees-radius-lg); color: var(--dees-color-text-secondary); transition: background 140ms var(--dees-ease-standard), color 140ms var(--dees-ease-standard); }
|
|
77
|
+
.viewTab dees-icon, .action dees-icon { flex-shrink: 0; }
|
|
78
|
+
.viewTab span, .action span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
79
|
+
.viewTab .chevron { margin-left: auto; transform: rotate(-90deg); transition: transform 180ms var(--dees-ease-standard); }
|
|
80
|
+
.viewTab.groupActive .chevron, .viewTab.selected .chevron { transform: rotate(0); }
|
|
81
|
+
.viewTab.selected { background: var(--dees-color-nav-selected); color: var(--dees-color-accent-primary); font-weight: 600; }
|
|
82
|
+
.viewTab.groupActive { color: var(--dees-color-text-primary); }
|
|
83
|
+
.subViews { display: grid; grid-template-rows: 0fr; margin: 0 0 0 20px; border-left: 1px solid var(--dees-color-border-subtle); transition: grid-template-rows 180ms var(--dees-ease-standard), margin 180ms var(--dees-ease-standard); }
|
|
84
|
+
.subViews.expanded { grid-template-rows: 1fr; margin-top: 4px; margin-bottom: 8px; }
|
|
85
|
+
.subViews-inner { display: grid; gap: 3px; min-height: 0; overflow: hidden; padding-left: 8px; opacity: 0; transform: translateY(-4px); transition: opacity 140ms var(--dees-ease-standard), transform 180ms var(--dees-ease-standard); }
|
|
86
|
+
.subViews.expanded .subViews-inner { opacity: 1; transform: translateY(0); }
|
|
87
|
+
.viewTab.sub { min-height: 34px; font-size: 12px; }
|
|
88
|
+
.appActions { padding: 8px; border-top: 1px solid var(--dees-color-border-subtle); }
|
|
89
|
+
.workspace { display: grid; grid-template-rows: 64px auto minmax(0, 1fr); min-width: 0; min-height: 0; }
|
|
90
|
+
.workspaceHeader { display: flex; align-items: center; gap: 8px; padding: 0 24px; border-bottom: 1px solid var(--dees-color-border-subtle); min-width: 0; font-weight: 600; }
|
|
91
|
+
.workspaceHeader span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
92
|
+
.workspaceHeader .parent { color: var(--dees-color-text-secondary); font-weight: 400; }
|
|
93
|
+
.contentArea { min-height: 0; min-width: 0; position: relative; display: grid; }
|
|
94
|
+
.appcontent { overflow: auto; min-height: 0; min-width: 0; scrollbar-gutter: stable; overscroll-behavior: contain; }
|
|
95
|
+
.appcontent[inert] { visibility: hidden; }
|
|
96
|
+
.terminalLayer { position: absolute; inset: 0; }
|
|
97
|
+
.terminalLayer[hidden] { display: none; }
|
|
98
|
+
.terminalLayer > dees-workspace-terminal { display: block; position: absolute; inset: 0; width: 100%; height: 100%; }
|
|
99
|
+
.messageBannerArea { max-height: 192px; overflow: auto; }
|
|
100
|
+
.messageBanner { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 12px 24px; border-bottom: 1px solid var(--dees-color-border-subtle); background: var(--dees-color-bg-primary); }
|
|
101
|
+
.messageIcon { display: grid; place-items: center; width: 28px; height: 28px; flex-shrink: 0; border-radius: var(--dees-radius-md); background: var(--message-bg); color: var(--message-fg); }
|
|
102
|
+
.messageBanner-info { --message-bg: var(--dees-color-nav-selected); --message-fg: var(--dees-color-link); }
|
|
103
|
+
.messageBanner-success { --message-bg: var(--dees-color-badge-success-bg); --message-fg: var(--dees-color-badge-success-fg); }
|
|
104
|
+
.messageBanner-warning { --message-bg: var(--dees-color-badge-warning-bg); --message-fg: var(--dees-color-badge-warning-fg); }
|
|
105
|
+
.messageBanner-error { --message-bg: var(--dees-color-badge-error-bg); --message-fg: var(--dees-color-badge-error-fg); }
|
|
106
|
+
.messageBanner-text { flex: 1 1 160px; min-width: 0; overflow-wrap: anywhere; }
|
|
107
|
+
.messageBanner-actions { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
108
|
+
.messageBanner-dismiss { display: grid; place-items: center; width: 28px; height: 28px; padding: 0; border-radius: var(--dees-radius-md); color: var(--dees-color-text-secondary); }
|
|
109
|
+
.controlbar { grid-column: 1 / -1; display: flex; align-items: center; justify-content: flex-end; border-top: 1px solid var(--dees-color-border-subtle); background: var(--dees-color-bg-secondary); font-size: 11px; color: var(--dees-color-text-secondary); }
|
|
110
|
+
.control { display: flex; align-items: center; gap: 6px; padding: 0 12px; height: 100%; }
|
|
111
|
+
.status-terminal { border-left: 1px solid var(--dees-color-border-subtle); }
|
|
112
|
+
.status-terminal[aria-pressed='true'] { color: var(--dees-color-accent-primary); background: var(--dees-color-nav-selected); }
|
|
113
|
+
.collapsed .sidebar-header { justify-content: center; padding: 0; }
|
|
114
|
+
.collapsed .sidebar-header > dees-icon, .collapsed .appName, .collapsed .section-label, .collapsed .viewTab span, .collapsed .viewTab .chevron, .collapsed .action span { display: none; }
|
|
115
|
+
.collapsed .collapseToggle { margin: 0; width: 40px; height: 38px; }
|
|
116
|
+
.collapsed .viewTab, .collapsed .action { justify-content: center; padding: 8px 0; }
|
|
117
|
+
.collapsed .subViews { margin-left: 0; padding: 0; border: 0; }
|
|
118
|
+
.collapsed .subViews-inner { padding-left: 0; }
|
|
119
|
+
.collapsed .sub { color: var(--dees-color-text-secondary); }
|
|
120
|
+
.collapsed .sub.selected { color: var(--dees-color-accent-primary); }
|
|
121
|
+
/* The content column resizes once; only sidebar contents animate during a rail toggle. */
|
|
122
|
+
.maincontainer:not(.collapsed) .viewTab span, .maincontainer:not(.collapsed) .appName, .maincontainer:not(.collapsed) .action span { animation: labelReveal 160ms var(--dees-ease-standard); }
|
|
123
|
+
.collapsed .viewTab > dees-icon:not(.chevron), .collapsed .action > dees-icon { animation: railSettle 160ms var(--dees-ease-standard); }
|
|
124
|
+
@keyframes labelReveal { from { opacity: 0; transform: translateX(-4px); } to { opacity: 1; transform: translateX(0); } }
|
|
125
|
+
@keyframes railSettle { from { transform: translateX(-4px); } to { transform: translateX(0); } }
|
|
126
|
+
@media (hover: hover) { button:hover:not(.selected) { background: var(--dees-color-hover); } .action:hover { color: var(--dees-color-text-error); } }
|
|
127
|
+
@media (pointer: coarse) { .viewTab, .viewTab.sub, .action, .collapseToggle, .messageBanner-dismiss { min-height: 44px; } .maincontainer { grid-template-rows: minmax(0, 1fr) 44px; } }
|
|
128
|
+
@media (prefers-reduced-motion: reduce) { .maincontainer, .viewTab, .action, .chevron, .subViews, .subViews-inner { transition: none; } .maincontainer * { animation: none !important; } }
|
|
129
|
+
`];
|
|
130
|
+
|
|
131
|
+
private get rail(): boolean { return this.collapsed || this.compact; }
|
|
132
|
+
private icon(name: string): string { return name.includes(':') ? name : `lucide:${name}`; }
|
|
133
|
+
private isGroupActive(view: IView): boolean { return view === this.selectedView || !!view.subViews?.includes(this.selectedView); }
|
|
134
|
+
|
|
135
|
+
private renderView(view: IView, sub = false): TemplateResult {
|
|
136
|
+
const selected = this.selectedView === view;
|
|
137
|
+
const expanded = this.isGroupActive(view);
|
|
138
|
+
return html`<button type="button" class="viewTab ${sub ? 'sub' : ''} ${selected ? 'selected' : ''} ${expanded && !selected ? 'groupActive' : ''}"
|
|
139
|
+
aria-label=${view.name} aria-current=${selected ? 'page' : 'false'}
|
|
140
|
+
@keydown=${this.handleNavigationKey} @click=${() => this.loadView(view)}>
|
|
141
|
+
<dees-icon .icon=${this.icon(view.iconName || (sub ? 'dot' : 'file'))} iconSize="17"></dees-icon><span>${view.name}</span>
|
|
142
|
+
${view.subViews?.length ? html`<dees-icon class="chevron" icon="lucide:chevronDown" iconSize="12"></dees-icon>` : ''}
|
|
143
|
+
</button><dees-speechbubble hint .disabled=${!this.rail} .text=${view.name}></dees-speechbubble>`;
|
|
144
|
+
}
|
|
637
145
|
|
|
638
146
|
public render(): TemplateResult {
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
</div>
|
|
648
|
-
<div class="viewTabs-container">
|
|
649
|
-
<div class="section-label">Navigation</div>
|
|
650
|
-
<div class="viewTabs">
|
|
651
|
-
${this.viewTabs.map((view) => {
|
|
652
|
-
const hasSubs = !!view.subViews?.length;
|
|
653
|
-
const groupActive = hasSubs && this.isGroupActive(view);
|
|
654
|
-
return html`
|
|
655
|
-
<div
|
|
656
|
-
class="viewTab ${this.selectedView === view ? 'selected' : ''} ${hasSubs ? 'hasSubs' : ''} ${groupActive ? 'groupActive' : ''}"
|
|
657
|
-
@click=${() => this.loadView(view)}
|
|
658
|
-
>
|
|
659
|
-
${view.iconName ? html`
|
|
660
|
-
<dees-icon .icon="${view.iconName.includes(':') ? view.iconName : `lucide:${view.iconName}`}"></dees-icon>
|
|
661
|
-
` : html`
|
|
662
|
-
<dees-icon .icon="${'lucide:file'}"></dees-icon>
|
|
663
|
-
`}
|
|
664
|
-
<span>${view.name}</span>
|
|
665
|
-
${hasSubs ? html`
|
|
666
|
-
<dees-icon class="chevron" .icon="${'lucide:chevronDown'}"></dees-icon>
|
|
667
|
-
` : ''}
|
|
668
|
-
</div>
|
|
669
|
-
${hasSubs ? html`
|
|
670
|
-
<div
|
|
671
|
-
class="subViews ${groupActive ? 'expanded' : ''}"
|
|
672
|
-
?inert=${!groupActive}
|
|
673
|
-
>
|
|
674
|
-
<div class="subViews-inner">
|
|
675
|
-
${view.subViews!.map(
|
|
676
|
-
(sub) => html`
|
|
677
|
-
<div
|
|
678
|
-
class="viewTab sub ${this.selectedView === sub ? 'selected' : ''}"
|
|
679
|
-
@click=${(e: Event) => {
|
|
680
|
-
e.stopPropagation();
|
|
681
|
-
this.loadView(sub);
|
|
682
|
-
}}
|
|
683
|
-
>
|
|
684
|
-
${sub.iconName ? html`
|
|
685
|
-
<dees-icon .icon="${sub.iconName.includes(':') ? sub.iconName : `lucide:${sub.iconName}`}"></dees-icon>
|
|
686
|
-
` : html`
|
|
687
|
-
<dees-icon .icon="${'lucide:dot'}"></dees-icon>
|
|
688
|
-
`}
|
|
689
|
-
<span>${sub.name}</span>
|
|
690
|
-
</div>
|
|
691
|
-
`
|
|
692
|
-
)}
|
|
693
|
-
</div>
|
|
694
|
-
</div>
|
|
695
|
-
` : ''}
|
|
696
|
-
`;
|
|
697
|
-
})}
|
|
698
|
-
</div>
|
|
699
|
-
</div>
|
|
700
|
-
<div class="appActions">
|
|
701
|
-
<div class="action" @click=${() => {
|
|
702
|
-
this.dispatchEvent(new CustomEvent('logout', { bubbles: true, composed: true }));
|
|
703
|
-
}}>
|
|
704
|
-
<dees-icon .icon="${'lucide:logOut'}"></dees-icon>
|
|
705
|
-
<span>Sign out</span>
|
|
706
|
-
</div>
|
|
707
|
-
</div>
|
|
708
|
-
</div>
|
|
709
|
-
${this._activeMessages.length > 0 ? html`
|
|
710
|
-
<div class="messageBannerArea">
|
|
711
|
-
${this._activeMessages.map(msg => html`
|
|
712
|
-
<div
|
|
713
|
-
class="messageBanner messageBanner-${msg.type}"
|
|
714
|
-
data-message-id="${msg.id}"
|
|
715
|
-
>
|
|
716
|
-
<dees-icon .icon="${this.getMessageIcon(msg)}"></dees-icon>
|
|
717
|
-
<span class="messageBanner-text">${msg.message}</span>
|
|
718
|
-
${msg.actions?.length ? html`
|
|
719
|
-
<div class="messageBanner-actions">
|
|
720
|
-
${msg.actions.map(a => html`
|
|
721
|
-
<div class="messageBanner-action" @click=${() => a.action()}>
|
|
722
|
-
${a.iconName ? html`<dees-icon .icon="${a.iconName.includes(':') ? a.iconName : `lucide:${a.iconName}`}"></dees-icon>` : ''}
|
|
723
|
-
<span>${a.name}</span>
|
|
724
|
-
</div>
|
|
725
|
-
`)}
|
|
726
|
-
</div>
|
|
727
|
-
` : ''}
|
|
728
|
-
${msg.dismissible !== false ? html`
|
|
729
|
-
<div class="messageBanner-dismiss" @click=${() => this.removeMessage(msg.id)}>
|
|
730
|
-
<dees-icon .icon="${'lucide:x'}"></dees-icon>
|
|
731
|
-
</div>
|
|
732
|
-
` : ''}
|
|
733
|
-
</div>
|
|
734
|
-
`)}
|
|
147
|
+
const parent = this.viewTabs.find(view => view.subViews?.includes(this.selectedView));
|
|
148
|
+
return html`<div class="maincontainer ${this.rail ? 'collapsed' : ''}">
|
|
149
|
+
<aside class="appbar" aria-label=${this.name}>
|
|
150
|
+
<div class="sidebar-header"><dees-icon icon="lucide:layers" iconSize="24"></dees-icon><div class="appName">${this.name}</div>
|
|
151
|
+
<button type="button" class="collapseToggle" aria-label=${this.compact ? 'Compact navigation' : this.rail ? 'Expand navigation' : 'Collapse navigation'}
|
|
152
|
+
?disabled=${this.compact} @click=${() => { this.collapsed = !this.collapsed; }}>
|
|
153
|
+
<dees-icon .icon=${this.rail ? 'lucide:panelLeftOpen' : 'lucide:panelLeftClose'} iconSize="17"></dees-icon>
|
|
154
|
+
</button>
|
|
735
155
|
</div>
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
156
|
+
<nav class="viewTabs-container" aria-label="App navigation"><div class="section-label">Workspace</div><div class="viewTabs">
|
|
157
|
+
${this.viewTabs.map(view => html`<div class="viewGroup">${this.renderView(view)}${view.subViews?.length ? html`<div class="subViews ${this.isGroupActive(view) ? 'expanded' : ''}" ?inert=${!this.isGroupActive(view)}>
|
|
158
|
+
<div class="subViews-inner">${view.subViews.map(sub => this.renderView(sub, true))}</div></div>` : ''}</div>`)}
|
|
159
|
+
</div></nav>
|
|
160
|
+
<div class="appActions"><button type="button" class="action" aria-label="Sign out" @click=${() => this.dispatchEvent(new CustomEvent('logout', { bubbles: true, composed: true }))}>
|
|
161
|
+
<dees-icon icon="lucide:logOut" iconSize="17"></dees-icon><span>Sign out</span>
|
|
162
|
+
</button><dees-speechbubble hint .disabled=${!this.rail} text="Sign out"></dees-speechbubble></div>
|
|
163
|
+
</aside>
|
|
164
|
+
<section class="workspace">
|
|
165
|
+
<header class="workspaceHeader">${parent ? html`<span class="parent">${parent.name}</span><dees-icon icon="lucide:chevronRight" iconSize="12"></dees-icon>` : ''}<span>${this.selectedView?.name || this.name}</span></header>
|
|
166
|
+
<div class="messageBannerArea" aria-live="polite">
|
|
167
|
+
${this._activeMessages.map(message => html`<div class="messageBanner messageBanner-${message.type}" data-message-id=${message.id}>
|
|
168
|
+
<span class="messageIcon"><dees-icon .icon=${this.getMessageIcon(message)} iconSize="16"></dees-icon></span>
|
|
169
|
+
<span class="messageBanner-text">${message.message}</span>
|
|
170
|
+
${message.actions?.length ? html`<div class="messageBanner-actions">${message.actions.map(action => html`<dees-button size="sm" type="outline" .text=${action.name} .icon=${action.iconName ? this.icon(action.iconName) : ''} @clicked=${() => action.action()}></dees-button>`)}</div>` : ''}
|
|
171
|
+
${message.dismissible !== false ? html`<button type="button" class="messageBanner-dismiss" aria-label=${`Dismiss: ${message.message}`} @click=${() => this.removeMessage(message.id)}><dees-icon icon="lucide:x" iconSize="14"></dees-icon></button>` : ''}
|
|
172
|
+
</div>`)}
|
|
739
173
|
</div>
|
|
740
|
-
<div class="
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
<dees-icon .icon="${'lucide:terminal'}"></dees-icon>
|
|
747
|
-
<span>Terminal</span>
|
|
748
|
-
</div>
|
|
749
|
-
</div>
|
|
750
|
-
</div>
|
|
751
|
-
`;
|
|
174
|
+
<div class="contentArea"><div class="appcontent" ?inert=${this.terminalOpen}></div><div class="terminalLayer" ?hidden=${!this.terminalOpen}></div></div>
|
|
175
|
+
</section>
|
|
176
|
+
<footer class="controlbar"><span class="control status-connected"><dees-icon icon="lucide:wifi" iconSize="12"></dees-icon>Connected</span>
|
|
177
|
+
<button type="button" class="control status-terminal" aria-pressed=${String(this.terminalOpen)} @click=${this.launchTerminal}><dees-icon icon="lucide:terminal" iconSize="13"></dees-icon>Terminal</button>
|
|
178
|
+
</footer>
|
|
179
|
+
</div>`;
|
|
752
180
|
}
|
|
753
181
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
182
|
+
private handleNavigationKey(event: KeyboardEvent): void {
|
|
183
|
+
if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) return;
|
|
184
|
+
event.preventDefault();
|
|
185
|
+
const buttons = [...this.shadowRoot!.querySelectorAll<HTMLButtonElement>('.viewTab')].filter(button => !button.closest('[inert]'));
|
|
186
|
+
const index = buttons.indexOf(event.currentTarget as HTMLButtonElement);
|
|
187
|
+
const next = event.key === 'Home' ? 0 : event.key === 'End' ? buttons.length - 1 : (index + (event.key === 'ArrowDown' ? 1 : -1) + buttons.length) % buttons.length;
|
|
188
|
+
buttons[next]?.focus();
|
|
761
189
|
}
|
|
762
190
|
|
|
763
|
-
public willUpdate(
|
|
764
|
-
if (
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
const existingIds = new Set(this._activeMessages.map(m => m.id));
|
|
769
|
-
|
|
770
|
-
// Add new messages from property that aren't already active
|
|
771
|
-
const newMessages = this.globalMessages.filter(m => !existingIds.has(m.id));
|
|
772
|
-
|
|
773
|
-
// Keep messages added via API (those not in globalMessages are kept as-is)
|
|
774
|
-
// Remove messages that were in the previous globalMessages but are no longer
|
|
775
|
-
const previousGlobalMessages = (changedProperties.get('globalMessages') as IGlobalMessage[]) || [];
|
|
776
|
-
const previousIds = new Set(previousGlobalMessages.map(m => m.id));
|
|
777
|
-
const removedIds = new Set([...previousIds].filter(id => !propertyIds.has(id)));
|
|
778
|
-
|
|
779
|
-
this._activeMessages = [
|
|
780
|
-
...this._activeMessages.filter(m => !removedIds.has(m.id)),
|
|
781
|
-
...newMessages,
|
|
782
|
-
];
|
|
191
|
+
public willUpdate(changes: Map<string, unknown>): void {
|
|
192
|
+
if (changes.has('globalMessages')) {
|
|
193
|
+
const previousIds = new Set(((changes.get('globalMessages') as IGlobalMessage[]) || []).map(message => message.id));
|
|
194
|
+
const incoming = new Map(this.globalMessages.map(message => [message.id, message]));
|
|
195
|
+
this._activeMessages = [...this._activeMessages.filter(message => !previousIds.has(message.id) && !incoming.has(message.id)), ...incoming.values()];
|
|
783
196
|
}
|
|
784
197
|
}
|
|
785
198
|
|
|
786
|
-
public updated(
|
|
787
|
-
super.updated(
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
});
|
|
199
|
+
public updated(changes: Map<string, unknown>): void {
|
|
200
|
+
super.updated(changes);
|
|
201
|
+
if (changes.has('viewTabs')) {
|
|
202
|
+
const views = this.viewTabs.flatMap(view => [view, ...(view.subViews || [])]);
|
|
203
|
+
if (!views.includes(this.selectedView)) {
|
|
204
|
+
this.currentView?.remove();
|
|
205
|
+
this.currentView = undefined;
|
|
206
|
+
this.selectedView = undefined!;
|
|
207
|
+
}
|
|
208
|
+
if (!this.currentView && this.viewTabs.length) void this.loadView(this.selectedView || this.viewTabs[0]);
|
|
209
|
+
}
|
|
798
210
|
}
|
|
799
211
|
|
|
800
|
-
private getMessageIcon(
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
info: 'lucide:info',
|
|
804
|
-
success: 'lucide:circleCheck',
|
|
805
|
-
warning: 'lucide:triangleAlert',
|
|
806
|
-
error: 'lucide:circleX',
|
|
807
|
-
};
|
|
808
|
-
return defaults[msg.type];
|
|
212
|
+
private getMessageIcon(message: IGlobalMessage): string {
|
|
213
|
+
const icons: Record<TGlobalMessageType, string> = { info: 'info', success: 'circleCheck', warning: 'triangleAlert', error: 'circleX' };
|
|
214
|
+
return this.icon(message.icon || icons[message.type]);
|
|
809
215
|
}
|
|
810
|
-
|
|
811
216
|
public addMessage(message: Omit<IGlobalMessage, 'id'> & { id?: string }): string {
|
|
812
|
-
const id = message.id || `msg-${Date.now()}-${Math.random().toString(36).
|
|
813
|
-
const
|
|
814
|
-
|
|
815
|
-
...
|
|
816
|
-
id,
|
|
817
|
-
};
|
|
818
|
-
this._activeMessages = [...this._activeMessages, fullMessage];
|
|
217
|
+
const id = message.id || `msg-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
218
|
+
const next = { dismissible: true, ...message, id };
|
|
219
|
+
this._activeMessages = this._activeMessages.some(entry => entry.id === id)
|
|
220
|
+
? this._activeMessages.map(entry => entry.id === id ? next : entry) : [...this._activeMessages, next];
|
|
819
221
|
return id;
|
|
820
222
|
}
|
|
821
|
-
|
|
822
223
|
public removeMessage(id: string): void {
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
bannerEl.addEventListener('animationend', () => {
|
|
827
|
-
this._activeMessages = this._activeMessages.filter(m => m.id !== id);
|
|
828
|
-
this.dispatchEvent(new CustomEvent('message-dismiss', {
|
|
829
|
-
detail: { id },
|
|
830
|
-
bubbles: true,
|
|
831
|
-
composed: true,
|
|
832
|
-
}));
|
|
833
|
-
}, { once: true });
|
|
834
|
-
} else {
|
|
835
|
-
this._activeMessages = this._activeMessages.filter(m => m.id !== id);
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
public clearMessages(): void {
|
|
840
|
-
this._activeMessages = [];
|
|
224
|
+
if (!this._activeMessages.some(message => message.id === id)) return;
|
|
225
|
+
this._activeMessages = this._activeMessages.filter(message => message.id !== id);
|
|
226
|
+
this.dispatchEvent(new CustomEvent('message-dismiss', { detail: { id }, bubbles: true, composed: true }));
|
|
841
227
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
public async launchTerminal() {
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
terminal.style.right = '0px';
|
|
863
|
-
terminal.style.height = 'auto';
|
|
864
|
-
terminal.style.bottom = '24px';
|
|
865
|
-
terminal.style.opacity = '0';
|
|
866
|
-
terminal.style.transform = 'translateY(8px) scale(0.99)';
|
|
867
|
-
terminal.style.transition = 'all 0.25s var(--dees-ease-standard)';
|
|
868
|
-
terminal.style.boxShadow = 'var(--dees-shadow-xl), 0 0 0 1px var(--dees-color-border-subtle)';
|
|
869
|
-
|
|
870
|
-
// Add close button to terminal
|
|
871
|
-
terminal.addEventListener('close', () => this.closeTerminal());
|
|
872
|
-
|
|
873
|
-
await domtools.convenience.smartdelay.delayFor(0);
|
|
874
|
-
terminal.style.opacity = '1';
|
|
875
|
-
terminal.style.transform = 'translateY(0) scale(1)';
|
|
876
|
-
return terminal;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
private async closeTerminal() {
|
|
880
|
-
const domtools = await this.domtoolsPromise;
|
|
881
|
-
if (this.currentTerminal) {
|
|
882
|
-
this.currentTerminal.style.opacity = '0';
|
|
883
|
-
this.currentTerminal.style.transform = 'translateY(8px) scale(0.99)';
|
|
884
|
-
await domtools.convenience.smartdelay.delayFor(250);
|
|
885
|
-
this.currentTerminal.remove();
|
|
886
|
-
this.currentTerminal = null;
|
|
228
|
+
public clearMessages(): void { this._activeMessages = []; }
|
|
229
|
+
|
|
230
|
+
public async launchTerminal(): Promise<DeesWorkspaceTerminal | undefined> {
|
|
231
|
+
if (this.terminalOpen) { this.closeTerminal(); return; }
|
|
232
|
+
this.terminalOpen = true;
|
|
233
|
+
const request = ++this.terminalRequest;
|
|
234
|
+
try {
|
|
235
|
+
const { DeesWorkspaceTerminal } = await import('../../00group-workspace/dees-workspace-terminal/dees-workspace-terminal.js');
|
|
236
|
+
await this.updateComplete;
|
|
237
|
+
if (!this.isConnected || request !== this.terminalRequest) return;
|
|
238
|
+
const terminal = new DeesWorkspaceTerminal();
|
|
239
|
+
terminal.executionEnvironment = this.executionEnvironment;
|
|
240
|
+
terminal.setupCommand = this.terminalSetupCommand;
|
|
241
|
+
this.currentTerminal = terminal;
|
|
242
|
+
terminal.addEventListener('close', () => { if (terminal === this.currentTerminal) this.closeTerminal(); });
|
|
243
|
+
this.shadowRoot!.querySelector('.terminalLayer')!.appendChild(terminal);
|
|
244
|
+
return terminal;
|
|
245
|
+
} catch (error) {
|
|
246
|
+
if (request === this.terminalRequest) this.closeTerminal();
|
|
247
|
+
throw error;
|
|
887
248
|
}
|
|
888
249
|
}
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
250
|
+
private closeTerminal(): void {
|
|
251
|
+
++this.terminalRequest;
|
|
252
|
+
this.currentTerminal?.remove();
|
|
253
|
+
this.currentTerminal = null;
|
|
254
|
+
this.terminalOpen = false;
|
|
894
255
|
}
|
|
256
|
+
public async disconnectedCallback(): Promise<void> { this.closeTerminal(); await super.disconnectedCallback(); }
|
|
895
257
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
if (firstNavigable) {
|
|
902
|
-
return this.loadView(firstNavigable);
|
|
903
|
-
}
|
|
904
|
-
return; // nothing navigable — ignore click
|
|
905
|
-
}
|
|
906
|
-
if (!viewArg.element) return; // safety: no element and no subs → no-op
|
|
907
|
-
|
|
908
|
-
const appcontent = this.shadowRoot!.querySelector('.appcontent')!;
|
|
909
|
-
const view = new viewArg.element();
|
|
910
|
-
if (this.currentView) {
|
|
911
|
-
this.currentView.remove();
|
|
258
|
+
public async loadView(view: IView): Promise<void> {
|
|
259
|
+
if (!view.element && view.subViews?.length) {
|
|
260
|
+
const child = view.subViews.find(candidate => candidate.element);
|
|
261
|
+
if (child) await this.loadView(child);
|
|
262
|
+
return;
|
|
912
263
|
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
this.
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
264
|
+
if (!view.element || (view === this.selectedView && this.currentView)) return;
|
|
265
|
+
const content = this.shadowRoot?.querySelector('.appcontent');
|
|
266
|
+
if (!content) return;
|
|
267
|
+
const element = new view.element();
|
|
268
|
+
this.closeTerminal();
|
|
269
|
+
this.currentView?.remove();
|
|
270
|
+
content.appendChild(element);
|
|
271
|
+
content.scrollTop = 0;
|
|
272
|
+
this.currentView = element;
|
|
273
|
+
this.selectedView = view;
|
|
274
|
+
this.dispatchEvent(new CustomEvent('view-select', { detail: { view }, bubbles: true, composed: true }));
|
|
923
275
|
}
|
|
924
276
|
}
|