@signature.digital/catalog 1.3.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.
Files changed (71) hide show
  1. package/.smartconfig.json +82 -0
  2. package/dist_bundle/bundle.js +6077 -0
  3. package/dist_bundle/bundle.js.map +7 -0
  4. package/dist_bundle/index.html +28 -0
  5. package/dist_ts_web/00_commitinfo_data.d.ts +8 -0
  6. package/dist_ts_web/00_commitinfo_data.js +9 -0
  7. package/dist_ts_web/elements/index.d.ts +3 -0
  8. package/dist_ts_web/elements/index.js +6 -0
  9. package/dist_ts_web/elements/sdig-signbox/index.d.ts +1 -0
  10. package/dist_ts_web/elements/sdig-signbox/index.js +2 -0
  11. package/dist_ts_web/elements/sdig-signbox/sdig-signbox.d.ts +13 -0
  12. package/dist_ts_web/elements/sdig-signbox/sdig-signbox.js +153 -0
  13. package/dist_ts_web/elements/sdig-signpad/index.d.ts +1 -0
  14. package/dist_ts_web/elements/sdig-signpad/index.js +2 -0
  15. package/dist_ts_web/elements/sdig-signpad/sdig-signpad.d.ts +22 -0
  16. package/dist_ts_web/elements/sdig-signpad/sdig-signpad.js +149 -0
  17. package/dist_ts_web/elements/sdig-workspace/index.d.ts +8 -0
  18. package/dist_ts_web/elements/sdig-workspace/index.js +9 -0
  19. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-audit.d.ts +12 -0
  20. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-audit.js +81 -0
  21. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-compose.d.ts +44 -0
  22. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-compose.js +418 -0
  23. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-developers.d.ts +12 -0
  24. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-developers.js +91 -0
  25. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-inbox.d.ts +19 -0
  26. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-inbox.js +181 -0
  27. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-placeholder.d.ts +14 -0
  28. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-placeholder.js +84 -0
  29. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-sign.d.ts +18 -0
  30. package/dist_ts_web/elements/sdig-workspace/sdig-workspace-sign.js +152 -0
  31. package/dist_ts_web/elements/sdig-workspace/sdig-workspace.d.ts +31 -0
  32. package/dist_ts_web/elements/sdig-workspace/sdig-workspace.js +245 -0
  33. package/dist_ts_web/elements/sdig-workspace/sdig-workspace.shared.d.ts +53 -0
  34. package/dist_ts_web/elements/sdig-workspace/sdig-workspace.shared.js +385 -0
  35. package/dist_ts_web/index.d.ts +1 -0
  36. package/dist_ts_web/index.js +2 -0
  37. package/dist_ts_web/pages/index.d.ts +2 -0
  38. package/dist_ts_web/pages/index.js +3 -0
  39. package/dist_ts_web/pages/page1.d.ts +1 -0
  40. package/dist_ts_web/pages/page1.js +5 -0
  41. package/dist_ts_web/pages/page2.d.ts +1 -0
  42. package/dist_ts_web/pages/page2.js +5 -0
  43. package/dist_ts_web/plugins.d.ts +3 -0
  44. package/dist_ts_web/plugins.js +5 -0
  45. package/dist_watch/bundle.js +135319 -0
  46. package/dist_watch/bundle.js.map +7 -0
  47. package/dist_watch/index.html +28 -0
  48. package/license +19 -0
  49. package/package.json +63 -0
  50. package/readme.hints.md +31 -0
  51. package/readme.md +172 -0
  52. package/ts_web/00_commitinfo_data.ts +8 -0
  53. package/ts_web/elements/index.ts +6 -0
  54. package/ts_web/elements/sdig-signbox/index.ts +1 -0
  55. package/ts_web/elements/sdig-signbox/sdig-signbox.ts +109 -0
  56. package/ts_web/elements/sdig-signpad/index.ts +1 -0
  57. package/ts_web/elements/sdig-signpad/sdig-signpad.ts +115 -0
  58. package/ts_web/elements/sdig-workspace/index.ts +8 -0
  59. package/ts_web/elements/sdig-workspace/sdig-workspace-audit.ts +37 -0
  60. package/ts_web/elements/sdig-workspace/sdig-workspace-compose.ts +369 -0
  61. package/ts_web/elements/sdig-workspace/sdig-workspace-developers.ts +47 -0
  62. package/ts_web/elements/sdig-workspace/sdig-workspace-inbox.ts +117 -0
  63. package/ts_web/elements/sdig-workspace/sdig-workspace-placeholder.ts +22 -0
  64. package/ts_web/elements/sdig-workspace/sdig-workspace-sign.ts +91 -0
  65. package/ts_web/elements/sdig-workspace/sdig-workspace.shared.ts +440 -0
  66. package/ts_web/elements/sdig-workspace/sdig-workspace.ts +175 -0
  67. package/ts_web/index.ts +1 -0
  68. package/ts_web/pages/index.ts +2 -0
  69. package/ts_web/pages/page1.ts +5 -0
  70. package/ts_web/pages/page2.ts +5 -0
  71. package/ts_web/plugins.ts +8 -0
@@ -0,0 +1,91 @@
1
+ import { DeesElement, state, html, customElement, type TemplateResult, css } from '@design.estate/dees-element';
2
+ import { actionButton, demoFields, fakeDocument, icon, pill, workspaceBaseStyles, workspaceDemoFrame, type IFieldPlacement } from './sdig-workspace.shared.js';
3
+
4
+ declare global {
5
+ interface HTMLElementTagNameMap {
6
+ 'sdig-workspace-sign': SdigWorkspaceSign;
7
+ }
8
+ }
9
+
10
+ @customElement('sdig-workspace-sign')
11
+ export class SdigWorkspaceSign extends DeesElement {
12
+ public static demo = () => workspaceDemoFrame(html`<sdig-workspace-sign></sdig-workspace-sign>`);
13
+ public static demoGroups = ['Signature Digital Workspace'];
14
+
15
+ @state() private accessor activeFieldId: string = 'f1';
16
+ @state() private accessor signedFieldIds: string[] = [];
17
+
18
+ public static styles = [workspaceBaseStyles, css`
19
+ .recipient-header { height: 56px; flex-shrink: 0; padding: 0 24px; background: var(--bg-card); border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }
20
+ .logomark { width: 28px; height: 28px; border-radius: 6px; background: var(--bg-el); border: 1px solid var(--border-strong); display: inline-flex; align-items: center; justify-content: center; font-family: 'Plus Jakarta Sans', Inter, sans-serif; font-weight: 700; position: relative; }
21
+ .logomark::after { content: ''; position: absolute; right: 5px; bottom: 5px; width: 4px; height: 4px; border-radius: 50%; background: var(--accent); }
22
+ .sign-layout { flex: 1; display: flex; overflow: hidden; background: hsl(0 0% 96%); color: hsl(0 0% 10%); }
23
+ :host-context(sdig-workspace[theme='dark']) .sign-layout { background: hsl(0 0% 6%); color: hsl(0 0% 95%); }
24
+ .sign-body { flex: 1; overflow: auto; padding: 32px 32px 80px; display: flex; flex-direction: column; align-items: center; gap: 20px; }
25
+ .sign-panel { width: 320px; border-left: 1px solid var(--border); background: var(--bg-card); padding: 20px; overflow: auto; flex-shrink: 0; }
26
+ @media (max-width: 920px) { .recipient-header .actions { display: none; } .sign-layout { flex-direction: column; overflow: auto; } .sign-panel { width: 100%; border-left: 0; border-top: 1px solid var(--border); } .document-page { width: 560px; } }
27
+ `];
28
+
29
+ private get signFields() {
30
+ return demoFields.slice(0, 3);
31
+ }
32
+
33
+ private fieldIcon(type: IFieldPlacement['type']): string {
34
+ if (type === 'signature') return 'sign';
35
+ if (type === 'date') return 'calendar';
36
+ return 'type';
37
+ }
38
+
39
+ private signField(fieldId: string) {
40
+ if (!this.signedFieldIds.includes(fieldId)) {
41
+ this.signedFieldIds = [...this.signedFieldIds, fieldId];
42
+ }
43
+ const next = this.signFields.find((field) => !this.signedFieldIds.includes(field.id) && field.id !== fieldId);
44
+ if (next) this.activeFieldId = next.id;
45
+ }
46
+
47
+ private renderSignedValue(field: IFieldPlacement): TemplateResult {
48
+ if (field.type === 'signature') return html`<span style="font-family: 'Plus Jakarta Sans', cursive; font-size: 22px; font-weight: 600; font-style: italic; color: hsl(220 50% 30%);">Sarah Chen</span>`;
49
+ if (field.type === 'date') return html`<span class="mono" style="font-size: 12px; color: hsl(0 0% 18%);">2026-05-02</span>`;
50
+ return html`<span style="font-size: 12px; color: hsl(0 0% 18%);">Sarah Chen</span>`;
51
+ }
52
+
53
+ public render(): TemplateResult {
54
+ const completed = this.signedFieldIds.length;
55
+ const progress = Math.round((completed / this.signFields.length) * 100);
56
+ const activeField = this.signFields.find((field) => field.id === this.activeFieldId) || this.signFields[0];
57
+
58
+ return html`
59
+ <div class="recipient-header">
60
+ <div style="display: flex; align-items: center; gap: 12px;"><span class="logomark">s</span><div><div style="font-size: 12px; font-weight: 600;">Master Services Agreement</div><div class="mono" style="font-size: 10px; color: var(--text-muted);">From Lossless GmbH · doc_8mK3pL · 14 pages</div></div></div>
61
+ <div class="actions"><span class="pill success">${icon('shield', 12)} Verified sender · DKIM ✓</span>${actionButton('Decline', 'outline')}${actionButton('PDF', 'outline', 'download')}</div>
62
+ </div>
63
+ <div class="progress-track"><div class="progress-fill" style="width: ${progress}%"></div></div>
64
+ <div class="sign-layout">
65
+ <div class="sign-body">
66
+ <div class="document-page" style="width: 620px; min-height: 820px;">
67
+ ${fakeDocument()}
68
+ ${this.signFields.map((field) => {
69
+ const filled = this.signedFieldIds.includes(field.id);
70
+ const active = this.activeFieldId === field.id && !filled;
71
+ return html`<div class="field-box ${active ? 'selected' : ''}" style="--x: ${field.x}px; --y: ${field.y}px; --w: ${field.w}px; --h: ${field.h}px; --field-color: ${filled ? 'transparent' : 'var(--accent)'}; color: ${filled ? 'hsl(220 50% 30%)' : 'var(--accent)'}; background: ${filled ? 'transparent' : 'color-mix(in srgb, var(--accent) 12%, transparent)'};" @click=${() => !filled ? this.signField(field.id) : undefined}>${filled ? this.renderSignedValue(field) : html`${icon(this.fieldIcon(field.type), 12)}<span>${active ? html`<span style="display: inline-block; width: 5px; height: 5px; border-radius: 50%; background: var(--accent); animation: pulse 1.4s infinite;"></span>` : ''}${field.label}</span>`}</div>`;
72
+ })}
73
+ <div class="mono" style="position: absolute; bottom: 14px; right: 18px; font-size: 9px; color: hsl(0 0% 60%);">Page 1 of 14</div>
74
+ </div>
75
+ </div>
76
+ <div class="sign-panel">
77
+ <div style="display: flex; align-items: center; gap: 8px;"><span class="avatar" style="background: #60a5fa;">SC</span><div><div style="font-size: 13px; font-weight: 600;">Hi, Sarah</div><div class="mono" style="font-size: 10px; color: var(--text-muted);">sarah@acme.com</div></div></div>
78
+ <div class="card" style="padding: 14px; margin-top: 16px;"><div class="label-upper">Your progress</div><div style="font-size: 24px; font-weight: 700;">${completed} <span style="color: var(--text-muted); font-weight: 400;">/ ${this.signFields.length}</span></div><div style="font-size: 11px; color: var(--text-muted); margin-top: 2px;">${this.signFields.length - completed === 0 ? 'All fields complete' : `${this.signFields.length - completed} fields remaining`}</div><div class="progress-track" style="margin-top: 12px;"><div class="progress-fill" style="width: ${progress}%"></div></div></div>
79
+ <div style="margin-top: 20px;" class="label-upper">Step by step</div>
80
+ ${this.signFields.map((field, index) => {
81
+ const filled = this.signedFieldIds.includes(field.id);
82
+ const active = this.activeFieldId === field.id && !filled;
83
+ return html`<div class="recipient-line ${active ? 'active' : ''}" @click=${() => !filled ? this.activeFieldId = field.id : undefined}><span class="avatar" style="width: 22px; height: 22px; background: ${filled ? 'var(--success)' : active ? 'var(--accent)' : 'var(--bg-input)'}; color: ${filled || active ? 'white' : 'var(--text-muted)'};">${filled ? '✓' : index + 1}</span><div style="flex: 1;"><div style="font-size: 12px; font-weight: 500; text-decoration: ${filled ? 'line-through' : 'none'};">${field.label}</div><div class="mono" style="font-size: 10px; color: var(--text-muted);">${field.type} · page ${field.page}</div></div>${active ? icon('chevronRight', 12) : ''}</div>`;
84
+ })}
85
+ <button class="btn primary" style="width: 100%; height: 44px; margin-top: 20px;" @click=${() => this.signField(activeField.id)}>${completed === this.signFields.length ? 'Finish & submit' : `Continue - ${activeField.label}`}</button>
86
+ <div style="margin-top: 12px; padding: 10px; font-size: 10px; color: var(--text-muted); line-height: 1.5; text-align: center; border-radius: 6px; background: var(--bg-el);">By signing, you agree to the ESIGN Act & eIDAS terms.<br /><span class="mono">IP 81.221.4.18 · Brussels, BE</span></div>
87
+ </div>
88
+ </div>
89
+ `;
90
+ }
91
+ }
@@ -0,0 +1,440 @@
1
+ import { html, css, type TemplateResult } from '@design.estate/dees-element';
2
+ import '@design.estate/dees-catalog/ts_web/elements/00group-utility/dees-icon/dees-icon.js';
3
+
4
+ export type TWorkspaceView =
5
+ | 'inbox'
6
+ | 'compose'
7
+ | 'sign'
8
+ | 'audit'
9
+ | 'developers'
10
+ | 'templates'
11
+ | 'team'
12
+ | 'settings';
13
+
14
+ export type TWorkspaceTheme = 'dark' | 'light';
15
+ export type TDensity = 'compact' | 'comfortable';
16
+
17
+ export interface IDocumentRow {
18
+ id: string;
19
+ title: string;
20
+ status: 'awaiting' | 'signed' | 'draft' | 'declined';
21
+ recipients: Array<{ name: string; initials: string; signed: boolean }>;
22
+ updated: string;
23
+ sender: string;
24
+ pages: number;
25
+ deadline?: string;
26
+ }
27
+
28
+ export interface IRecipient {
29
+ id: number;
30
+ name: string;
31
+ email: string;
32
+ color: string;
33
+ order: number;
34
+ }
35
+
36
+ export interface IFieldPlacement {
37
+ id: string;
38
+ type: 'signature' | 'date' | 'text' | 'initials' | 'check';
39
+ x: number;
40
+ y: number;
41
+ w: number;
42
+ h: number;
43
+ page: number;
44
+ recipient: number;
45
+ label: string;
46
+ }
47
+
48
+ export const demoDocuments: IDocumentRow[] = [
49
+ { id: 'doc_8mK3pL', title: 'Master Services Agreement - Acme Corp', status: 'awaiting', recipients: [{ name: 'Sarah Chen', initials: 'SC', signed: true }, { name: 'David Park', initials: 'DP', signed: false }, { name: 'You', initials: 'PK', signed: true }], updated: '2 min ago', sender: 'You', pages: 14, deadline: 'May 5' },
50
+ { id: 'doc_2nQ7vR', title: 'NDA - Helio Robotics', status: 'signed', recipients: [{ name: 'Marcus Tan', initials: 'MT', signed: true }, { name: 'You', initials: 'PK', signed: true }], updated: '1h ago', sender: 'You', pages: 3 },
51
+ { id: 'doc_5tH1zM', title: 'Series B Term Sheet (Lead) v3', status: 'awaiting', recipients: [{ name: 'Anna Lindqvist', initials: 'AL', signed: false }, { name: 'Roy Banerjee', initials: 'RB', signed: true }, { name: 'You', initials: 'PK', signed: false }], updated: '3h ago', sender: 'Sequoia Counsel', pages: 22, deadline: 'May 3' },
52
+ { id: 'doc_9wB4cX', title: 'Employment Offer - Mira Abebe', status: 'declined', recipients: [{ name: 'Mira Abebe', initials: 'MA', signed: false }, { name: 'You', initials: 'PK', signed: true }], updated: 'yesterday', sender: 'You', pages: 6 },
53
+ { id: 'doc_1jF6kY', title: 'Lease - Berlin office Q3', status: 'draft', recipients: [{ name: 'You', initials: 'PK', signed: false }], updated: 'yesterday', sender: 'You', pages: 11 },
54
+ { id: 'doc_4dN8sP', title: 'API Reseller Agreement - Northwind', status: 'signed', recipients: [{ name: 'Lila Brooks', initials: 'LB', signed: true }, { name: 'You', initials: 'PK', signed: true }], updated: '2 days ago', sender: 'You', pages: 8 },
55
+ ];
56
+
57
+ export const demoRecipients: IRecipient[] = [
58
+ { id: 0, name: 'Sarah Chen', email: 'sarah@acme.com', color: '#60a5fa', order: 1 },
59
+ { id: 1, name: 'David Park', email: 'd.park@acme.com', color: '#fbbf24', order: 2 },
60
+ { id: 2, name: 'Philipp K.', email: 'philipp@lossless.com', color: '#3b82f6', order: 3 },
61
+ ];
62
+
63
+ export const demoFields: IFieldPlacement[] = [
64
+ { id: 'f1', type: 'signature', x: 60, y: 580, w: 200, h: 50, page: 1, recipient: 0, label: 'Signature' },
65
+ { id: 'f2', type: 'date', x: 320, y: 580, w: 120, h: 30, page: 1, recipient: 0, label: 'Date' },
66
+ { id: 'f3', type: 'text', x: 60, y: 460, w: 280, h: 30, page: 1, recipient: 1, label: 'Full legal name' },
67
+ { id: 'f4', type: 'signature', x: 60, y: 700, w: 200, h: 50, page: 1, recipient: 1, label: 'Counter-signature' },
68
+ ];
69
+
70
+ export const workspaceBaseStyles = css`
71
+ :host {
72
+ display: flex;
73
+ flex-direction: column;
74
+ height: 100%;
75
+ min-height: 0;
76
+ color: var(--text);
77
+ background: var(--bg);
78
+ font-family: Geist, Inter, Roboto, -apple-system, BlinkMacSystemFont, sans-serif;
79
+ font-feature-settings: 'cv11', 'tnum', 'cv05' 1;
80
+ }
81
+
82
+ * { box-sizing: border-box; }
83
+ button, input, textarea { font: inherit; }
84
+ button { border: 0; cursor: pointer; }
85
+ dees-icon { flex-shrink: 0; }
86
+
87
+ .mono {
88
+ font-family: 'Intel One Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
89
+ font-variant-numeric: tabular-nums;
90
+ }
91
+
92
+ .topbar {
93
+ height: 56px;
94
+ flex-shrink: 0;
95
+ padding: 0 24px;
96
+ display: flex;
97
+ align-items: center;
98
+ justify-content: space-between;
99
+ border-bottom: 1px solid var(--border-subtle);
100
+ background: var(--bg);
101
+ gap: 12px;
102
+ }
103
+
104
+ .breadcrumb {
105
+ font-size: 11px;
106
+ color: var(--text-muted);
107
+ display: flex;
108
+ align-items: center;
109
+ gap: 5px;
110
+ white-space: nowrap;
111
+ overflow: hidden;
112
+ text-overflow: ellipsis;
113
+ }
114
+
115
+ .top-title {
116
+ display: flex;
117
+ align-items: center;
118
+ gap: 10px;
119
+ min-width: 0;
120
+ }
121
+
122
+ .top-title > span:first-child {
123
+ font-family: 'Plus Jakarta Sans', Inter, sans-serif;
124
+ font-size: 18px;
125
+ font-weight: 600;
126
+ letter-spacing: -0.02em;
127
+ white-space: nowrap;
128
+ overflow: hidden;
129
+ text-overflow: ellipsis;
130
+ }
131
+
132
+ .actions {
133
+ display: flex;
134
+ align-items: center;
135
+ gap: 8px;
136
+ flex-shrink: 0;
137
+ }
138
+
139
+ .btn {
140
+ height: 34px;
141
+ padding: 0 14px;
142
+ border-radius: 6px;
143
+ font-size: 13px;
144
+ font-weight: 500;
145
+ letter-spacing: -0.01em;
146
+ display: inline-flex;
147
+ align-items: center;
148
+ justify-content: center;
149
+ gap: 6px;
150
+ white-space: nowrap;
151
+ transition: all 0.12s ease;
152
+ }
153
+
154
+ .btn.small {
155
+ height: 28px;
156
+ padding: 0 10px;
157
+ font-size: 12px;
158
+ }
159
+
160
+ .btn.primary {
161
+ background: var(--accent);
162
+ color: white;
163
+ border: 1px solid var(--accent);
164
+ }
165
+
166
+ .btn.outline {
167
+ background: transparent;
168
+ color: var(--text);
169
+ border: 1px solid var(--border);
170
+ }
171
+
172
+ .btn.ghost {
173
+ background: transparent;
174
+ color: var(--text);
175
+ border: 1px solid transparent;
176
+ }
177
+
178
+ .btn:hover { background-color: var(--hover); }
179
+
180
+ .pill {
181
+ display: inline-flex;
182
+ align-items: center;
183
+ gap: 5px;
184
+ padding: 2px 8px;
185
+ border-radius: 999px;
186
+ background: var(--bg-el);
187
+ color: var(--text-sec);
188
+ font-size: 11px;
189
+ font-weight: 500;
190
+ white-space: nowrap;
191
+ }
192
+
193
+ .pill::before {
194
+ content: '';
195
+ width: 5px;
196
+ height: 5px;
197
+ display: none;
198
+ border-radius: 50%;
199
+ background: currentColor;
200
+ }
201
+
202
+ .pill.dot::before { display: block; }
203
+ .pill.success { background: rgba(34,197,94,0.12); color: #4ade80; }
204
+ .pill.warning { background: rgba(245,158,11,0.12); color: #fbbf24; }
205
+ .pill.error { background: rgba(239,68,68,0.12); color: #f87171; }
206
+ .pill.info { background: rgba(59,130,246,0.12); color: #60a5fa; }
207
+
208
+ .content-scroll {
209
+ flex: 1;
210
+ overflow: auto;
211
+ padding: 24px;
212
+ }
213
+
214
+ .card {
215
+ background: var(--bg-card);
216
+ border: 1px solid var(--border);
217
+ border-radius: 8px;
218
+ overflow: hidden;
219
+ }
220
+
221
+ .label-upper {
222
+ font-size: 10px;
223
+ font-weight: 600;
224
+ color: var(--text-dim);
225
+ text-transform: uppercase;
226
+ letter-spacing: 0.08em;
227
+ margin-bottom: 10px;
228
+ }
229
+
230
+ .avatar {
231
+ width: 26px;
232
+ height: 26px;
233
+ border-radius: 50%;
234
+ background: var(--accent);
235
+ color: white;
236
+ font-size: 11px;
237
+ font-weight: 700;
238
+ display: inline-flex;
239
+ align-items: center;
240
+ justify-content: center;
241
+ flex-shrink: 0;
242
+ }
243
+
244
+ .document-page {
245
+ position: relative;
246
+ width: 600px;
247
+ min-height: 800px;
248
+ background: white;
249
+ border-radius: 4px;
250
+ box-shadow: 0 8px 32px rgba(0,0,0,0.35), 0 0 0 1px rgba(255,255,255,0.05);
251
+ color: hsl(0 0% 20%);
252
+ }
253
+
254
+ .fake-document {
255
+ padding: 48px 56px;
256
+ font-size: 11px;
257
+ line-height: 1.7;
258
+ }
259
+
260
+ .fake-title {
261
+ font-family: 'Plus Jakarta Sans', Inter, sans-serif;
262
+ font-size: 18px;
263
+ font-weight: 700;
264
+ margin-bottom: 4px;
265
+ color: hsl(0 0% 10%);
266
+ }
267
+
268
+ .fake-line {
269
+ height: 6px;
270
+ background: hsl(0 0% 82%);
271
+ margin-bottom: 7px;
272
+ border-radius: 1px;
273
+ }
274
+
275
+ .fake-line.heavy { background: hsl(0 0% 65%); }
276
+ .fake-line.short { width: 70%; }
277
+
278
+ .field-box {
279
+ position: absolute;
280
+ left: var(--x);
281
+ top: var(--y);
282
+ width: var(--w);
283
+ height: var(--h);
284
+ background: color-mix(in srgb, var(--field-color) 13%, transparent);
285
+ border: 1.5px dashed var(--field-color);
286
+ border-radius: 3px;
287
+ cursor: pointer;
288
+ display: flex;
289
+ align-items: center;
290
+ gap: 6px;
291
+ padding: 0 8px;
292
+ font-size: 10px;
293
+ font-weight: 500;
294
+ color: var(--field-color);
295
+ }
296
+
297
+ .field-box.selected {
298
+ border-style: solid;
299
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--field-color) 18%, transparent);
300
+ }
301
+
302
+ .recipient-line {
303
+ display: flex;
304
+ align-items: center;
305
+ gap: 8px;
306
+ padding: 8px 10px;
307
+ background: var(--bg-card);
308
+ border: 1px solid var(--border);
309
+ border-radius: 6px;
310
+ font-size: 12px;
311
+ color: var(--text-sec);
312
+ margin-bottom: 6px;
313
+ }
314
+
315
+ .recipient-line.active {
316
+ background: var(--hover);
317
+ border-color: var(--border-strong);
318
+ }
319
+
320
+ .progress-track {
321
+ height: 4px;
322
+ background: var(--bg-el);
323
+ flex-shrink: 0;
324
+ }
325
+
326
+ .progress-fill {
327
+ height: 100%;
328
+ background: var(--accent);
329
+ transition: width 0.4s ease;
330
+ }
331
+
332
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
333
+
334
+ @media (max-width: 920px) {
335
+ .topbar { padding: 0 16px; }
336
+ .actions { display: none; }
337
+ .content-scroll { padding: 16px; }
338
+ }
339
+ `;
340
+
341
+ export function icon(name: string, size = 14): TemplateResult {
342
+ const iconMap: Record<string, string> = {
343
+ inbox: 'lucide:Inbox', plus: 'lucide:Plus', folder: 'lucide:Folder', shield: 'lucide:Shield', code: 'lucide:Code2',
344
+ user: 'lucide:User', settings: 'lucide:Settings', upload: 'lucide:Upload', file: 'lucide:FileText', sign: 'lucide:PenTool',
345
+ clock: 'lucide:Clock', search: 'lucide:Search', more: 'lucide:MoreHorizontal', send: 'lucide:Send', check: 'lucide:Check',
346
+ eye: 'lucide:Eye', calendar: 'lucide:Calendar', type: 'lucide:Type', download: 'lucide:Download', hash: 'lucide:Hash',
347
+ github: 'lucide:GitBranch', git: 'lucide:GitBranch', server: 'lucide:Server', star: 'lucide:Star', sparkle: 'lucide:Sparkles',
348
+ chevronRight: 'lucide:ChevronRight', chevronDown: 'lucide:ChevronDown', x: 'lucide:X', activity: 'lucide:Activity',
349
+ };
350
+ return html`<dees-icon .icon=${iconMap[name] || iconMap.file} style="font-size: ${size}px;"></dees-icon>`;
351
+ }
352
+
353
+ export function pill(label: string, tone: 'default' | 'success' | 'warning' | 'error' | 'info' = 'default', dot = false): TemplateResult {
354
+ return html`<span class="pill ${tone} ${dot ? 'dot' : ''}">${label}</span>`;
355
+ }
356
+
357
+ export function actionButton(label: string, variant: 'primary' | 'outline' | 'ghost' = 'outline', iconName?: string, onClick?: () => void): TemplateResult {
358
+ return html`<button class="btn ${variant}" @click=${onClick || (() => undefined)}>${iconName ? icon(iconName, 13) : ''}${label}</button>`;
359
+ }
360
+
361
+ export function topBar(config: { breadcrumb: string[]; title: string; subtitle?: TemplateResult; actions?: TemplateResult }): TemplateResult {
362
+ return html`
363
+ <div class="topbar">
364
+ <div style="min-width: 0; flex: 1;">
365
+ <div class="breadcrumb">
366
+ ${config.breadcrumb.map((part, index) => html`${index > 0 ? icon('chevronRight', 10) : ''}<span>${part}</span>`)}
367
+ </div>
368
+ <div class="top-title"><span>${config.title}</span>${config.subtitle || ''}</div>
369
+ </div>
370
+ <div class="actions">${config.actions || ''}</div>
371
+ </div>
372
+ `;
373
+ }
374
+
375
+ export function workspaceDemoFrame(content: TemplateResult, theme: TWorkspaceTheme = 'dark'): TemplateResult {
376
+ const darkVars = `
377
+ --accent: #3b82f6;
378
+ --bg: hsl(0 0% 3.9%);
379
+ --bg-el: hsl(0 0% 6%);
380
+ --bg-card: hsl(0 0% 7%);
381
+ --bg-input: hsl(0 0% 9%);
382
+ --border: hsl(0 0% 14.9%);
383
+ --border-subtle: hsl(0 0% 11%);
384
+ --border-strong: hsl(0 0% 20%);
385
+ --text: hsl(0 0% 98%);
386
+ --text-sec: hsl(0 0% 63.9%);
387
+ --text-muted: hsl(0 0% 48%);
388
+ --text-dim: hsl(0 0% 32%);
389
+ --hover: rgba(255,255,255,0.06);
390
+ --hover-subtle: rgba(255,255,255,0.03);
391
+ --row-hover: rgba(255,255,255,0.025);
392
+ --success: #22c55e;
393
+ --warning: #f59e0b;
394
+ --error: #ef4444;
395
+ `;
396
+ const lightVars = `
397
+ --accent: #3b82f6;
398
+ --bg: hsl(0 0% 99%);
399
+ --bg-el: hsl(0 0% 97%);
400
+ --bg-card: hsl(0 0% 100%);
401
+ --bg-input: hsl(0 0% 98%);
402
+ --border: hsl(0 0% 90%);
403
+ --border-subtle: hsl(0 0% 93%);
404
+ --border-strong: hsl(0 0% 80%);
405
+ --text: hsl(0 0% 9%);
406
+ --text-sec: hsl(0 0% 32%);
407
+ --text-muted: hsl(0 0% 45%);
408
+ --text-dim: hsl(0 0% 62%);
409
+ --hover: rgba(0,0,0,0.04);
410
+ --hover-subtle: rgba(0,0,0,0.02);
411
+ --row-hover: rgba(0,0,0,0.02);
412
+ --success: #16a34a;
413
+ --warning: #d97706;
414
+ --error: #dc2626;
415
+ `;
416
+
417
+ return html`<div style="${theme === 'dark' ? darkVars : lightVars} height: 720px; min-height: 720px; background: var(--bg); color: var(--text); overflow: hidden; font-family: Geist, Inter, Roboto, -apple-system, BlinkMacSystemFont, sans-serif;">${content}</div>`;
418
+ }
419
+
420
+ export function fakeDocument(): TemplateResult {
421
+ return html`
422
+ <div class="fake-document">
423
+ <div class="fake-title">Master Services Agreement</div>
424
+ <div class="mono" style="font-size: 10px; color: hsl(0 0% 45%); margin-bottom: 24px;">Effective: May 2, 2026 · Acme Corp ↔ Lossless GmbH</div>
425
+ ${Array.from({ length: 18 }).map((_, index) => html`<div class="fake-line ${index % 5 === 0 ? 'heavy' : ''} ${index % 4 === 3 ? 'short' : ''}"></div>`)}
426
+ <div style="height: 16px;"></div>
427
+ ${Array.from({ length: 8 }).map((_, index) => html`<div class="fake-line ${index % 3 === 2 ? 'short' : ''}"></div>`)}
428
+ <div style="margin-top: 60px; font-size: 10px; font-weight: 600; color: hsl(0 0% 35%);">SIGNED ON BEHALF OF ACME CORP</div>
429
+ <div style="margin-top: 70px; font-size: 10px; font-weight: 600; color: hsl(0 0% 35%);">SIGNED ON BEHALF OF LOSSLESS GMBH</div>
430
+ </div>
431
+ `;
432
+ }
433
+
434
+ export function requestWorkspaceView(element: HTMLElement, view: TWorkspaceView) {
435
+ element.dispatchEvent(new CustomEvent('workspace-view-request', {
436
+ detail: { view },
437
+ bubbles: true,
438
+ composed: true,
439
+ }));
440
+ }