@things-factory/ai-assistant 10.1.6
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/board-ai-chat.test.ts +149 -0
- package/client/components/board-ai-chat.ts +3228 -0
- package/client/components/chat-defaults.ts +27 -0
- package/client/components/chat-echo-dedup.test.ts +230 -0
- package/client/components/chat-echo-dedup.ts +155 -0
- package/client/components/chat-input-builder.ts +69 -0
- package/client/components/markdown.test.ts +518 -0
- package/client/components/markdown.ts +508 -0
- package/client/components/mention-popup-helpers.ts +433 -0
- package/client/components/mention-popup.test.ts +588 -0
- package/client/components/mention-popup.ts +238 -0
- package/client/index.ts +14 -0
- package/client/tsconfig.json +13 -0
- package/client/utils/board-edit-patch.ts +374 -0
- package/dist-client/components/board-ai-chat.d.ts +423 -0
- package/dist-client/components/board-ai-chat.js +3034 -0
- package/dist-client/components/board-ai-chat.js.map +1 -0
- package/dist-client/components/board-ai-chat.test.d.ts +1 -0
- package/dist-client/components/board-ai-chat.test.js +138 -0
- package/dist-client/components/board-ai-chat.test.js.map +1 -0
- package/dist-client/components/chat-defaults.d.ts +8 -0
- package/dist-client/components/chat-defaults.js +22 -0
- package/dist-client/components/chat-defaults.js.map +1 -0
- package/dist-client/components/chat-echo-dedup.d.ts +54 -0
- package/dist-client/components/chat-echo-dedup.js +115 -0
- package/dist-client/components/chat-echo-dedup.js.map +1 -0
- package/dist-client/components/chat-echo-dedup.test.d.ts +1 -0
- package/dist-client/components/chat-echo-dedup.test.js +203 -0
- package/dist-client/components/chat-echo-dedup.test.js.map +1 -0
- package/dist-client/components/chat-input-builder.d.ts +55 -0
- package/dist-client/components/chat-input-builder.js +32 -0
- package/dist-client/components/chat-input-builder.js.map +1 -0
- package/dist-client/components/markdown.d.ts +60 -0
- package/dist-client/components/markdown.js +422 -0
- package/dist-client/components/markdown.js.map +1 -0
- package/dist-client/components/markdown.test.d.ts +1 -0
- package/dist-client/components/markdown.test.js +418 -0
- package/dist-client/components/markdown.test.js.map +1 -0
- package/dist-client/components/mention-popup-helpers.d.ts +165 -0
- package/dist-client/components/mention-popup-helpers.js +330 -0
- package/dist-client/components/mention-popup-helpers.js.map +1 -0
- package/dist-client/components/mention-popup.d.ts +35 -0
- package/dist-client/components/mention-popup.js +223 -0
- package/dist-client/components/mention-popup.js.map +1 -0
- package/dist-client/components/mention-popup.test.d.ts +1 -0
- package/dist-client/components/mention-popup.test.js +474 -0
- package/dist-client/components/mention-popup.test.js.map +1 -0
- package/dist-client/index.d.ts +14 -0
- package/dist-client/index.js +15 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-client/utils/board-edit-patch.d.ts +157 -0
- package/dist-client/utils/board-edit-patch.js +284 -0
- package/dist-client/utils/board-edit-patch.js.map +1 -0
- package/dist-server/index.d.ts +28 -0
- package/dist-server/index.js +32 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/service/chat-message/chat-message-broadcast.d.ts +13 -0
- package/dist-server/service/chat-message/chat-message-broadcast.js +13 -0
- package/dist-server/service/chat-message/chat-message-broadcast.js.map +1 -0
- package/dist-server/service/chat-message/chat-message-publish.d.ts +6 -0
- package/dist-server/service/chat-message/chat-message-publish.js +27 -0
- package/dist-server/service/chat-message/chat-message-publish.js.map +1 -0
- package/dist-server/service/chat-message/chat-message-subscription.d.ts +8 -0
- package/dist-server/service/chat-message/chat-message-subscription.js +57 -0
- package/dist-server/service/chat-message/chat-message-subscription.js.map +1 -0
- package/dist-server/service/chat-message/chat-message.d.ts +68 -0
- package/dist-server/service/chat-message/chat-message.js +172 -0
- package/dist-server/service/chat-message/chat-message.js.map +1 -0
- package/dist-server/service/chat-message/fold-history.d.ts +43 -0
- package/dist-server/service/chat-message/fold-history.js +33 -0
- package/dist-server/service/chat-message/fold-history.js.map +1 -0
- package/dist-server/service/chat-message/history-summary.d.ts +43 -0
- package/dist-server/service/chat-message/history-summary.js +77 -0
- package/dist-server/service/chat-message/history-summary.js.map +1 -0
- package/dist-server/service/chat-message/index.d.ts +5 -0
- package/dist-server/service/chat-message/index.js +12 -0
- package/dist-server/service/chat-message/index.js.map +1 -0
- package/dist-server/service/chat-message/llm-history.d.ts +46 -0
- package/dist-server/service/chat-message/llm-history.js +87 -0
- package/dist-server/service/chat-message/llm-history.js.map +1 -0
- package/dist-server/service/chat-session/chat-session.d.ts +60 -0
- package/dist-server/service/chat-session/chat-session.js +159 -0
- package/dist-server/service/chat-session/chat-session.js.map +1 -0
- package/dist-server/service/chat-session/index.d.ts +3 -0
- package/dist-server/service/chat-session/index.js +7 -0
- package/dist-server/service/chat-session/index.js.map +1 -0
- package/dist-server/service/chat-session/session-activity-broadcast.d.ts +30 -0
- package/dist-server/service/chat-session/session-activity-broadcast.js +34 -0
- package/dist-server/service/chat-session/session-activity-broadcast.js.map +1 -0
- package/dist-server/service/chat-session/session-activity-publish.d.ts +12 -0
- package/dist-server/service/chat-session/session-activity-publish.js +37 -0
- package/dist-server/service/chat-session/session-activity-publish.js.map +1 -0
- package/dist-server/service/chat-session/session-activity-subscription.d.ts +14 -0
- package/dist-server/service/chat-session/session-activity-subscription.js +87 -0
- package/dist-server/service/chat-session/session-activity-subscription.js.map +1 -0
- package/dist-server/service/chat-session/session-anchor.d.ts +26 -0
- package/dist-server/service/chat-session/session-anchor.js +100 -0
- package/dist-server/service/chat-session/session-anchor.js.map +1 -0
- package/dist-server/service/chat-session/session-inbox.d.ts +80 -0
- package/dist-server/service/chat-session/session-inbox.js +133 -0
- package/dist-server/service/chat-session/session-inbox.js.map +1 -0
- package/dist-server/service/chat-session-participant/chat-session-participant.d.ts +49 -0
- package/dist-server/service/chat-session-participant/chat-session-participant.js +141 -0
- package/dist-server/service/chat-session-participant/chat-session-participant.js.map +1 -0
- package/dist-server/service/chat-session-participant/index.d.ts +4 -0
- package/dist-server/service/chat-session-participant/index.js +10 -0
- package/dist-server/service/chat-session-participant/index.js.map +1 -0
- package/dist-server/service/chat-session-participant/invite-notification.d.ts +27 -0
- package/dist-server/service/chat-session-participant/invite-notification.js +30 -0
- package/dist-server/service/chat-session-participant/invite-notification.js.map +1 -0
- package/dist-server/service/chat-session-participant/presence.d.ts +4 -0
- package/dist-server/service/chat-session-participant/presence.js +22 -0
- package/dist-server/service/chat-session-participant/presence.js.map +1 -0
- package/dist-server/service/chat-session-resolver.d.ts +93 -0
- package/dist-server/service/chat-session-resolver.js +706 -0
- package/dist-server/service/chat-session-resolver.js.map +1 -0
- package/dist-server/service/index.d.ts +24 -0
- package/dist-server/service/index.js +39 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/subscribe-auth.d.ts +14 -0
- package/dist-server/service/subscribe-auth.js +33 -0
- package/dist-server/service/subscribe-auth.js.map +1 -0
- package/dist-server/service/types.d.ts +5 -0
- package/dist-server/service/types.js +12 -0
- package/dist-server/service/types.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +48 -0
- package/server/index.ts +28 -0
- package/server/service/chat-message/chat-message-broadcast.integration.test.ts +73 -0
- package/server/service/chat-message/chat-message-broadcast.test.ts +40 -0
- package/server/service/chat-message/chat-message-broadcast.ts +27 -0
- package/server/service/chat-message/chat-message-publish.ts +22 -0
- package/server/service/chat-message/chat-message-subscription.ts +45 -0
- package/server/service/chat-message/chat-message.ts +195 -0
- package/server/service/chat-message/fold-history.test.ts +98 -0
- package/server/service/chat-message/fold-history.ts +77 -0
- package/server/service/chat-message/history-summary.test.ts +127 -0
- package/server/service/chat-message/history-summary.ts +100 -0
- package/server/service/chat-message/index.ts +12 -0
- package/server/service/chat-message/llm-history.test.ts +194 -0
- package/server/service/chat-message/llm-history.ts +163 -0
- package/server/service/chat-session/chat-session.ts +193 -0
- package/server/service/chat-session/index.ts +5 -0
- package/server/service/chat-session/session-activity-broadcast.ts +57 -0
- package/server/service/chat-session/session-activity-publish.ts +44 -0
- package/server/service/chat-session/session-activity-subscription.ts +67 -0
- package/server/service/chat-session/session-anchor.ts +106 -0
- package/server/service/chat-session/session-inbox.test.ts +185 -0
- package/server/service/chat-session/session-inbox.ts +161 -0
- package/server/service/chat-session-participant/chat-session-participant.ts +135 -0
- package/server/service/chat-session-participant/index.ts +6 -0
- package/server/service/chat-session-participant/invite-notification.test.ts +30 -0
- package/server/service/chat-session-participant/invite-notification.ts +48 -0
- package/server/service/chat-session-participant/presence.test.ts +39 -0
- package/server/service/chat-session-participant/presence.ts +21 -0
- package/server/service/chat-session-resolver.ts +644 -0
- package/server/service/index.ts +37 -0
- package/server/service/session-anchor.test.ts +133 -0
- package/server/service/subscribe-auth.test.ts +57 -0
- package/server/service/subscribe-auth.ts +34 -0
- package/server/service/types.ts +15 -0
- package/things-factory.config.js +1 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
/**
|
|
3
|
+
* <ox-mention-popup> — Mention 후보 리스트 popup overlay.
|
|
4
|
+
*
|
|
5
|
+
* board-ai-chat 의 textarea 위에 absolute 로 띄움. plain `<textarea>` + popup 분리 패턴
|
|
6
|
+
* (한국어 IME 함정 회피 — contenteditable 사용 안 함).
|
|
7
|
+
*
|
|
8
|
+
* 키보드 네비게이션은 호스트 (board-ai-chat) 가 textarea keydown 으로 처리하고
|
|
9
|
+
* activeIndex 만 동기화. 마우스 클릭은 popup 자체에서 처리.
|
|
10
|
+
*
|
|
11
|
+
* Pure helper (extractMentionContext / filterMentionCandidates 등) 는
|
|
12
|
+
* `mention-popup-helpers.ts` 에 분리 — 단위 테스트 가능.
|
|
13
|
+
*/
|
|
14
|
+
import { LitElement, css, html } from 'lit';
|
|
15
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
16
|
+
export { buildMentionCandidates, buildCatalogCandidates, buildSlashCandidates, buildUserCandidates, extractMentionContext, filterMentionCandidates, applyMentionSelection, resolveMentionToken } from './mention-popup-helpers.js';
|
|
17
|
+
/** match type 라벨 — things-scene SearchOverlay 의 MATCH_TYPE_LABELS 와 동일. */
|
|
18
|
+
const MATCH_TYPE_LABEL = {
|
|
19
|
+
id: 'ID',
|
|
20
|
+
name: 'Name',
|
|
21
|
+
tag: 'Tag',
|
|
22
|
+
type: 'Type',
|
|
23
|
+
class: 'Class',
|
|
24
|
+
text: 'Text',
|
|
25
|
+
data: 'Data',
|
|
26
|
+
property: 'Prop',
|
|
27
|
+
description: 'Desc'
|
|
28
|
+
};
|
|
29
|
+
let OxMentionPopup = class OxMentionPopup extends LitElement {
|
|
30
|
+
constructor() {
|
|
31
|
+
super(...arguments);
|
|
32
|
+
this.results = [];
|
|
33
|
+
this.activeIndex = 0;
|
|
34
|
+
/** filter cap 후 표시 안 된 추가 매칭 수 ("...and N more" 표기용) */
|
|
35
|
+
this.overflow = 0;
|
|
36
|
+
}
|
|
37
|
+
static { this.styles = css `
|
|
38
|
+
:host {
|
|
39
|
+
display: block;
|
|
40
|
+
position: absolute;
|
|
41
|
+
z-index: 1000;
|
|
42
|
+
background: var(--md-sys-color-surface, #ffffff);
|
|
43
|
+
border: 1px solid var(--md-sys-color-outline-variant, #e2e8f0);
|
|
44
|
+
border-radius: 6px;
|
|
45
|
+
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.10);
|
|
46
|
+
max-height: 220px;
|
|
47
|
+
overflow-y: auto;
|
|
48
|
+
/* 폭은 호스트가 left/right 로 결정. */
|
|
49
|
+
}
|
|
50
|
+
.empty {
|
|
51
|
+
padding: 8px 10px;
|
|
52
|
+
color: var(--md-sys-color-outline, #94a3b8);
|
|
53
|
+
font-size: 11px;
|
|
54
|
+
}
|
|
55
|
+
/* dense 한 단일 행 — scene Finder 톤. label / hint / type-chip inline. */
|
|
56
|
+
.item {
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
gap: 6px;
|
|
60
|
+
padding: 4px 8px;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
font-size: 11.5px;
|
|
63
|
+
line-height: 1.4;
|
|
64
|
+
color: var(--md-sys-color-on-surface, #0f172a);
|
|
65
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant, #f1f5f9);
|
|
66
|
+
}
|
|
67
|
+
.item:last-child {
|
|
68
|
+
border-bottom: 0;
|
|
69
|
+
}
|
|
70
|
+
.item:hover {
|
|
71
|
+
background: var(--md-sys-color-surface-container, #f8fafc);
|
|
72
|
+
}
|
|
73
|
+
.item.active {
|
|
74
|
+
background: var(--md-sys-color-primary-container, rgba(37, 99, 235, 0.10));
|
|
75
|
+
}
|
|
76
|
+
.match-badge {
|
|
77
|
+
flex-shrink: 0;
|
|
78
|
+
background: var(--md-sys-color-primary-container, rgba(37, 99, 235, 0.15));
|
|
79
|
+
color: var(--md-sys-color-on-primary-container, #1e40af);
|
|
80
|
+
padding: 0 5px;
|
|
81
|
+
border-radius: 3px;
|
|
82
|
+
font-size: 9px;
|
|
83
|
+
font-weight: 600;
|
|
84
|
+
letter-spacing: 0.04em;
|
|
85
|
+
min-width: 32px;
|
|
86
|
+
text-align: center;
|
|
87
|
+
}
|
|
88
|
+
/* badge 가 비어있을 때 (matchType=label) — 자리는 reserve 해서 모든 행 정렬 일치 */
|
|
89
|
+
.match-badge.invisible {
|
|
90
|
+
visibility: hidden;
|
|
91
|
+
}
|
|
92
|
+
.label {
|
|
93
|
+
flex: 1;
|
|
94
|
+
min-width: 0;
|
|
95
|
+
font-weight: 500;
|
|
96
|
+
overflow: hidden;
|
|
97
|
+
text-overflow: ellipsis;
|
|
98
|
+
white-space: nowrap;
|
|
99
|
+
color: var(--md-sys-color-on-surface, #0f172a);
|
|
100
|
+
}
|
|
101
|
+
.hint {
|
|
102
|
+
font-size: 10px;
|
|
103
|
+
color: var(--md-sys-color-outline, #94a3b8);
|
|
104
|
+
flex-shrink: 0;
|
|
105
|
+
white-space: nowrap;
|
|
106
|
+
max-width: 45%;
|
|
107
|
+
overflow: hidden;
|
|
108
|
+
text-overflow: ellipsis;
|
|
109
|
+
}
|
|
110
|
+
.type {
|
|
111
|
+
flex-shrink: 0;
|
|
112
|
+
background: var(--md-sys-color-surface-container, #f1f5f9);
|
|
113
|
+
padding: 0 5px;
|
|
114
|
+
border-radius: 3px;
|
|
115
|
+
font-size: 9.5px;
|
|
116
|
+
color: var(--md-sys-color-outline, #94a3b8);
|
|
117
|
+
letter-spacing: 0.02em;
|
|
118
|
+
}
|
|
119
|
+
/* 상태 footer — 'N matches' 카운터 + cap 초과 안내 */
|
|
120
|
+
.status {
|
|
121
|
+
padding: 3px 8px;
|
|
122
|
+
font-size: 9.5px;
|
|
123
|
+
color: var(--md-sys-color-outline, #94a3b8);
|
|
124
|
+
border-top: 1px solid var(--md-sys-color-outline-variant, #e2e8f0);
|
|
125
|
+
background: var(--md-sys-color-surface-container-low, #fafbfc);
|
|
126
|
+
display: flex;
|
|
127
|
+
justify-content: space-between;
|
|
128
|
+
gap: 8px;
|
|
129
|
+
}
|
|
130
|
+
.more {
|
|
131
|
+
color: var(--md-sys-color-outline, #94a3b8);
|
|
132
|
+
}
|
|
133
|
+
`; }
|
|
134
|
+
render() {
|
|
135
|
+
if (this.results.length === 0) {
|
|
136
|
+
return html `<div class="empty">매칭 없음</div>`;
|
|
137
|
+
}
|
|
138
|
+
const totalShown = this.results.length;
|
|
139
|
+
const totalAll = totalShown + this.overflow;
|
|
140
|
+
return html `
|
|
141
|
+
${this.results.map((r, i) => {
|
|
142
|
+
// 행 layout:
|
|
143
|
+
// [matchType-badge] [type-chip] [label] [secondary hint when 의미 있을 때]
|
|
144
|
+
// - label = 항상 사람-친화적 1차 식별 (component=id, user=name, catalog=name 등)
|
|
145
|
+
// - secondary hint = matchValue 가 label 과 다른 경우 (예: tag/text 매칭) 또는
|
|
146
|
+
// user 의 email 같은 부가 정보. 없으면 생략.
|
|
147
|
+
const matchType = r.matchType;
|
|
148
|
+
const badgeText = matchType ? MATCH_TYPE_LABEL[matchType] ?? '' : '';
|
|
149
|
+
const label = r.candidate.label;
|
|
150
|
+
// matchValue 가 label 과 같으면 redundant 라 hint 안 함
|
|
151
|
+
const matchHint = r.matchValue && r.matchValue !== label ? r.matchValue : '';
|
|
152
|
+
// user 후보이고 hint 가 비어있으면 email 을 hint 로 (보조 식별)
|
|
153
|
+
const fallbackHint = !matchHint && r.candidate.type === 'user' && r.candidate.email !== label
|
|
154
|
+
? r.candidate.email ?? ''
|
|
155
|
+
: '';
|
|
156
|
+
const hint = matchHint || fallbackHint;
|
|
157
|
+
return html `
|
|
158
|
+
<div
|
|
159
|
+
class="item ${i === this.activeIndex ? 'active' : ''}"
|
|
160
|
+
@mouseenter=${() => this._setActive(i)}
|
|
161
|
+
@mousedown=${(e) => this._select(e, r.candidate)}>
|
|
162
|
+
<span class="match-badge ${badgeText ? '' : 'invisible'}"
|
|
163
|
+
>${badgeText || '·'}</span
|
|
164
|
+
>
|
|
165
|
+
<span class="type">${r.candidate.type}</span>
|
|
166
|
+
<span class="label">${label}</span>
|
|
167
|
+
${hint ? html `<span class="hint">${hint}</span>` : ''}
|
|
168
|
+
</div>
|
|
169
|
+
`;
|
|
170
|
+
})}
|
|
171
|
+
<div class="status">
|
|
172
|
+
<span>${totalShown} / ${totalAll} matches</span>
|
|
173
|
+
${this.overflow > 0
|
|
174
|
+
? html `<span class="more">+${this.overflow} more — narrow query</span>`
|
|
175
|
+
: ''}
|
|
176
|
+
</div>
|
|
177
|
+
`;
|
|
178
|
+
}
|
|
179
|
+
_select(e, candidate) {
|
|
180
|
+
e.preventDefault(); // textarea blur 방지
|
|
181
|
+
this.dispatchEvent(new CustomEvent('mention-select', {
|
|
182
|
+
detail: { candidate },
|
|
183
|
+
bubbles: true,
|
|
184
|
+
composed: true
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
187
|
+
/** 마우스 hover 로 active 이동 — host 가 받아 keyboard nav 와 동일하게 처리. */
|
|
188
|
+
_setActive(index) {
|
|
189
|
+
if (index === this.activeIndex)
|
|
190
|
+
return;
|
|
191
|
+
this.dispatchEvent(new CustomEvent('mention-active-change', {
|
|
192
|
+
detail: { index },
|
|
193
|
+
bubbles: true,
|
|
194
|
+
composed: true
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
/** activeIndex 가 바뀌면 해당 항목을 view 로 스크롤 — popup 의 overflow-y:auto
|
|
198
|
+
* 영역에서 키보드 네비 시 active 가 가려지지 않도록. */
|
|
199
|
+
updated(changed) {
|
|
200
|
+
if (changed.has('activeIndex') || changed.has('results')) {
|
|
201
|
+
const items = this.renderRoot.querySelectorAll('.item');
|
|
202
|
+
const active = items[this.activeIndex];
|
|
203
|
+
active?.scrollIntoView({ block: 'nearest' });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
__decorate([
|
|
208
|
+
property({ type: Array }),
|
|
209
|
+
__metadata("design:type", Array)
|
|
210
|
+
], OxMentionPopup.prototype, "results", void 0);
|
|
211
|
+
__decorate([
|
|
212
|
+
property({ type: Number }),
|
|
213
|
+
__metadata("design:type", Object)
|
|
214
|
+
], OxMentionPopup.prototype, "activeIndex", void 0);
|
|
215
|
+
__decorate([
|
|
216
|
+
property({ type: Number }),
|
|
217
|
+
__metadata("design:type", Object)
|
|
218
|
+
], OxMentionPopup.prototype, "overflow", void 0);
|
|
219
|
+
OxMentionPopup = __decorate([
|
|
220
|
+
customElement('ox-mention-popup')
|
|
221
|
+
], OxMentionPopup);
|
|
222
|
+
export { OxMentionPopup };
|
|
223
|
+
//# sourceMappingURL=mention-popup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mention-popup.js","sourceRoot":"","sources":["../../client/components/mention-popup.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAO3D,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACrB,mBAAmB,EAIpB,MAAM,4BAA4B,CAAA;AAEnC,2EAA2E;AAC3E,MAAM,gBAAgB,GAA2B;IAC/C,EAAE,EAAE,IAAI;IACR,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,MAAM;IAChB,WAAW,EAAE,MAAM;CACpB,CAAA;AAGM,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,UAAU;IAAvC;;QACsB,YAAO,GAAmB,EAAE,CAAA;QAC3B,gBAAW,GAAG,CAAC,CAAA;QAC3C,wDAAwD;QAC5B,aAAQ,GAAG,CAAC,CAAA;IAmL1C,CAAC;aAjLQ,WAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgGlB,AAhGY,CAgGZ;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAA,gCAAgC,CAAA;QAC7C,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAA;QACtC,MAAM,QAAQ,GAAG,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAA;QAC3C,OAAO,IAAI,CAAA;QACP,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAC1B,YAAY;YACZ,wEAAwE;YACxE,sEAAsE;YACtE,oEAAoE;YACpE,mCAAmC;YACnC,MAAM,SAAS,GAAG,CAAC,CAAC,SAAS,CAAA;YAC7B,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;YACpE,MAAM,KAAK,GAAG,CAAC,CAAC,SAAS,CAAC,KAAK,CAAA;YAC/B,gDAAgD;YAChD,MAAM,SAAS,GAAG,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAA;YAC5E,gDAAgD;YAChD,MAAM,YAAY,GAChB,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,SAAS,CAAC,KAAK,KAAK,KAAK;gBACtE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE;gBACzB,CAAC,CAAC,EAAE,CAAA;YACR,MAAM,IAAI,GAAG,SAAS,IAAI,YAAY,CAAA;YACtC,OAAO,IAAI,CAAA;;0BAEO,CAAC,KAAK,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;0BACtC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;yBACzB,CAAC,CAAa,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;uCACjC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW;iBAClD,SAAS,IAAI,GAAG;;iCAEA,CAAC,CAAC,SAAS,CAAC,IAAI;kCACf,KAAK;cACzB,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA,sBAAsB,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE;;SAExD,CAAA;QACH,CAAC,CAAC;;gBAEQ,UAAU,MAAM,QAAQ;UAC9B,IAAI,CAAC,QAAQ,GAAG,CAAC;YACjB,CAAC,CAAC,IAAI,CAAA,uBAAuB,IAAI,CAAC,QAAQ,6BAA6B;YACvE,CAAC,CAAC,EAAE;;KAET,CAAA;IACH,CAAC;IAEO,OAAO,CAAC,CAAa,EAAE,SAA2B;QACxD,CAAC,CAAC,cAAc,EAAE,CAAA,CAAC,mBAAmB;QACtC,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,gBAAgB,EAAE;YAChC,MAAM,EAAE,EAAE,SAAS,EAAE;YACrB,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;SACf,CAAC,CACH,CAAA;IACH,CAAC;IAED,gEAAgE;IACxD,UAAU,CAAC,KAAa;QAC9B,IAAI,KAAK,KAAK,IAAI,CAAC,WAAW;YAAE,OAAM;QACtC,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,uBAAuB,EAAE;YACvC,MAAM,EAAE,EAAE,KAAK,EAAE;YACjB,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;SACf,CAAC,CACH,CAAA;IACH,CAAC;IAED;2CACuC;IAC7B,OAAO,CAAC,OAA6B;QAC7C,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACzD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAA;YACvD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,CAA4B,CAAA;YACjE,MAAM,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;QAC9C,CAAC;IACH,CAAC;;AArL0B;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;;+CAA6B;AAC3B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;mDAAgB;AAEf;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;gDAAa;AAJ7B,cAAc;IAD1B,aAAa,CAAC,kBAAkB,CAAC;GACrB,cAAc,CAuL1B","sourcesContent":["/**\n * <ox-mention-popup> — Mention 후보 리스트 popup overlay.\n *\n * board-ai-chat 의 textarea 위에 absolute 로 띄움. plain `<textarea>` + popup 분리 패턴\n * (한국어 IME 함정 회피 — contenteditable 사용 안 함).\n *\n * 키보드 네비게이션은 호스트 (board-ai-chat) 가 textarea keydown 으로 처리하고\n * activeIndex 만 동기화. 마우스 클릭은 popup 자체에서 처리.\n *\n * Pure helper (extractMentionContext / filterMentionCandidates 등) 는\n * `mention-popup-helpers.ts` 에 분리 — 단위 테스트 가능.\n */\nimport { LitElement, css, html } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport type { FilterResult, MentionCandidate } from './mention-popup-helpers.js'\n\nexport type {\n MentionCandidate,\n FilterResult\n} from './mention-popup-helpers.js'\nexport {\n buildMentionCandidates,\n buildCatalogCandidates,\n buildSlashCandidates,\n buildUserCandidates,\n extractMentionContext,\n filterMentionCandidates,\n applyMentionSelection,\n resolveMentionToken,\n type CatalogEntry,\n type SlashTemplate,\n type ResolveMentionTokenOptions\n} from './mention-popup-helpers.js'\n\n/** match type 라벨 — things-scene SearchOverlay 의 MATCH_TYPE_LABELS 와 동일. */\nconst MATCH_TYPE_LABEL: Record<string, string> = {\n id: 'ID',\n name: 'Name',\n tag: 'Tag',\n type: 'Type',\n class: 'Class',\n text: 'Text',\n data: 'Data',\n property: 'Prop',\n description: 'Desc'\n}\n\n@customElement('ox-mention-popup')\nexport class OxMentionPopup extends LitElement {\n @property({ type: Array }) results: FilterResult[] = []\n @property({ type: Number }) activeIndex = 0\n /** filter cap 후 표시 안 된 추가 매칭 수 (\"...and N more\" 표기용) */\n @property({ type: Number }) overflow = 0\n\n static styles = css`\n :host {\n display: block;\n position: absolute;\n z-index: 1000;\n background: var(--md-sys-color-surface, #ffffff);\n border: 1px solid var(--md-sys-color-outline-variant, #e2e8f0);\n border-radius: 6px;\n box-shadow: 0 6px 18px rgba(0, 0, 0, 0.10);\n max-height: 220px;\n overflow-y: auto;\n /* 폭은 호스트가 left/right 로 결정. */\n }\n .empty {\n padding: 8px 10px;\n color: var(--md-sys-color-outline, #94a3b8);\n font-size: 11px;\n }\n /* dense 한 단일 행 — scene Finder 톤. label / hint / type-chip inline. */\n .item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px;\n cursor: pointer;\n font-size: 11.5px;\n line-height: 1.4;\n color: var(--md-sys-color-on-surface, #0f172a);\n border-bottom: 1px solid var(--md-sys-color-outline-variant, #f1f5f9);\n }\n .item:last-child {\n border-bottom: 0;\n }\n .item:hover {\n background: var(--md-sys-color-surface-container, #f8fafc);\n }\n .item.active {\n background: var(--md-sys-color-primary-container, rgba(37, 99, 235, 0.10));\n }\n .match-badge {\n flex-shrink: 0;\n background: var(--md-sys-color-primary-container, rgba(37, 99, 235, 0.15));\n color: var(--md-sys-color-on-primary-container, #1e40af);\n padding: 0 5px;\n border-radius: 3px;\n font-size: 9px;\n font-weight: 600;\n letter-spacing: 0.04em;\n min-width: 32px;\n text-align: center;\n }\n /* badge 가 비어있을 때 (matchType=label) — 자리는 reserve 해서 모든 행 정렬 일치 */\n .match-badge.invisible {\n visibility: hidden;\n }\n .label {\n flex: 1;\n min-width: 0;\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--md-sys-color-on-surface, #0f172a);\n }\n .hint {\n font-size: 10px;\n color: var(--md-sys-color-outline, #94a3b8);\n flex-shrink: 0;\n white-space: nowrap;\n max-width: 45%;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .type {\n flex-shrink: 0;\n background: var(--md-sys-color-surface-container, #f1f5f9);\n padding: 0 5px;\n border-radius: 3px;\n font-size: 9.5px;\n color: var(--md-sys-color-outline, #94a3b8);\n letter-spacing: 0.02em;\n }\n /* 상태 footer — 'N matches' 카운터 + cap 초과 안내 */\n .status {\n padding: 3px 8px;\n font-size: 9.5px;\n color: var(--md-sys-color-outline, #94a3b8);\n border-top: 1px solid var(--md-sys-color-outline-variant, #e2e8f0);\n background: var(--md-sys-color-surface-container-low, #fafbfc);\n display: flex;\n justify-content: space-between;\n gap: 8px;\n }\n .more {\n color: var(--md-sys-color-outline, #94a3b8);\n }\n `\n\n render() {\n if (this.results.length === 0) {\n return html`<div class=\"empty\">매칭 없음</div>`\n }\n const totalShown = this.results.length\n const totalAll = totalShown + this.overflow\n return html`\n ${this.results.map((r, i) => {\n // 행 layout:\n // [matchType-badge] [type-chip] [label] [secondary hint when 의미 있을 때]\n // - label = 항상 사람-친화적 1차 식별 (component=id, user=name, catalog=name 등)\n // - secondary hint = matchValue 가 label 과 다른 경우 (예: tag/text 매칭) 또는\n // user 의 email 같은 부가 정보. 없으면 생략.\n const matchType = r.matchType\n const badgeText = matchType ? MATCH_TYPE_LABEL[matchType] ?? '' : ''\n const label = r.candidate.label\n // matchValue 가 label 과 같으면 redundant 라 hint 안 함\n const matchHint = r.matchValue && r.matchValue !== label ? r.matchValue : ''\n // user 후보이고 hint 가 비어있으면 email 을 hint 로 (보조 식별)\n const fallbackHint =\n !matchHint && r.candidate.type === 'user' && r.candidate.email !== label\n ? r.candidate.email ?? ''\n : ''\n const hint = matchHint || fallbackHint\n return html`\n <div\n class=\"item ${i === this.activeIndex ? 'active' : ''}\"\n @mouseenter=${() => this._setActive(i)}\n @mousedown=${(e: MouseEvent) => this._select(e, r.candidate)}>\n <span class=\"match-badge ${badgeText ? '' : 'invisible'}\"\n >${badgeText || '·'}</span\n >\n <span class=\"type\">${r.candidate.type}</span>\n <span class=\"label\">${label}</span>\n ${hint ? html`<span class=\"hint\">${hint}</span>` : ''}\n </div>\n `\n })}\n <div class=\"status\">\n <span>${totalShown} / ${totalAll} matches</span>\n ${this.overflow > 0\n ? html`<span class=\"more\">+${this.overflow} more — narrow query</span>`\n : ''}\n </div>\n `\n }\n\n private _select(e: MouseEvent, candidate: MentionCandidate) {\n e.preventDefault() // textarea blur 방지\n this.dispatchEvent(\n new CustomEvent('mention-select', {\n detail: { candidate },\n bubbles: true,\n composed: true\n })\n )\n }\n\n /** 마우스 hover 로 active 이동 — host 가 받아 keyboard nav 와 동일하게 처리. */\n private _setActive(index: number) {\n if (index === this.activeIndex) return\n this.dispatchEvent(\n new CustomEvent('mention-active-change', {\n detail: { index },\n bubbles: true,\n composed: true\n })\n )\n }\n\n /** activeIndex 가 바뀌면 해당 항목을 view 로 스크롤 — popup 의 overflow-y:auto\n * 영역에서 키보드 네비 시 active 가 가려지지 않도록. */\n protected updated(changed: Map<string, unknown>) {\n if (changed.has('activeIndex') || changed.has('results')) {\n const items = this.renderRoot.querySelectorAll('.item')\n const active = items[this.activeIndex] as HTMLElement | undefined\n active?.scrollIntoView({ block: 'nearest' })\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'ox-mention-popup': OxMentionPopup\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|