@things-factory/ai-assistant 10.1.109 → 10.1.111
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/client/components/assistant-chat.ts +290 -18
- package/client/components/assistant-dock.ts +8 -3
- package/client/setup-assistant-dock.ts +9 -1
- package/client/utils/composer-frame.ts +49 -0
- package/client/utils/pasted-table.ts +2 -2
- package/dist-client/components/assistant-chat.d.ts +18 -0
- package/dist-client/components/assistant-chat.js +291 -18
- package/dist-client/components/assistant-chat.js.map +1 -1
- package/dist-client/components/assistant-dock.js +9 -3
- package/dist-client/components/assistant-dock.js.map +1 -1
- package/dist-client/setup-assistant-dock.js +8 -1
- package/dist-client/setup-assistant-dock.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/composer-frame.d.ts +28 -0
- package/dist-client/utils/composer-frame.js +46 -0
- package/dist-client/utils/composer-frame.js.map +1 -0
- package/dist-client/utils/pasted-table.d.ts +1 -0
- package/dist-client/utils/pasted-table.js +1 -1
- package/dist-client/utils/pasted-table.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/test/assistant-dock.test.ts +28 -0
- package/test/composer-frame.test.ts +125 -0
- package/test/pasted-table.test.ts +1 -1
- package/translations/en.json +7 -1
- package/translations/ja.json +7 -1
- package/translations/ko.json +7 -1
- package/translations/ms.json +7 -1
- package/translations/zh.json +7 -1
|
@@ -29,6 +29,7 @@ import gql from 'graphql-tag';
|
|
|
29
29
|
import { renderMarkdown, injectMentionRefids, injectMentionUserIds, stripMentionRefids } from './markdown.js';
|
|
30
30
|
import { acceptOf, chatAttachmentUrl, refuseAttachments, textCharsOf } from '../utils/attachment-policy.js';
|
|
31
31
|
import { pastedTableFile } from '../utils/pasted-table.js';
|
|
32
|
+
import { appendWords, composerHeight, composerKeyAction, sendShortcut, tableAsWords } from '../utils/composer-frame.js';
|
|
32
33
|
import { readSpreadsheet, spreadsheetKindOf } from '../utils/spreadsheet-text.js';
|
|
33
34
|
import { entryReviewAddress, offersWorkspaceReview } from '../utils/entry-workspace.js';
|
|
34
35
|
import { buildAssistantChatInput, buildChatMutationInput } from './chat-input-builder.js';
|
|
@@ -295,6 +296,10 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
295
296
|
this.attachments = [];
|
|
296
297
|
/** 드래그 오버 상태 (시각 피드백용) */
|
|
297
298
|
this.dragOver = false;
|
|
299
|
+
/** The wide box (넓게 쓰기) covers the conversation inside the dock - shared-dock.md §10. */
|
|
300
|
+
this.composerWide = false;
|
|
301
|
+
/** The pasted table whose rows cover the conversation, or null. */
|
|
302
|
+
this.previewAttachmentId = null;
|
|
298
303
|
/** mini action — 인라인 예시 토글 */
|
|
299
304
|
this.examplesOpen = false;
|
|
300
305
|
/** 원본 JSON 을 펼친 단계(라인:단계) — 목록은 한 줄 요약으로 훑고 필요한 것만 펼친다. */
|
|
@@ -449,7 +454,7 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
449
454
|
params.kinds = refusal.kinds.join(', ');
|
|
450
455
|
return String(i18next.t(`ai-assistant.text.${refusal.code}`, { ...params, defaultValue: refusal.code }));
|
|
451
456
|
}
|
|
452
|
-
async onAttachFiles(files) {
|
|
457
|
+
async onAttachFiles(files, table) {
|
|
453
458
|
if (!files || files.length === 0)
|
|
454
459
|
return;
|
|
455
460
|
let list = Array.from(files);
|
|
@@ -488,7 +493,8 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
488
493
|
size: file.size,
|
|
489
494
|
type: file.type || 'application/octet-stream',
|
|
490
495
|
url,
|
|
491
|
-
base64
|
|
496
|
+
base64,
|
|
497
|
+
table
|
|
492
498
|
});
|
|
493
499
|
}
|
|
494
500
|
this.attachments = [...this.attachments, ...newAttachments];
|
|
@@ -531,6 +537,40 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
531
537
|
URL.revokeObjectURL(target.url);
|
|
532
538
|
}
|
|
533
539
|
this.attachments = this.attachments.filter(a => a.id !== id);
|
|
540
|
+
if (this.previewAttachmentId === id)
|
|
541
|
+
this.previewAttachmentId = null;
|
|
542
|
+
}
|
|
543
|
+
/** A pasted table goes back into the box as the words it was copied as - the conversion was not the person's choice. */
|
|
544
|
+
tableToWords(id) {
|
|
545
|
+
const target = this.attachments.find(a => a.id === id);
|
|
546
|
+
if (!target?.table)
|
|
547
|
+
return;
|
|
548
|
+
this.input = appendWords(this.input, tableAsWords(target.table));
|
|
549
|
+
this.removeAttachment(id);
|
|
550
|
+
this.updateComplete.then(() => this.renderRoot.querySelector('textarea')?.focus());
|
|
551
|
+
}
|
|
552
|
+
/** The small box stands as tall as its text, up to 40% of the chat; the wide box fills what it covers. */
|
|
553
|
+
fitComposer() {
|
|
554
|
+
const ta = this.renderRoot.querySelector('.input-row textarea');
|
|
555
|
+
if (!ta)
|
|
556
|
+
return;
|
|
557
|
+
if (this.composerWide) {
|
|
558
|
+
ta.style.height = '';
|
|
559
|
+
ta.style.overflowY = '';
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
ta.style.height = '0px';
|
|
563
|
+
const border = ta.offsetHeight - ta.clientHeight;
|
|
564
|
+
const resting = parseFloat(getComputedStyle(ta).minHeight) || 0;
|
|
565
|
+
const { height, scrolls } = composerHeight(ta.scrollHeight + border, this.clientHeight, resting);
|
|
566
|
+
ta.style.height = `${height}px`;
|
|
567
|
+
ta.style.overflowY = scrolls ? 'auto' : 'hidden';
|
|
568
|
+
}
|
|
569
|
+
setComposerWide(wide) {
|
|
570
|
+
this.composerWide = wide;
|
|
571
|
+
if (wide)
|
|
572
|
+
this.previewAttachmentId = null;
|
|
573
|
+
this.updateComplete.then(() => this.renderRoot.querySelector('.input-row textarea')?.focus());
|
|
534
574
|
}
|
|
535
575
|
onComposerPaste(e) {
|
|
536
576
|
const items = e.clipboardData?.items;
|
|
@@ -548,7 +588,8 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
548
588
|
this.errorMessage = String(i18next.t(`ai-assistant.text.${pasted.refusal.code}`, { ...pasted.refusal, defaultValue: pasted.refusal.code }));
|
|
549
589
|
return;
|
|
550
590
|
}
|
|
551
|
-
|
|
591
|
+
const words = e.clipboardData?.getData('text/plain');
|
|
592
|
+
this.onAttachFiles([new File([pasted.text], pasted.name, { type: 'text/csv' })], { ...pasted.table, text: words });
|
|
552
593
|
return;
|
|
553
594
|
}
|
|
554
595
|
}
|
|
@@ -2015,6 +2056,128 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2015
2056
|
flex-direction: column;
|
|
2016
2057
|
transition: background 0.15s, border-color 0.15s;
|
|
2017
2058
|
}
|
|
2059
|
+
/* The wide box and a table's rows cover the conversation, inside the dock - the page beside it stays. */
|
|
2060
|
+
.messages.covered {
|
|
2061
|
+
flex: 0 0 0;
|
|
2062
|
+
padding: 0;
|
|
2063
|
+
overflow: hidden;
|
|
2064
|
+
visibility: hidden;
|
|
2065
|
+
}
|
|
2066
|
+
.composer.wide,
|
|
2067
|
+
.composer.previewing {
|
|
2068
|
+
flex: 1 1 auto;
|
|
2069
|
+
min-height: 0;
|
|
2070
|
+
}
|
|
2071
|
+
.composer.wide .input-row {
|
|
2072
|
+
flex: 1;
|
|
2073
|
+
min-height: 0;
|
|
2074
|
+
align-items: stretch;
|
|
2075
|
+
}
|
|
2076
|
+
.composer.wide .input-row > :not(.textarea-wrap) {
|
|
2077
|
+
align-self: flex-end;
|
|
2078
|
+
}
|
|
2079
|
+
.composer.wide textarea {
|
|
2080
|
+
min-height: 0;
|
|
2081
|
+
}
|
|
2082
|
+
.composer-size {
|
|
2083
|
+
margin-left: auto;
|
|
2084
|
+
}
|
|
2085
|
+
/* Named above the send button, out of the row - it takes no width from the box. */
|
|
2086
|
+
.composer.wide .input-row {
|
|
2087
|
+
position: relative;
|
|
2088
|
+
}
|
|
2089
|
+
.send-shortcut {
|
|
2090
|
+
position: absolute;
|
|
2091
|
+
right: 14px;
|
|
2092
|
+
bottom: 48px;
|
|
2093
|
+
font-size: 10px;
|
|
2094
|
+
color: var(--md-sys-color-outline, #64748b);
|
|
2095
|
+
white-space: nowrap;
|
|
2096
|
+
pointer-events: none;
|
|
2097
|
+
}
|
|
2098
|
+
/* A finger has no Ctrl or ⌘ - the send button is the way. */
|
|
2099
|
+
@media (pointer: coarse) {
|
|
2100
|
+
.send-shortcut {
|
|
2101
|
+
display: none;
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
.table-preview {
|
|
2105
|
+
flex: 1;
|
|
2106
|
+
min-height: 0;
|
|
2107
|
+
display: flex;
|
|
2108
|
+
flex-direction: column;
|
|
2109
|
+
margin: 4px 14px;
|
|
2110
|
+
border: 1px solid var(--md-sys-color-outline-variant, #e2e8f0);
|
|
2111
|
+
border-radius: 8px;
|
|
2112
|
+
background: var(--md-sys-color-surface, #ffffff);
|
|
2113
|
+
overflow: hidden;
|
|
2114
|
+
}
|
|
2115
|
+
.table-preview-head {
|
|
2116
|
+
display: flex;
|
|
2117
|
+
align-items: center;
|
|
2118
|
+
gap: 6px;
|
|
2119
|
+
padding: 6px 8px;
|
|
2120
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant, #e2e8f0);
|
|
2121
|
+
flex-wrap: wrap;
|
|
2122
|
+
}
|
|
2123
|
+
.table-preview-head > md-icon {
|
|
2124
|
+
--md-icon-size: 18px;
|
|
2125
|
+
color: var(--md-sys-color-primary, #0284c7);
|
|
2126
|
+
}
|
|
2127
|
+
.table-preview-name {
|
|
2128
|
+
font-weight: 500;
|
|
2129
|
+
color: var(--md-sys-color-on-surface, #0f172a);
|
|
2130
|
+
min-width: 0;
|
|
2131
|
+
overflow: hidden;
|
|
2132
|
+
text-overflow: ellipsis;
|
|
2133
|
+
white-space: nowrap;
|
|
2134
|
+
}
|
|
2135
|
+
.table-preview-shape {
|
|
2136
|
+
color: var(--md-sys-color-on-surface-variant, #475569);
|
|
2137
|
+
margin-right: auto;
|
|
2138
|
+
}
|
|
2139
|
+
.table-preview-body {
|
|
2140
|
+
flex: 1;
|
|
2141
|
+
min-height: 0;
|
|
2142
|
+
overflow: auto;
|
|
2143
|
+
}
|
|
2144
|
+
.table-preview-body table {
|
|
2145
|
+
border-collapse: collapse;
|
|
2146
|
+
font-size: 11px;
|
|
2147
|
+
}
|
|
2148
|
+
.table-preview-body th,
|
|
2149
|
+
.table-preview-body td {
|
|
2150
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant, #e2e8f0);
|
|
2151
|
+
padding: 4px 8px;
|
|
2152
|
+
text-align: left;
|
|
2153
|
+
white-space: nowrap;
|
|
2154
|
+
color: var(--md-sys-color-on-surface, #0f172a);
|
|
2155
|
+
}
|
|
2156
|
+
.table-preview-body th {
|
|
2157
|
+
position: sticky;
|
|
2158
|
+
left: 0;
|
|
2159
|
+
font-weight: 400;
|
|
2160
|
+
text-align: right;
|
|
2161
|
+
color: var(--md-sys-color-on-surface-variant, #475569);
|
|
2162
|
+
background: var(--md-sys-color-surface-container, #f1f5f9);
|
|
2163
|
+
}
|
|
2164
|
+
.attachment-open {
|
|
2165
|
+
all: unset;
|
|
2166
|
+
display: flex;
|
|
2167
|
+
align-items: center;
|
|
2168
|
+
gap: 6px;
|
|
2169
|
+
min-width: 0;
|
|
2170
|
+
flex: 1;
|
|
2171
|
+
cursor: pointer;
|
|
2172
|
+
border-radius: 6px;
|
|
2173
|
+
}
|
|
2174
|
+
.attachment-open:focus-visible {
|
|
2175
|
+
outline: 2px solid var(--md-sys-color-primary, #0284c7);
|
|
2176
|
+
outline-offset: 2px;
|
|
2177
|
+
}
|
|
2178
|
+
.attachment-chip.open {
|
|
2179
|
+
border-color: var(--md-sys-color-primary, #0284c7);
|
|
2180
|
+
}
|
|
2018
2181
|
.composer.drag-over {
|
|
2019
2182
|
background: var(--md-sys-color-surface-container, #f1f5f9);
|
|
2020
2183
|
border-top-color: var(--md-sys-color-primary, #0284c7);
|
|
@@ -2281,8 +2444,9 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2281
2444
|
font: inherit;
|
|
2282
2445
|
letter-spacing: inherit;
|
|
2283
2446
|
resize: none;
|
|
2284
|
-
|
|
2285
|
-
|
|
2447
|
+
/* The resting height of one line as it stood; the box grows from here by composerHeight (shared-dock.md §10). */
|
|
2448
|
+
box-sizing: border-box;
|
|
2449
|
+
min-height: 54px;
|
|
2286
2450
|
outline: none;
|
|
2287
2451
|
transition:
|
|
2288
2452
|
border-color 0.2s,
|
|
@@ -2386,6 +2550,15 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2386
2550
|
this.loadModeView();
|
|
2387
2551
|
if (this.subject)
|
|
2388
2552
|
this.onSubjectChanged();
|
|
2553
|
+
/*
|
|
2554
|
+
* Put back after the dock was closed: what is being written stays, the conversation is read again and its
|
|
2555
|
+
* broadcast and presence start again - they were stopped when it was taken out.
|
|
2556
|
+
*/
|
|
2557
|
+
if (this.hasUpdated && this.sessionId) {
|
|
2558
|
+
this.loadHistory();
|
|
2559
|
+
this._startPresence();
|
|
2560
|
+
this._startMessageSubscription();
|
|
2561
|
+
}
|
|
2389
2562
|
}
|
|
2390
2563
|
/**
|
|
2391
2564
|
* 화면이 보여 주는 것의 종류가 바뀌었다 — 설 수 있는 대화를 다시 묻고, 지난번 고른 것을 되살린다.
|
|
@@ -2402,6 +2575,8 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2402
2575
|
this.askHostForMode(kind);
|
|
2403
2576
|
}
|
|
2404
2577
|
updated(changed) {
|
|
2578
|
+
if (changed.has('input') || changed.has('composerWide') || changed.has('previewAttachmentId') || changed.has('attachments'))
|
|
2579
|
+
this.fitComposer();
|
|
2405
2580
|
/*
|
|
2406
2581
|
* 밖에서 넣어 준 질문을 자동 전송한다(오류 토스트의 "AI에게 묻기" 등).
|
|
2407
2582
|
*
|
|
@@ -2639,7 +2814,7 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2639
2814
|
${this.renderModeSubject()}
|
|
2640
2815
|
${this.renderSessionTabs()}
|
|
2641
2816
|
${this.renderRoster()}
|
|
2642
|
-
<div class="messages" @click=${this.onBubbleClick}>
|
|
2817
|
+
<div class="messages ${this.composerWide || this.previewedTable ? 'covered' : ''}" @click=${this.onBubbleClick}>
|
|
2643
2818
|
${!hasMessages ? this.renderEmpty() : nothing}
|
|
2644
2819
|
${(() => {
|
|
2645
2820
|
const myEmail = this._myEmail;
|
|
@@ -2763,7 +2938,7 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2763
2938
|
: nothing}
|
|
2764
2939
|
${this.renderFoldNotice()}
|
|
2765
2940
|
<div
|
|
2766
|
-
class="composer ${this.dragOver ? 'drag-over' : ''}"
|
|
2941
|
+
class="composer ${this.dragOver ? 'drag-over' : ''} ${this.composerWide ? 'wide' : ''} ${this.previewedTable ? 'previewing' : ''}"
|
|
2767
2942
|
@dragover=${(e) => this.onComposerDragOver(e)}
|
|
2768
2943
|
@dragleave=${(e) => this.onComposerDragLeave(e)}
|
|
2769
2944
|
@drop=${(e) => this.onComposerDrop(e)}
|
|
@@ -2815,20 +2990,43 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2815
2990
|
</button>
|
|
2816
2991
|
`
|
|
2817
2992
|
: nothing}
|
|
2993
|
+
<button
|
|
2994
|
+
type="button"
|
|
2995
|
+
class="action composer-size"
|
|
2996
|
+
aria-pressed=${this.composerWide ? 'true' : 'false'}
|
|
2997
|
+
@click=${() => this.setComposerWide(!this.composerWide)}>
|
|
2998
|
+
<md-icon>${this.composerWide ? 'close_fullscreen' : 'open_in_full'}</md-icon>
|
|
2999
|
+
${this.composerWide
|
|
3000
|
+
? i18next.t('ai-assistant.button.write-narrow', { defaultValue: 'Narrow' })
|
|
3001
|
+
: i18next.t('ai-assistant.button.write-wide', { defaultValue: 'Write wide' })}
|
|
3002
|
+
</button>
|
|
2818
3003
|
</div>
|
|
3004
|
+
${this.renderTablePreview()}
|
|
2819
3005
|
${this.examplesOpen ? this.renderInlineExamples() : nothing}
|
|
2820
3006
|
${this.attachments.length > 0
|
|
2821
3007
|
? html `
|
|
2822
3008
|
<div class="attachment-strip">
|
|
2823
3009
|
${this.attachments.map(att => html `
|
|
2824
|
-
<div class="attachment-chip" title=${att.name}>
|
|
2825
|
-
${att.
|
|
2826
|
-
? html `<
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
3010
|
+
<div class="attachment-chip ${att.id === this.previewAttachmentId ? 'open' : ''}" title=${att.name}>
|
|
3011
|
+
${att.table
|
|
3012
|
+
? html `<button
|
|
3013
|
+
type="button"
|
|
3014
|
+
class="attachment-open"
|
|
3015
|
+
aria-expanded=${att.id === this.previewAttachmentId ? 'true' : 'false'}
|
|
3016
|
+
@click=${() => (this.previewAttachmentId = att.id === this.previewAttachmentId ? null : att.id)}>
|
|
3017
|
+
<md-icon class="attachment-icon">table_chart</md-icon>
|
|
3018
|
+
<span class="attachment-meta">
|
|
3019
|
+
<span class="attachment-name">${att.name}</span>
|
|
3020
|
+
<span class="attachment-size">${this.tableShape(att.table)}</span>
|
|
3021
|
+
</span>
|
|
3022
|
+
</button>`
|
|
3023
|
+
: html `${att.type.startsWith('image/')
|
|
3024
|
+
? html `<img class="attachment-thumb" src=${att.url} alt=${att.name} />`
|
|
3025
|
+
: html `<md-icon class="attachment-icon">description</md-icon>`}
|
|
3026
|
+
<div class="attachment-meta">
|
|
3027
|
+
<span class="attachment-name">${att.name}</span>
|
|
3028
|
+
<span class="attachment-size">${formatFileSize(att.size)}</span>
|
|
3029
|
+
</div>`}
|
|
2832
3030
|
<button
|
|
2833
3031
|
type="button"
|
|
2834
3032
|
class="attachment-remove"
|
|
@@ -2898,10 +3096,16 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2898
3096
|
</button>
|
|
2899
3097
|
`
|
|
2900
3098
|
: nothing}
|
|
3099
|
+
${this.composerWide
|
|
3100
|
+
? html `<span class="send-shortcut">${i18next.t('ai-assistant.text.send-shortcut', { key: sendShortcut(navigator.platform), defaultValue: '{key} to send' })}</span>`
|
|
3101
|
+
: nothing}
|
|
2901
3102
|
<button
|
|
2902
3103
|
?disabled=${this.busy || !!this.sendBlocked || (!this.input.trim() && this.attachments.length === 0)}
|
|
2903
3104
|
@click=${this.send}
|
|
2904
|
-
title=${this.sendBlocked ||
|
|
3105
|
+
title=${this.sendBlocked ||
|
|
3106
|
+
(this.composerWide
|
|
3107
|
+
? i18next.t('ai-assistant.text.send-tooltip-wide', { key: sendShortcut(navigator.platform), defaultValue: 'Send ({key})' })
|
|
3108
|
+
: i18next.t('ai-assistant.text.send-tooltip'))}>
|
|
2905
3109
|
${this.busy
|
|
2906
3110
|
? html `<span class="spinner"></span>`
|
|
2907
3111
|
: html `<md-icon>arrow_upward</md-icon>`}
|
|
@@ -2986,6 +3190,56 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2986
3190
|
</div>
|
|
2987
3191
|
`;
|
|
2988
3192
|
}
|
|
3193
|
+
get previewedTable() {
|
|
3194
|
+
return this.previewAttachmentId ? this.attachments.find(a => a.id === this.previewAttachmentId && a.table) : undefined;
|
|
3195
|
+
}
|
|
3196
|
+
tableShape(table) {
|
|
3197
|
+
return i18next.t('ai-assistant.text.table-shape', { rows: table.rows.length, columns: table.columns, defaultValue: '{rows} rows × {columns} columns' });
|
|
3198
|
+
}
|
|
3199
|
+
/*
|
|
3200
|
+
The rows of a pasted table, over the conversation - numbered as the file numbers them, which is the number the
|
|
3201
|
+
entry card names. Every row: the paste is held under the mode's limit already.
|
|
3202
|
+
*/
|
|
3203
|
+
renderTablePreview() {
|
|
3204
|
+
const att = this.previewedTable;
|
|
3205
|
+
if (!att?.table)
|
|
3206
|
+
return nothing;
|
|
3207
|
+
const { rows, columns } = att.table;
|
|
3208
|
+
return html `
|
|
3209
|
+
<div
|
|
3210
|
+
class="table-preview"
|
|
3211
|
+
role="region"
|
|
3212
|
+
aria-label=${att.name}
|
|
3213
|
+
@keydown=${(e) => {
|
|
3214
|
+
if (e.key === 'Escape') {
|
|
3215
|
+
e.stopPropagation();
|
|
3216
|
+
this.previewAttachmentId = null;
|
|
3217
|
+
}
|
|
3218
|
+
}}>
|
|
3219
|
+
<div class="table-preview-head">
|
|
3220
|
+
<md-icon>table_chart</md-icon>
|
|
3221
|
+
<span class="table-preview-name">${att.name}</span>
|
|
3222
|
+
<span class="table-preview-shape">${this.tableShape(att.table)}</span>
|
|
3223
|
+
<button type="button" class="action" @click=${() => this.tableToWords(att.id)}>
|
|
3224
|
+
<md-icon>notes</md-icon>
|
|
3225
|
+
${i18next.t('ai-assistant.button.table-to-words', { defaultValue: 'Put in as words' })}
|
|
3226
|
+
</button>
|
|
3227
|
+
<button type="button" class="action" @click=${() => (this.previewAttachmentId = null)}>
|
|
3228
|
+
<md-icon>close</md-icon>
|
|
3229
|
+
${i18next.t('ai-assistant.button.close')}
|
|
3230
|
+
</button>
|
|
3231
|
+
</div>
|
|
3232
|
+
<div class="table-preview-body">
|
|
3233
|
+
<table>
|
|
3234
|
+
${rows.map((row, i) => html `<tr>
|
|
3235
|
+
<th scope="row">${i + 1}</th>
|
|
3236
|
+
${Array.from({ length: columns }, (_, c) => html `<td>${row[c] ?? ''}</td>`)}
|
|
3237
|
+
</tr>`)}
|
|
3238
|
+
</table>
|
|
3239
|
+
</div>
|
|
3240
|
+
</div>
|
|
3241
|
+
`;
|
|
3242
|
+
}
|
|
2989
3243
|
onKeyDown(e) {
|
|
2990
3244
|
/*
|
|
2991
3245
|
The Enter that ends a Korean (or Japanese, Chinese) syllable belongs to the input method - sending on it
|
|
@@ -3029,9 +3283,17 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
3029
3283
|
return;
|
|
3030
3284
|
}
|
|
3031
3285
|
}
|
|
3032
|
-
|
|
3286
|
+
const action = composerKeyAction(e, this.composerWide);
|
|
3287
|
+
if (action === 'narrow') {
|
|
3288
|
+
/* The Escape that narrows the box is spent here - the floating dock closes on the next one. */
|
|
3033
3289
|
e.preventDefault();
|
|
3034
|
-
|
|
3290
|
+
e.stopPropagation();
|
|
3291
|
+
this.setComposerWide(false);
|
|
3292
|
+
}
|
|
3293
|
+
else if (action === 'send') {
|
|
3294
|
+
e.preventDefault();
|
|
3295
|
+
if (!this.busy)
|
|
3296
|
+
this.send();
|
|
3035
3297
|
}
|
|
3036
3298
|
}
|
|
3037
3299
|
/** input 이벤트 — value 즉시 반영 + popup 갱신은 150ms 디바운스 (F-Finder 와 동일).
|
|
@@ -4198,6 +4460,9 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
4198
4460
|
if ((!text && attachmentsToSend.length === 0) || this.busy)
|
|
4199
4461
|
return;
|
|
4200
4462
|
this.attachments = [];
|
|
4463
|
+
/* What was written wide is sent to be answered - the conversation comes back to show it. */
|
|
4464
|
+
this.composerWide = false;
|
|
4465
|
+
this.previewAttachmentId = null;
|
|
4201
4466
|
const promptText = text || (attachmentsToSend.length > 0
|
|
4202
4467
|
? (attachmentsToSend[0].type.startsWith('image/')
|
|
4203
4468
|
? i18next.t('ai-assistant.text.attached-image-ask', { defaultValue: 'here is an image — go by it.' })
|
|
@@ -4574,6 +4839,14 @@ __decorate([
|
|
|
4574
4839
|
state(),
|
|
4575
4840
|
__metadata("design:type", Object)
|
|
4576
4841
|
], OxAssistantChat.prototype, "dragOver", void 0);
|
|
4842
|
+
__decorate([
|
|
4843
|
+
state(),
|
|
4844
|
+
__metadata("design:type", Object)
|
|
4845
|
+
], OxAssistantChat.prototype, "composerWide", void 0);
|
|
4846
|
+
__decorate([
|
|
4847
|
+
state(),
|
|
4848
|
+
__metadata("design:type", Object)
|
|
4849
|
+
], OxAssistantChat.prototype, "previewAttachmentId", void 0);
|
|
4577
4850
|
__decorate([
|
|
4578
4851
|
state(),
|
|
4579
4852
|
__metadata("design:type", Object)
|