@design.estate/dees-catalog 9.6.0 → 9.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 +2957 -2685
- package/dist_bundle/bundle.js.map +1 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/demos/inputs.d.ts +1 -1
- package/dist_ts_web/demos/inputs.js +5 -4
- package/dist_ts_web/elements/00group-dataview/dees-table/dees-table.js +5 -4
- package/dist_ts_web/elements/00group-form/dees-form/dees-form.d.ts +5 -1
- package/dist_ts_web/elements/00group-form/dees-form/dees-form.js +27 -3
- package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.d.ts +1 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.js +11 -2
- package/dist_ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.d.ts +3 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.demo.js +4 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.js +51 -13
- package/dist_ts_web/elements/00group-input/dees-input-base/dees-input-base.d.ts +1 -8
- package/dist_ts_web/elements/00group-input/dees-input-base/dees-input-base.js +7 -2
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/component.d.ts +18 -3
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/component.js +181 -144
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/date-validation.d.ts +13 -0
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/date-validation.js +65 -0
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/datepicker-popup.js +44 -56
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/styles.js +6 -1
- package/dist_ts_web/elements/00group-input/dees-input-datepicker/template.js +11 -5
- package/dist_ts_web/elements/00group-input/dees-input-text/dees-input-text.d.ts +8 -8
- package/dist_ts_web/elements/00group-input/dees-input-text/dees-input-text.js +6 -53
- package/dist_ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.d.ts +62 -0
- package/dist_ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.demo.d.ts +1 -0
- package/dist_ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.demo.js +3 -0
- package/dist_ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.js +224 -0
- package/dist_ts_web/elements/00group-input/dees-input-textarea/index.d.ts +1 -0
- package/dist_ts_web/elements/00group-input/dees-input-textarea/index.js +2 -0
- package/dist_ts_web/elements/00group-input/index.d.ts +1 -0
- package/dist_ts_web/elements/00group-input/index.js +2 -1
- package/dist_ts_web/elements/00group-input/text-input-contextmenu.d.ts +29 -0
- package/dist_ts_web/elements/00group-input/text-input-contextmenu.js +29 -0
- package/dist_ts_web/elements/00group-overlay/dees-contextmenu/dees-contextmenu.d.ts +1 -1
- package/dist_ts_web/elements/00group-overlay/dees-contextmenu/dees-contextmenu.js +5 -4
- package/package.json +2 -2
- package/readme.md +78 -4
- package/scripts/check-bdtheme-ratchet.cjs +2 -2
- package/scripts/check-packed-consumer.mjs +48 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/demos/inputs.ts +5 -4
- package/ts_web/elements/00group-dataview/dees-table/dees-table.ts +3 -2
- package/ts_web/elements/00group-form/dees-form/dees-form.ts +25 -2
- package/ts_web/elements/00group-harness/dees-harness-chat/dees-harness-chat.ts +4 -0
- package/ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.demo.ts +3 -0
- package/ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.ts +37 -11
- package/ts_web/elements/00group-input/dees-input-base/dees-input-base.ts +6 -1
- package/ts_web/elements/00group-input/dees-input-datepicker/component.ts +149 -148
- package/ts_web/elements/00group-input/dees-input-datepicker/date-validation.ts +64 -0
- package/ts_web/elements/00group-input/dees-input-datepicker/datepicker-popup.ts +40 -40
- package/ts_web/elements/00group-input/dees-input-datepicker/styles.ts +5 -0
- package/ts_web/elements/00group-input/dees-input-datepicker/template.ts +10 -4
- package/ts_web/elements/00group-input/dees-input-text/dees-input-text.ts +5 -52
- package/ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.demo.ts +2 -0
- package/ts_web/elements/00group-input/dees-input-textarea/dees-input-textarea.ts +128 -0
- package/ts_web/elements/00group-input/dees-input-textarea/index.ts +1 -0
- package/ts_web/elements/00group-input/index.ts +1 -0
- package/ts_web/elements/00group-input/text-input-contextmenu.ts +31 -0
- package/ts_web/elements/00group-overlay/dees-contextmenu/dees-contextmenu.ts +3 -3
- package/readme.hints.md +0 -1233
- package/readme.icons.md +0 -1849
- package/readme.info.md +0 -80
- package/readme.plan.md +0 -671
- package/readme.playbook.md +0 -820
- package/readme.theme-migration.md +0 -168
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* bdTheme ratchet: cssManager.bdTheme() is legacy (see readme.theme-migration.md).
|
|
3
|
+
* bdTheme ratchet: cssManager.bdTheme() is legacy (see docs/development/readme.theme-migration.md).
|
|
4
4
|
* The only sanctioned usage is the token bridge in ts_web/elements/00theme.ts.
|
|
5
5
|
* Per-file call counts are frozen in scripts/bdtheme-baseline.json and may only shrink.
|
|
6
6
|
*
|
|
@@ -72,7 +72,7 @@ for (const [file, count] of Object.entries(current)) {
|
|
|
72
72
|
|
|
73
73
|
if (violations.length > 0) {
|
|
74
74
|
console.error('[bdtheme-ratchet] FAIL — cssManager.bdTheme() is legacy; use --dees-* tokens from 00theme.ts');
|
|
75
|
-
console.error('[bdtheme-ratchet] see readme.theme-migration.md and readme.playbook.md (Theming System)');
|
|
75
|
+
console.error('[bdtheme-ratchet] see docs/development/readme.theme-migration.md and docs/development/readme.playbook.md (Theming System)');
|
|
76
76
|
for (const violation of violations) console.error(` ${violation}`);
|
|
77
77
|
process.exit(1);
|
|
78
78
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { cpSync, mkdirSync, mkdtempSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
// An actual isolated consumer: install the tarball, never source aliases or links.
|
|
7
|
+
// An optional argument checks a release journal's exact tarball without repacking.
|
|
8
|
+
const root = fileURLToPath(new URL('../', import.meta.url));
|
|
9
|
+
const debugRoot = join(root, '.nogit/debug');
|
|
10
|
+
mkdirSync(debugRoot, { recursive: true });
|
|
11
|
+
const consumer = mkdtempSync(join(debugRoot, 'packed-consumer-'));
|
|
12
|
+
const run = (args, cwd = consumer) => execFileSync('pnpm', args, { cwd, stdio: 'inherit' });
|
|
13
|
+
const tarball = process.argv[2] ? resolve(process.argv[2]) : join(consumer, 'catalog.tgz');
|
|
14
|
+
if (!process.argv[2]) run(['pack', '--out', tarball], root);
|
|
15
|
+
cpSync(join(root, 'test/fixtures/packed-consumer'), consumer, { recursive: true });
|
|
16
|
+
renameSync(join(consumer, 'entrypoints.ts.fixture'), join(consumer, 'entrypoints.ts'));
|
|
17
|
+
writeFileSync(join(consumer, 'package.json'), JSON.stringify({
|
|
18
|
+
name: 'catalog-packed-consumer', private: true, type: 'module',
|
|
19
|
+
packageManager: 'pnpm@12.3.1',
|
|
20
|
+
dependencies: {
|
|
21
|
+
'@design.estate/dees-catalog': tarball,
|
|
22
|
+
'@design.estate/dees-element': '3.1.1',
|
|
23
|
+
},
|
|
24
|
+
devDependencies: {
|
|
25
|
+
typescript: '5.9.3',
|
|
26
|
+
'@types/node': '26.4.1',
|
|
27
|
+
'@git.zone/tstest': '6.0.0',
|
|
28
|
+
'@push.rocks/smartbrowser': '2.0.11',
|
|
29
|
+
},
|
|
30
|
+
}, null, 2));
|
|
31
|
+
writeFileSync(join(consumer, 'pnpm-workspace.yaml'), 'packages:\n - "."\n');
|
|
32
|
+
// Run the same regressions against installed JavaScript, not sources.
|
|
33
|
+
const browserTests = ['table-construction', 'datepicker-validation', 'textarea', 'composer-attachments'];
|
|
34
|
+
for (const name of browserTests) {
|
|
35
|
+
const browserTest = readFileSync(join(root, `test/test.${name}.browser.ts`), 'utf8')
|
|
36
|
+
.replaceAll('../ts_web/', '@design.estate/dees-catalog/dist_ts_web/');
|
|
37
|
+
writeFileSync(join(consumer, `test.${name}.browser.ts`), browserTest);
|
|
38
|
+
}
|
|
39
|
+
console.log(`Packed consumer: ${consumer}`);
|
|
40
|
+
run(['install', '--ignore-scripts']);
|
|
41
|
+
run(['exec', 'tsc', '--version']);
|
|
42
|
+
run(['exec', 'tsc', '--project', 'tsconfig.json']);
|
|
43
|
+
// Verify the current producer compiler too; neither run skips dependency checks.
|
|
44
|
+
run(['add', '--save-dev', '--save-exact', '--ignore-scripts', 'typescript@6.0.3']);
|
|
45
|
+
run(['exec', 'tsc', '--version']);
|
|
46
|
+
run(['exec', 'tsc', '--project', 'tsconfig.json']);
|
|
47
|
+
run(['exec', 'tstest', 'test.*.browser.ts', '--verbose', '--logfile']);
|
|
48
|
+
run(['exec', 'node', 'browser.mjs']);
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@design.estate/dees-catalog',
|
|
6
|
-
version: '9.
|
|
6
|
+
version: '9.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/demos/inputs.ts
CHANGED
|
@@ -4,20 +4,21 @@ import { demoPage } from './templates.js';
|
|
|
4
4
|
import type { TFormData } from '../elements/00group-form/dees-form/dees-form.js';
|
|
5
5
|
|
|
6
6
|
/** One fixture vocabulary for the showcase and each input's focused demo. */
|
|
7
|
-
export type TInputExample = 'text' | 'dropdown' | 'datepicker' | 'checkbox' | 'toggle' | 'radiogroup' | 'multitoggle' | 'quantityselector' | 'phone' | 'iban' | 'tags' | 'typelist' | 'list' | 'fileupload' | 'profilepicture' | 'richtext' | 'wysiwyg' | 'code';
|
|
7
|
+
export type TInputExample = 'text' | 'textarea' | 'dropdown' | 'datepicker' | 'checkbox' | 'toggle' | 'radiogroup' | 'multitoggle' | 'quantityselector' | 'phone' | 'iban' | 'tags' | 'typelist' | 'list' | 'fileupload' | 'profilepicture' | 'richtext' | 'wysiwyg' | 'code';
|
|
8
8
|
|
|
9
9
|
const titles: Record<TInputExample, string> = {
|
|
10
|
-
text: 'Text fields', dropdown: 'Dropdowns', datepicker: 'Dates & times', checkbox: 'Checkboxes', toggle: 'Switches',
|
|
10
|
+
text: 'Text fields', textarea: 'Multiline text', dropdown: 'Dropdowns', datepicker: 'Dates & times', checkbox: 'Checkboxes', toggle: 'Switches',
|
|
11
11
|
radiogroup: 'Radio choices', multitoggle: 'Segmented controls', quantityselector: 'Quantities', phone: 'Phone numbers',
|
|
12
12
|
iban: 'IBAN', tags: 'Tags', typelist: 'Type lists', list: 'Editable lists', fileupload: 'File uploads',
|
|
13
13
|
profilepicture: 'Profile pictures', richtext: 'Rich text', wysiwyg: 'Block editor', code: 'Code editor',
|
|
14
14
|
};
|
|
15
|
-
const compound: TInputExample[] = ['tags', 'typelist', 'list', 'fileupload', 'profilepicture', 'richtext', 'wysiwyg', 'code'];
|
|
15
|
+
const compound: TInputExample[] = ['textarea', 'tags', 'typelist', 'list', 'fileupload', 'profilepicture', 'richtext', 'wysiwyg', 'code'];
|
|
16
16
|
const visibility = [{ key: 'team', option: 'Team members' }, { key: 'private', option: 'Only me' }, { key: 'public', option: 'Anyone with the link' }];
|
|
17
17
|
|
|
18
18
|
export const inputExample = (kind: TInputExample, disabled = false): TemplateResult => {
|
|
19
19
|
switch (kind) {
|
|
20
20
|
case 'text': return html`<dees-input-text key="name" label="Workspace name" value="Design studio" required ?disabled=${disabled} description="The name your teammates see."></dees-input-text>`;
|
|
21
|
+
case 'textarea': return html`<dees-input-textarea key="paymentNotes" label="Payment notes" .value=${'Invoice 2026-042\nReference: studio equipment'} .rows=${3} .maxLength=${1000} ?disabled=${disabled} description="Plain text. Line breaks are preserved."></dees-input-textarea>`;
|
|
21
22
|
case 'dropdown': return html`<dees-input-dropdown key="visibility" label="Visible to" .options=${visibility} .selectedOption=${visibility[0]} ?disabled=${disabled}></dees-input-dropdown>`;
|
|
22
23
|
case 'datepicker': return html`<dees-input-datepicker key="review" label="Next review" value="2026-09-14T09:00:00" .enableTime=${true} timeFormat="24h" .minuteIncrement=${15} ?disabled=${disabled}></dees-input-datepicker>`;
|
|
23
24
|
case 'checkbox': return html`<dees-input-checkbox key="digest" label="Include a weekly summary" .value=${true} ?disabled=${disabled}></dees-input-checkbox>`;
|
|
@@ -96,7 +97,7 @@ export class DeesDemoInputWorkspace extends DeesElement {
|
|
|
96
97
|
<dees-form @formData=${this.submit} style="gap:0">
|
|
97
98
|
${this.section('General', 'Make this space your own.', ['text', 'dropdown', 'datepicker'])}
|
|
98
99
|
${this.section('Your team', 'A setup that fits the way you work.', ['radiogroup', 'quantityselector', 'multitoggle', 'toggle', 'checkbox'])}
|
|
99
|
-
${this.section('Profile & billing', 'Your picture and contact details.', ['profilepicture', 'phone', 'iban'])}
|
|
100
|
+
${this.section('Profile & billing', 'Your picture and contact details.', ['profilepicture', 'phone', 'iban', 'textarea'])}
|
|
100
101
|
${this.section('Organization', 'Small details that keep work moving.', ['tags', 'typelist', 'list'])}
|
|
101
102
|
${this.section('Documents', 'The context behind the work.', ['fileupload', 'richtext', 'wysiwyg'])}
|
|
102
103
|
${this.section('Automation', 'Fine-tune the workspace configuration.', ['code'])}
|
|
@@ -356,8 +356,6 @@ export class DeesTable<T> extends DeesElement {
|
|
|
356
356
|
|
|
357
357
|
constructor() {
|
|
358
358
|
super();
|
|
359
|
-
// Make the host focusable so it can receive Ctrl/Cmd+C for copy.
|
|
360
|
-
if (!this.hasAttribute('tabindex')) this.setAttribute('tabindex', '0');
|
|
361
359
|
this.addEventListener('keydown', this.__handleHostKeydown);
|
|
362
360
|
}
|
|
363
361
|
|
|
@@ -1280,6 +1278,9 @@ export class DeesTable<T> extends DeesElement {
|
|
|
1280
1278
|
}
|
|
1281
1279
|
|
|
1282
1280
|
public connectedCallback(): Promise<void> {
|
|
1281
|
+
// Custom-element constructors must not add host attributes. Initialize the
|
|
1282
|
+
// keyboard entry point here, after parser/caller attributes are available.
|
|
1283
|
+
if (!this.hasAttribute('tabindex')) this.setAttribute('tabindex', '0');
|
|
1283
1284
|
const connected = super.connectedCallback();
|
|
1284
1285
|
if (this.hasUpdated) this.requestUpdate();
|
|
1285
1286
|
return connected;
|
|
@@ -13,6 +13,7 @@ import { DeesInputCheckbox } from '../../00group-input/dees-input-checkbox/dees-
|
|
|
13
13
|
import { DeesInputCode } from '../../00group-input/dees-input-code/dees-input-code.js';
|
|
14
14
|
import { DeesInputDatepicker } from '../../00group-input/dees-input-datepicker/index.js';
|
|
15
15
|
import { DeesInputText } from '../../00group-input/dees-input-text/dees-input-text.js';
|
|
16
|
+
import { DeesInputTextarea } from '../../00group-input/dees-input-textarea/dees-input-textarea.js';
|
|
16
17
|
import { DeesInputQuantitySelector } from '../../00group-input/dees-input-quantityselector/dees-input-quantityselector.js';
|
|
17
18
|
import { DeesInputRadiogroup } from '../../00group-input/dees-input-radiogroup/dees-input-radiogroup.js';
|
|
18
19
|
import { DeesInputDropdown } from '../../00group-input/dees-input-dropdown/dees-input-dropdown.js';
|
|
@@ -44,6 +45,7 @@ const FORM_INPUT_TYPES = [
|
|
|
44
45
|
DeesInputQuantitySelector,
|
|
45
46
|
DeesInputRadiogroup,
|
|
46
47
|
DeesInputText,
|
|
48
|
+
DeesInputTextarea,
|
|
47
49
|
DeesInputToggle,
|
|
48
50
|
DeesInputTypelist,
|
|
49
51
|
DeesInputTags,
|
|
@@ -65,6 +67,9 @@ export interface IFormCompatibleElement extends HTMLElement {
|
|
|
65
67
|
};
|
|
66
68
|
getValue?: () => unknown;
|
|
67
69
|
setValue?: (value: unknown) => void;
|
|
70
|
+
checkValidity?: () => boolean;
|
|
71
|
+
reportValidity?: () => boolean;
|
|
72
|
+
reset?: () => void;
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
export type TFormDataValue =
|
|
@@ -96,6 +101,7 @@ export type TFormInputElement =
|
|
|
96
101
|
| DeesInputQuantitySelector
|
|
97
102
|
| DeesInputRadiogroup
|
|
98
103
|
| DeesInputText
|
|
104
|
+
| DeesInputTextarea
|
|
99
105
|
| DeesInputToggle
|
|
100
106
|
| DeesInputTypelist
|
|
101
107
|
| DeesInputTags
|
|
@@ -281,7 +287,9 @@ export class DeesForm extends DeesElement {
|
|
|
281
287
|
public async updateRequiredStatus() {
|
|
282
288
|
let requiredOK = true;
|
|
283
289
|
for (const childArg of this.getFormElements()) {
|
|
284
|
-
if (childArg.
|
|
290
|
+
if (childArg.disabled) continue;
|
|
291
|
+
if ((childArg.required && !getFormElementValue(childArg))
|
|
292
|
+
|| (childArg as IFormCompatibleElement).checkValidity?.() === false) {
|
|
285
293
|
requiredOK = false;
|
|
286
294
|
}
|
|
287
295
|
}
|
|
@@ -312,6 +320,15 @@ export class DeesForm extends DeesElement {
|
|
|
312
320
|
}
|
|
313
321
|
|
|
314
322
|
public async gatherAndDispatch() {
|
|
323
|
+
for (const child of this.getFormElements()) {
|
|
324
|
+
if (child.disabled) continue;
|
|
325
|
+
const input = child as IFormCompatibleElement;
|
|
326
|
+
if (input.checkValidity?.() === false) {
|
|
327
|
+
input.reportValidity?.();
|
|
328
|
+
child.focus();
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
315
332
|
const valueObject = await this.collectFormData();
|
|
316
333
|
const formDataEvent = new CustomEvent('formData', {
|
|
317
334
|
detail: {
|
|
@@ -371,12 +388,18 @@ export class DeesForm extends DeesElement {
|
|
|
371
388
|
const inputChildren = this.getFormElements();
|
|
372
389
|
|
|
373
390
|
for (const inputChild of inputChildren) {
|
|
374
|
-
|
|
391
|
+
const input = inputChild as IFormCompatibleElement;
|
|
392
|
+
if (input.reset) input.reset();
|
|
393
|
+
else setFormElementValue(inputChild, null);
|
|
375
394
|
}
|
|
376
395
|
this.setStatus('normal', 'Submit');
|
|
396
|
+
void this.updateRequiredStatus();
|
|
377
397
|
}
|
|
378
398
|
|
|
379
399
|
public async addBehaviours() {
|
|
400
|
+
this.addEventListener('validation-change', event => {
|
|
401
|
+
if (this.getFormElements().includes(event.target as TFormInputElement)) void this.updateRequiredStatus();
|
|
402
|
+
});
|
|
380
403
|
// Use event delegation
|
|
381
404
|
this.addEventListener('keydown', (event: KeyboardEvent) => {
|
|
382
405
|
const target = event.target as DeesElement;
|
|
@@ -203,6 +203,9 @@ export class DeesHarnessChat extends DeesElement {
|
|
|
203
203
|
@property({ attribute: false })
|
|
204
204
|
accessor attachments: IHarnessAttachment[] = [];
|
|
205
205
|
|
|
206
|
+
@property({ attribute: false })
|
|
207
|
+
accessor attachmentsEnabled: boolean = true;
|
|
208
|
+
|
|
206
209
|
@property({ attribute: false })
|
|
207
210
|
accessor suggestions: IHarnessComposerSuggestion[] = [];
|
|
208
211
|
|
|
@@ -763,6 +766,7 @@ export class DeesHarnessChat extends DeesElement {
|
|
|
763
766
|
.contextDescription=${this.heading ? `Replying to ${this.heading}` : ''}
|
|
764
767
|
.value=${this.composerValue}
|
|
765
768
|
.attachments=${this.attachments}
|
|
769
|
+
.attachmentsEnabled=${this.attachmentsEnabled}
|
|
766
770
|
.suggestions=${this.suggestions}
|
|
767
771
|
.maxAttachmentCount=${this.maxAttachmentCount}
|
|
768
772
|
.maxAttachmentBytes=${this.maxAttachmentBytes}
|
|
@@ -44,6 +44,9 @@ export const demoFunc = () => demoPage('Composer', 'Write, attach files, choose
|
|
|
44
44
|
<dees-harness-composer busy queuedCount="2"></dees-harness-composer>
|
|
45
45
|
<div class="dees-demo-label">Waiting for a connection</div>
|
|
46
46
|
<dees-harness-composer disabled placeholder="Connect to a project to chat."></dees-harness-composer>
|
|
47
|
+
<div class="dees-demo-label">Text-only conversation</div>
|
|
48
|
+
<dees-harness-composer .attachmentsEnabled=${false} .showModelControls=${false}
|
|
49
|
+
value="Summarize the outstanding invoices."></dees-harness-composer>
|
|
47
50
|
<div class="dees-demo-label">Files ready to send</div>
|
|
48
51
|
<dees-harness-composer
|
|
49
52
|
.showModelControls=${false}
|
|
@@ -112,6 +112,10 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
112
112
|
@property({ attribute: false })
|
|
113
113
|
accessor attachments: IHarnessAttachment[] = [];
|
|
114
114
|
|
|
115
|
+
/** Host capability: false disables every attachment entry point and sends text only. */
|
|
116
|
+
@property({ attribute: false })
|
|
117
|
+
accessor attachmentsEnabled: boolean = true;
|
|
118
|
+
|
|
115
119
|
@property({ attribute: false })
|
|
116
120
|
accessor suggestions: IHarnessComposerSuggestion[] = [];
|
|
117
121
|
|
|
@@ -473,6 +477,10 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
473
477
|
|
|
474
478
|
public updated(changedProperties: Map<PropertyKey, unknown>): void {
|
|
475
479
|
super.updated(changedProperties);
|
|
480
|
+
if (['attachmentsEnabled', 'disabled'].some(key => changedProperties.has(key) && changedProperties.get(key) !== undefined)) {
|
|
481
|
+
this.attachmentGeneration++;
|
|
482
|
+
if (!this.attachmentsEnabled || this.disabled) this.handleDragLeave();
|
|
483
|
+
}
|
|
476
484
|
this.syncTextareaSuggestionAria();
|
|
477
485
|
}
|
|
478
486
|
|
|
@@ -486,13 +494,13 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
486
494
|
@dragleave=${this.handleDragLeave}
|
|
487
495
|
@drop=${this.handleDrop}
|
|
488
496
|
>
|
|
489
|
-
|
|
497
|
+
${this.attachmentsEnabled ? html`<input
|
|
490
498
|
class="fileInput"
|
|
491
499
|
type="file"
|
|
492
500
|
multiple
|
|
493
501
|
@change=${this.handleFileInputChange}
|
|
494
|
-
|
|
495
|
-
${this.attachments.length
|
|
502
|
+
/>` : ''}
|
|
503
|
+
${this.attachmentsEnabled && this.attachments.length
|
|
496
504
|
? html`
|
|
497
505
|
<div class="attachmentRow">
|
|
498
506
|
${this.attachments.map((attachment) => this.renderChip(attachment))}
|
|
@@ -505,7 +513,7 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
505
513
|
.value=${this.value}
|
|
506
514
|
aria-label="Message"
|
|
507
515
|
aria-description=${this.contextDescription}
|
|
508
|
-
placeholder=${this.placeholder}
|
|
516
|
+
placeholder=${!this.attachmentsEnabled && this.placeholder === 'Message the agent — drop or paste files to attach…' ? 'Message the agent…' : this.placeholder}
|
|
509
517
|
?disabled=${this.disabled}
|
|
510
518
|
rows="1"
|
|
511
519
|
@input=${this.handleTextInput}
|
|
@@ -518,14 +526,16 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
518
526
|
></textarea>
|
|
519
527
|
</div>
|
|
520
528
|
<div class="controlsRow">
|
|
521
|
-
|
|
529
|
+
${this.attachmentsEnabled ? html`<dees-button
|
|
530
|
+
class="attachButton"
|
|
522
531
|
size="sm"
|
|
523
532
|
type="ghost"
|
|
533
|
+
title="Attach files"
|
|
524
534
|
?disabled=${this.disabled}
|
|
525
535
|
@clicked=${() => this.shadowRoot?.querySelector<HTMLInputElement>('.fileInput')?.click()}
|
|
526
536
|
>
|
|
527
537
|
<dees-icon icon="lucide:Paperclip" iconSize="14"></dees-icon>
|
|
528
|
-
</dees-button
|
|
538
|
+
</dees-button>` : ''}
|
|
529
539
|
${this.modeOptions.length
|
|
530
540
|
? html`
|
|
531
541
|
<dees-input-multitoggle class="modeSwitch" label="Agent mode" .labelPosition=${'none'}
|
|
@@ -869,6 +879,7 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
869
879
|
};
|
|
870
880
|
|
|
871
881
|
private handlePaste = (event: ClipboardEvent): void => {
|
|
882
|
+
if (!this.attachmentsEnabled || this.disabled) return;
|
|
872
883
|
const files = Array.from(event.clipboardData?.files ?? []);
|
|
873
884
|
if (!files.length) return;
|
|
874
885
|
event.preventDefault();
|
|
@@ -878,6 +889,10 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
878
889
|
private handleDragOver = (event: DragEvent): void => {
|
|
879
890
|
if (!event.dataTransfer?.types.includes('Files')) return;
|
|
880
891
|
event.preventDefault();
|
|
892
|
+
if (!this.attachmentsEnabled || this.disabled) {
|
|
893
|
+
event.dataTransfer.dropEffect = 'none';
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
881
896
|
this.shadowRoot?.querySelector('.composer')?.classList.add('dragOver');
|
|
882
897
|
};
|
|
883
898
|
|
|
@@ -890,23 +905,32 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
890
905
|
const files = Array.from(event.dataTransfer?.files ?? []);
|
|
891
906
|
if (!files.length) return;
|
|
892
907
|
event.preventDefault();
|
|
908
|
+
if (!this.attachmentsEnabled || this.disabled) {
|
|
909
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = 'none';
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
893
912
|
void this.addFiles(files);
|
|
894
913
|
};
|
|
895
914
|
|
|
896
915
|
private attachmentAdditionTail = Promise.resolve();
|
|
916
|
+
private attachmentGeneration = 0;
|
|
897
917
|
|
|
898
918
|
public addFiles(fileList: FileList | File[] | null): Promise<void> {
|
|
899
919
|
const files = Array.from(fileList ?? []);
|
|
900
|
-
if (!files.length) return Promise.resolve();
|
|
901
|
-
const
|
|
920
|
+
if (!files.length || !this.attachmentsEnabled || this.disabled) return Promise.resolve();
|
|
921
|
+
const generation = this.attachmentGeneration;
|
|
922
|
+
const addition = this.attachmentAdditionTail.then(() => this.addFilesSequential(files, generation));
|
|
902
923
|
this.attachmentAdditionTail = addition.catch(() => undefined);
|
|
903
924
|
return addition;
|
|
904
925
|
}
|
|
905
926
|
|
|
906
|
-
private async addFilesSequential(files: File[]): Promise<void> {
|
|
927
|
+
private async addFilesSequential(files: File[], generation: number): Promise<void> {
|
|
928
|
+
const allowed = () => this.attachmentsEnabled && !this.disabled && generation === this.attachmentGeneration;
|
|
929
|
+
if (!allowed()) return;
|
|
907
930
|
const added: IHarnessAttachment[] = [];
|
|
908
931
|
let totalBytes = this.attachments.reduce((sum, attachment) => sum + attachment.size, 0);
|
|
909
932
|
for (const file of files) {
|
|
933
|
+
if (!allowed()) return;
|
|
910
934
|
if (this.attachments.length + added.length >= this.maxAttachmentCount) {
|
|
911
935
|
this.emit('harness-attachment-error', {
|
|
912
936
|
message: `A maximum of ${this.maxAttachmentCount} attachments is allowed.`,
|
|
@@ -928,6 +952,7 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
928
952
|
}
|
|
929
953
|
try {
|
|
930
954
|
const dataBase64 = await harnessFileToBase64(file);
|
|
955
|
+
if (!allowed()) return;
|
|
931
956
|
const kind = harnessAttachmentKindFor(mediaType);
|
|
932
957
|
added.push({
|
|
933
958
|
id: globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
@@ -939,12 +964,13 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
939
964
|
});
|
|
940
965
|
totalBytes += file.size;
|
|
941
966
|
} catch (error) {
|
|
967
|
+
if (!allowed()) return;
|
|
942
968
|
this.emit('harness-attachment-error', {
|
|
943
969
|
message: error instanceof Error ? error.message : String(error),
|
|
944
970
|
});
|
|
945
971
|
}
|
|
946
972
|
}
|
|
947
|
-
if (added.length) {
|
|
973
|
+
if (added.length && allowed()) {
|
|
948
974
|
this.emitAttachments([...this.attachments, ...added]);
|
|
949
975
|
}
|
|
950
976
|
}
|
|
@@ -957,7 +983,7 @@ export class DeesHarnessComposer extends DeesElement {
|
|
|
957
983
|
&& (!this.busy || !this.inputSteeringEnabled || !this.value.trim())) return;
|
|
958
984
|
const detail: IHarnessComposerSendDetail = {
|
|
959
985
|
text: this.value,
|
|
960
|
-
attachments: this.attachments,
|
|
986
|
+
attachments: this.attachmentsEnabled ? this.attachments : [],
|
|
961
987
|
account: this.account || undefined,
|
|
962
988
|
model: this.model || undefined,
|
|
963
989
|
reasoningEffort: this.reasoningEffort || undefined,
|
|
@@ -15,7 +15,12 @@ import '../../00group-layout/dees-label/dees-label.js';
|
|
|
15
15
|
* Provides unified margin system and layout mode support
|
|
16
16
|
*/
|
|
17
17
|
export abstract class DeesInputBase<T = any> extends DeesElement {
|
|
18
|
-
|
|
18
|
+
// Keep declarations tied to the superclass's DOM contract instead of emitting
|
|
19
|
+
// the build compiler's inferred (and potentially newer) ShadowRootInit shape.
|
|
20
|
+
public static override shadowRootOptions: typeof DeesElement.shadowRootOptions = {
|
|
21
|
+
...DeesElement.shadowRootOptions,
|
|
22
|
+
delegatesFocus: true,
|
|
23
|
+
};
|
|
19
24
|
public static inputLayout: 'field' | 'choice' | 'compound' = 'field';
|
|
20
25
|
/**
|
|
21
26
|
* Layout mode for the input component
|