@things-factory/ai-assistant 10.1.108 → 10.1.110
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 +299 -19
- 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 +301 -19
- package/dist-client/components/assistant-chat.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/package.json +2 -2
- package/test/composer-frame.test.ts +109 -0
- package/test/entry-card-styles.test.ts +18 -0
- package/test/pasted-table.test.ts +8 -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
|
}
|
|
@@ -1671,6 +1712,42 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
1671
1712
|
color: inherit;
|
|
1672
1713
|
}
|
|
1673
1714
|
|
|
1715
|
+
/* The entry card's way to the large review - a quiet action under its table, never a second run button. */
|
|
1716
|
+
.workspace-review {
|
|
1717
|
+
display: flex;
|
|
1718
|
+
align-items: center;
|
|
1719
|
+
gap: 5px;
|
|
1720
|
+
margin: 6px 0 0 auto;
|
|
1721
|
+
padding: 5px 11px 5px 9px;
|
|
1722
|
+
border: 1px solid transparent;
|
|
1723
|
+
border-radius: 999px;
|
|
1724
|
+
background: transparent;
|
|
1725
|
+
color: var(--md-sys-color-primary, #0f766e);
|
|
1726
|
+
font-family: inherit;
|
|
1727
|
+
font-size: 12px;
|
|
1728
|
+
font-weight: 600;
|
|
1729
|
+
letter-spacing: 0.01em;
|
|
1730
|
+
cursor: pointer;
|
|
1731
|
+
transition:
|
|
1732
|
+
background-color 0.14s ease-in-out,
|
|
1733
|
+
border-color 0.14s ease-in-out;
|
|
1734
|
+
}
|
|
1735
|
+
.workspace-review md-icon {
|
|
1736
|
+
--md-icon-size: 15px;
|
|
1737
|
+
color: inherit;
|
|
1738
|
+
}
|
|
1739
|
+
.workspace-review:hover {
|
|
1740
|
+
background: color-mix(in srgb, var(--md-sys-color-primary, #0f766e) 8%, transparent);
|
|
1741
|
+
border-color: color-mix(in srgb, var(--md-sys-color-primary, #0f766e) 24%, transparent);
|
|
1742
|
+
}
|
|
1743
|
+
.workspace-review:active {
|
|
1744
|
+
background: color-mix(in srgb, var(--md-sys-color-primary, #0f766e) 14%, transparent);
|
|
1745
|
+
}
|
|
1746
|
+
.workspace-review:focus-visible {
|
|
1747
|
+
outline: 2px solid var(--md-sys-color-primary, #0f766e);
|
|
1748
|
+
outline-offset: 1px;
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1674
1751
|
/* ── 접지 경고 — 근거에 없는 대상을 지목한 답 ───── */
|
|
1675
1752
|
.grounding-warning {
|
|
1676
1753
|
display: flex;
|
|
@@ -1979,6 +2056,115 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
1979
2056
|
flex-direction: column;
|
|
1980
2057
|
transition: background 0.15s, border-color 0.15s;
|
|
1981
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
|
+
.send-shortcut {
|
|
2086
|
+
font-size: 10px;
|
|
2087
|
+
color: var(--md-sys-color-outline, #64748b);
|
|
2088
|
+
white-space: nowrap;
|
|
2089
|
+
padding-bottom: 10px;
|
|
2090
|
+
}
|
|
2091
|
+
.table-preview {
|
|
2092
|
+
flex: 1;
|
|
2093
|
+
min-height: 0;
|
|
2094
|
+
display: flex;
|
|
2095
|
+
flex-direction: column;
|
|
2096
|
+
margin: 4px 14px;
|
|
2097
|
+
border: 1px solid var(--md-sys-color-outline-variant, #e2e8f0);
|
|
2098
|
+
border-radius: 8px;
|
|
2099
|
+
background: var(--md-sys-color-surface, #ffffff);
|
|
2100
|
+
overflow: hidden;
|
|
2101
|
+
}
|
|
2102
|
+
.table-preview-head {
|
|
2103
|
+
display: flex;
|
|
2104
|
+
align-items: center;
|
|
2105
|
+
gap: 6px;
|
|
2106
|
+
padding: 6px 8px;
|
|
2107
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant, #e2e8f0);
|
|
2108
|
+
flex-wrap: wrap;
|
|
2109
|
+
}
|
|
2110
|
+
.table-preview-head > md-icon {
|
|
2111
|
+
--md-icon-size: 18px;
|
|
2112
|
+
color: var(--md-sys-color-primary, #0284c7);
|
|
2113
|
+
}
|
|
2114
|
+
.table-preview-name {
|
|
2115
|
+
font-weight: 500;
|
|
2116
|
+
color: var(--md-sys-color-on-surface, #0f172a);
|
|
2117
|
+
min-width: 0;
|
|
2118
|
+
overflow: hidden;
|
|
2119
|
+
text-overflow: ellipsis;
|
|
2120
|
+
white-space: nowrap;
|
|
2121
|
+
}
|
|
2122
|
+
.table-preview-shape {
|
|
2123
|
+
color: var(--md-sys-color-on-surface-variant, #475569);
|
|
2124
|
+
margin-right: auto;
|
|
2125
|
+
}
|
|
2126
|
+
.table-preview-body {
|
|
2127
|
+
flex: 1;
|
|
2128
|
+
min-height: 0;
|
|
2129
|
+
overflow: auto;
|
|
2130
|
+
}
|
|
2131
|
+
.table-preview-body table {
|
|
2132
|
+
border-collapse: collapse;
|
|
2133
|
+
font-size: 11px;
|
|
2134
|
+
}
|
|
2135
|
+
.table-preview-body th,
|
|
2136
|
+
.table-preview-body td {
|
|
2137
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant, #e2e8f0);
|
|
2138
|
+
padding: 4px 8px;
|
|
2139
|
+
text-align: left;
|
|
2140
|
+
white-space: nowrap;
|
|
2141
|
+
color: var(--md-sys-color-on-surface, #0f172a);
|
|
2142
|
+
}
|
|
2143
|
+
.table-preview-body th {
|
|
2144
|
+
position: sticky;
|
|
2145
|
+
left: 0;
|
|
2146
|
+
font-weight: 400;
|
|
2147
|
+
text-align: right;
|
|
2148
|
+
color: var(--md-sys-color-on-surface-variant, #475569);
|
|
2149
|
+
background: var(--md-sys-color-surface-container, #f1f5f9);
|
|
2150
|
+
}
|
|
2151
|
+
.attachment-open {
|
|
2152
|
+
all: unset;
|
|
2153
|
+
display: flex;
|
|
2154
|
+
align-items: center;
|
|
2155
|
+
gap: 6px;
|
|
2156
|
+
min-width: 0;
|
|
2157
|
+
flex: 1;
|
|
2158
|
+
cursor: pointer;
|
|
2159
|
+
border-radius: 6px;
|
|
2160
|
+
}
|
|
2161
|
+
.attachment-open:focus-visible {
|
|
2162
|
+
outline: 2px solid var(--md-sys-color-primary, #0284c7);
|
|
2163
|
+
outline-offset: 2px;
|
|
2164
|
+
}
|
|
2165
|
+
.attachment-chip.open {
|
|
2166
|
+
border-color: var(--md-sys-color-primary, #0284c7);
|
|
2167
|
+
}
|
|
1982
2168
|
.composer.drag-over {
|
|
1983
2169
|
background: var(--md-sys-color-surface-container, #f1f5f9);
|
|
1984
2170
|
border-top-color: var(--md-sys-color-primary, #0284c7);
|
|
@@ -2245,8 +2431,9 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2245
2431
|
font: inherit;
|
|
2246
2432
|
letter-spacing: inherit;
|
|
2247
2433
|
resize: none;
|
|
2248
|
-
|
|
2249
|
-
|
|
2434
|
+
/* The resting height of one line as it stood; the box grows from here by composerHeight (shared-dock.md §10). */
|
|
2435
|
+
box-sizing: border-box;
|
|
2436
|
+
min-height: 54px;
|
|
2250
2437
|
outline: none;
|
|
2251
2438
|
transition:
|
|
2252
2439
|
border-color 0.2s,
|
|
@@ -2366,6 +2553,8 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2366
2553
|
this.askHostForMode(kind);
|
|
2367
2554
|
}
|
|
2368
2555
|
updated(changed) {
|
|
2556
|
+
if (changed.has('input') || changed.has('composerWide') || changed.has('previewAttachmentId') || changed.has('attachments'))
|
|
2557
|
+
this.fitComposer();
|
|
2369
2558
|
/*
|
|
2370
2559
|
* 밖에서 넣어 준 질문을 자동 전송한다(오류 토스트의 "AI에게 묻기" 등).
|
|
2371
2560
|
*
|
|
@@ -2603,7 +2792,7 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2603
2792
|
${this.renderModeSubject()}
|
|
2604
2793
|
${this.renderSessionTabs()}
|
|
2605
2794
|
${this.renderRoster()}
|
|
2606
|
-
<div class="messages" @click=${this.onBubbleClick}>
|
|
2795
|
+
<div class="messages ${this.composerWide || this.previewedTable ? 'covered' : ''}" @click=${this.onBubbleClick}>
|
|
2607
2796
|
${!hasMessages ? this.renderEmpty() : nothing}
|
|
2608
2797
|
${(() => {
|
|
2609
2798
|
const myEmail = this._myEmail;
|
|
@@ -2727,7 +2916,7 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2727
2916
|
: nothing}
|
|
2728
2917
|
${this.renderFoldNotice()}
|
|
2729
2918
|
<div
|
|
2730
|
-
class="composer ${this.dragOver ? 'drag-over' : ''}"
|
|
2919
|
+
class="composer ${this.dragOver ? 'drag-over' : ''} ${this.composerWide ? 'wide' : ''} ${this.previewedTable ? 'previewing' : ''}"
|
|
2731
2920
|
@dragover=${(e) => this.onComposerDragOver(e)}
|
|
2732
2921
|
@dragleave=${(e) => this.onComposerDragLeave(e)}
|
|
2733
2922
|
@drop=${(e) => this.onComposerDrop(e)}
|
|
@@ -2779,20 +2968,43 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2779
2968
|
</button>
|
|
2780
2969
|
`
|
|
2781
2970
|
: nothing}
|
|
2971
|
+
<button
|
|
2972
|
+
type="button"
|
|
2973
|
+
class="action composer-size"
|
|
2974
|
+
aria-pressed=${this.composerWide ? 'true' : 'false'}
|
|
2975
|
+
@click=${() => this.setComposerWide(!this.composerWide)}>
|
|
2976
|
+
<md-icon>${this.composerWide ? 'close_fullscreen' : 'open_in_full'}</md-icon>
|
|
2977
|
+
${this.composerWide
|
|
2978
|
+
? i18next.t('ai-assistant.button.write-narrow', { defaultValue: 'Narrow' })
|
|
2979
|
+
: i18next.t('ai-assistant.button.write-wide', { defaultValue: 'Write wide' })}
|
|
2980
|
+
</button>
|
|
2782
2981
|
</div>
|
|
2982
|
+
${this.renderTablePreview()}
|
|
2783
2983
|
${this.examplesOpen ? this.renderInlineExamples() : nothing}
|
|
2784
2984
|
${this.attachments.length > 0
|
|
2785
2985
|
? html `
|
|
2786
2986
|
<div class="attachment-strip">
|
|
2787
2987
|
${this.attachments.map(att => html `
|
|
2788
|
-
<div class="attachment-chip" title=${att.name}>
|
|
2789
|
-
${att.
|
|
2790
|
-
? html `<
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2988
|
+
<div class="attachment-chip ${att.id === this.previewAttachmentId ? 'open' : ''}" title=${att.name}>
|
|
2989
|
+
${att.table
|
|
2990
|
+
? html `<button
|
|
2991
|
+
type="button"
|
|
2992
|
+
class="attachment-open"
|
|
2993
|
+
aria-expanded=${att.id === this.previewAttachmentId ? 'true' : 'false'}
|
|
2994
|
+
@click=${() => (this.previewAttachmentId = att.id === this.previewAttachmentId ? null : att.id)}>
|
|
2995
|
+
<md-icon class="attachment-icon">table_chart</md-icon>
|
|
2996
|
+
<span class="attachment-meta">
|
|
2997
|
+
<span class="attachment-name">${att.name}</span>
|
|
2998
|
+
<span class="attachment-size">${this.tableShape(att.table)}</span>
|
|
2999
|
+
</span>
|
|
3000
|
+
</button>`
|
|
3001
|
+
: html `${att.type.startsWith('image/')
|
|
3002
|
+
? html `<img class="attachment-thumb" src=${att.url} alt=${att.name} />`
|
|
3003
|
+
: html `<md-icon class="attachment-icon">description</md-icon>`}
|
|
3004
|
+
<div class="attachment-meta">
|
|
3005
|
+
<span class="attachment-name">${att.name}</span>
|
|
3006
|
+
<span class="attachment-size">${formatFileSize(att.size)}</span>
|
|
3007
|
+
</div>`}
|
|
2796
3008
|
<button
|
|
2797
3009
|
type="button"
|
|
2798
3010
|
class="attachment-remove"
|
|
@@ -2862,10 +3074,16 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2862
3074
|
</button>
|
|
2863
3075
|
`
|
|
2864
3076
|
: nothing}
|
|
3077
|
+
${this.composerWide
|
|
3078
|
+
? html `<span class="send-shortcut">${i18next.t('ai-assistant.text.send-shortcut', { key: sendShortcut(navigator.platform), defaultValue: '{key} to send' })}</span>`
|
|
3079
|
+
: nothing}
|
|
2865
3080
|
<button
|
|
2866
3081
|
?disabled=${this.busy || !!this.sendBlocked || (!this.input.trim() && this.attachments.length === 0)}
|
|
2867
3082
|
@click=${this.send}
|
|
2868
|
-
title=${this.sendBlocked ||
|
|
3083
|
+
title=${this.sendBlocked ||
|
|
3084
|
+
(this.composerWide
|
|
3085
|
+
? i18next.t('ai-assistant.text.send-tooltip-wide', { key: sendShortcut(navigator.platform), defaultValue: 'Send ({key})' })
|
|
3086
|
+
: i18next.t('ai-assistant.text.send-tooltip'))}>
|
|
2869
3087
|
${this.busy
|
|
2870
3088
|
? html `<span class="spinner"></span>`
|
|
2871
3089
|
: html `<md-icon>arrow_upward</md-icon>`}
|
|
@@ -2950,7 +3168,54 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2950
3168
|
</div>
|
|
2951
3169
|
`;
|
|
2952
3170
|
}
|
|
3171
|
+
get previewedTable() {
|
|
3172
|
+
return this.previewAttachmentId ? this.attachments.find(a => a.id === this.previewAttachmentId && a.table) : undefined;
|
|
3173
|
+
}
|
|
3174
|
+
tableShape(table) {
|
|
3175
|
+
return i18next.t('ai-assistant.text.table-shape', { rows: table.rows.length, columns: table.columns, defaultValue: '{rows} rows × {columns} columns' });
|
|
3176
|
+
}
|
|
3177
|
+
/*
|
|
3178
|
+
The rows of a pasted table, over the conversation - numbered as the file numbers them, which is the number the
|
|
3179
|
+
entry card names. Every row: the paste is held under the mode's limit already.
|
|
3180
|
+
*/
|
|
3181
|
+
renderTablePreview() {
|
|
3182
|
+
const att = this.previewedTable;
|
|
3183
|
+
if (!att?.table)
|
|
3184
|
+
return nothing;
|
|
3185
|
+
const { rows, columns } = att.table;
|
|
3186
|
+
return html `
|
|
3187
|
+
<div class="table-preview" role="region" aria-label=${att.name}>
|
|
3188
|
+
<div class="table-preview-head">
|
|
3189
|
+
<md-icon>table_chart</md-icon>
|
|
3190
|
+
<span class="table-preview-name">${att.name}</span>
|
|
3191
|
+
<span class="table-preview-shape">${this.tableShape(att.table)}</span>
|
|
3192
|
+
<button type="button" class="action" @click=${() => this.tableToWords(att.id)}>
|
|
3193
|
+
<md-icon>notes</md-icon>
|
|
3194
|
+
${i18next.t('ai-assistant.button.table-to-words', { defaultValue: 'Put in as words' })}
|
|
3195
|
+
</button>
|
|
3196
|
+
<button type="button" class="action" @click=${() => (this.previewAttachmentId = null)}>
|
|
3197
|
+
<md-icon>close</md-icon>
|
|
3198
|
+
${i18next.t('ai-assistant.button.close')}
|
|
3199
|
+
</button>
|
|
3200
|
+
</div>
|
|
3201
|
+
<div class="table-preview-body">
|
|
3202
|
+
<table>
|
|
3203
|
+
${rows.map((row, i) => html `<tr>
|
|
3204
|
+
<th scope="row">${i + 1}</th>
|
|
3205
|
+
${Array.from({ length: columns }, (_, c) => html `<td>${row[c] ?? ''}</td>`)}
|
|
3206
|
+
</tr>`)}
|
|
3207
|
+
</table>
|
|
3208
|
+
</div>
|
|
3209
|
+
</div>
|
|
3210
|
+
`;
|
|
3211
|
+
}
|
|
2953
3212
|
onKeyDown(e) {
|
|
3213
|
+
/*
|
|
3214
|
+
The Enter that ends a Korean (or Japanese, Chinese) syllable belongs to the input method - sending on it
|
|
3215
|
+
sends the line without its last syllable, which then lands in the emptied box.
|
|
3216
|
+
*/
|
|
3217
|
+
if (e.isComposing || e.keyCode === 229)
|
|
3218
|
+
return;
|
|
2954
3219
|
// Mention popup 활성 시 — 화살표/Enter/Tab/Esc 가로채서 popup 네비게이션
|
|
2955
3220
|
if (this.mentionOpen) {
|
|
2956
3221
|
if (e.key === 'ArrowDown') {
|
|
@@ -2987,9 +3252,15 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
2987
3252
|
return;
|
|
2988
3253
|
}
|
|
2989
3254
|
}
|
|
2990
|
-
|
|
3255
|
+
const action = composerKeyAction(e, this.composerWide);
|
|
3256
|
+
if (action === 'narrow') {
|
|
2991
3257
|
e.preventDefault();
|
|
2992
|
-
this.
|
|
3258
|
+
this.setComposerWide(false);
|
|
3259
|
+
}
|
|
3260
|
+
else if (action === 'send') {
|
|
3261
|
+
e.preventDefault();
|
|
3262
|
+
if (!this.busy)
|
|
3263
|
+
this.send();
|
|
2993
3264
|
}
|
|
2994
3265
|
}
|
|
2995
3266
|
/** input 이벤트 — value 즉시 반영 + popup 갱신은 150ms 디바운스 (F-Finder 와 동일).
|
|
@@ -3630,7 +3901,7 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
3630
3901
|
${entry ? html `<ox-assistant-entry-review .proposal=${p}></ox-assistant-entry-review>` : nothing}
|
|
3631
3902
|
${entry && this.sessionId && offersWorkspaceReview(p)
|
|
3632
3903
|
? html `<button
|
|
3633
|
-
class="
|
|
3904
|
+
class="workspace-review"
|
|
3634
3905
|
@click=${() => this.dispatchEvent(new CustomEvent('assistant-open-page', { detail: { href: entryReviewAddress(this.sessionId, key) }, bubbles: true, composed: true }))}
|
|
3635
3906
|
>
|
|
3636
3907
|
<md-icon>open_in_full</md-icon>
|
|
@@ -4156,6 +4427,9 @@ let OxAssistantChat = class OxAssistantChat extends LitElement {
|
|
|
4156
4427
|
if ((!text && attachmentsToSend.length === 0) || this.busy)
|
|
4157
4428
|
return;
|
|
4158
4429
|
this.attachments = [];
|
|
4430
|
+
/* What was written wide is sent to be answered - the conversation comes back to show it. */
|
|
4431
|
+
this.composerWide = false;
|
|
4432
|
+
this.previewAttachmentId = null;
|
|
4159
4433
|
const promptText = text || (attachmentsToSend.length > 0
|
|
4160
4434
|
? (attachmentsToSend[0].type.startsWith('image/')
|
|
4161
4435
|
? i18next.t('ai-assistant.text.attached-image-ask', { defaultValue: 'here is an image — go by it.' })
|
|
@@ -4532,6 +4806,14 @@ __decorate([
|
|
|
4532
4806
|
state(),
|
|
4533
4807
|
__metadata("design:type", Object)
|
|
4534
4808
|
], OxAssistantChat.prototype, "dragOver", void 0);
|
|
4809
|
+
__decorate([
|
|
4810
|
+
state(),
|
|
4811
|
+
__metadata("design:type", Object)
|
|
4812
|
+
], OxAssistantChat.prototype, "composerWide", void 0);
|
|
4813
|
+
__decorate([
|
|
4814
|
+
state(),
|
|
4815
|
+
__metadata("design:type", Object)
|
|
4816
|
+
], OxAssistantChat.prototype, "previewAttachmentId", void 0);
|
|
4535
4817
|
__decorate([
|
|
4536
4818
|
state(),
|
|
4537
4819
|
__metadata("design:type", Object)
|