chore: test deploy snapshot

This commit is contained in:
2026-05-28 16:11:33 +09:00
parent b1bec9cb6f
commit a97d933cff
2 changed files with 163 additions and 128 deletions

View File

@@ -2463,11 +2463,57 @@
.app-chat-preview-card--prompt .app-chat-preview-card__actions { .app-chat-preview-card--prompt .app-chat-preview-card__actions {
flex: 0 0 auto; flex: 0 0 auto;
min-width: fit-content; min-width: 0;
max-width: 100%;
flex-wrap: nowrap; flex-wrap: nowrap;
margin-left: auto; margin-left: auto;
} }
@media (max-width: 720px) {
.app-chat-preview-card--prompt .app-chat-preview-card__header {
align-items: flex-start;
flex-wrap: wrap;
}
.app-chat-preview-card--prompt .app-chat-preview-card__actions {
width: 100%;
flex: 1 1 100%;
justify-content: flex-start;
flex-wrap: wrap;
gap: 6px;
margin-left: 0;
}
.app-chat-prompt-card__submitted {
max-width: 100%;
white-space: normal;
overflow-wrap: anywhere;
word-break: break-word;
}
.app-chat-prompt-card__selection-pill {
max-width: 100%;
white-space: normal;
overflow-wrap: anywhere;
word-break: break-word;
}
.app-chat-prompt-card__step-header {
flex-wrap: wrap;
}
.app-chat-prompt-card__preview-toolbar {
flex-wrap: wrap;
align-items: flex-start;
justify-content: flex-start;
min-height: 0;
}
.app-chat-prompt-card__preview-title.ant-typography {
flex: 1 1 100%;
}
}
.app-chat-prompt-card__preview-modal.ant-modal { .app-chat-prompt-card__preview-modal.ant-modal {
background: background:
linear-gradient(180deg, rgba(248, 250, 252, 0.98), rgba(226, 232, 240, 0.98)), linear-gradient(180deg, rgba(248, 250, 252, 0.98), rgba(226, 232, 240, 0.98)),

View File

@@ -173,6 +173,9 @@ type ShareSearchResult = {
value: string; value: string;
}; };
}; };
type ShareSearchIndexedResult = ShareSearchResult & {
searchText: string;
};
type ShareSearchPanelMode = 'all' | 'apps'; type ShareSearchPanelMode = 'all' | 'apps';
type ShareWorkServerVersionStatus = 'latest' | 'unknown' | 'update-available' | 'build-required'; type ShareWorkServerVersionStatus = 'latest' | 'unknown' | 'update-available' | 'build-required';
type ClientNotificationPermissionState = 'unsupported' | 'default' | 'granted' | 'denied'; type ClientNotificationPermissionState = 'unsupported' | 'default' | 'granted' | 'denied';
@@ -1923,6 +1926,10 @@ function matchesSearchKeyword(keyword: string, ...values: Array<string | null |
return values.some((value) => normalizeSearchKeyword(value ?? '').includes(keyword)); return values.some((value) => normalizeSearchKeyword(value ?? '').includes(keyword));
} }
function buildSearchKeywordText(...values: Array<string | null | undefined>) {
return normalizeSearchKeyword(values.filter(Boolean).join(' '));
}
function buildSharePromptSelectionKey(parentRequestId: string, sourceMessageId: number, promptIndex: number, promptTitle: string, promptSignature: string) { function buildSharePromptSelectionKey(parentRequestId: string, sourceMessageId: number, promptIndex: number, promptTitle: string, promptSignature: string) {
return [parentRequestId.trim(), sourceMessageId, promptIndex, promptTitle.trim(), promptSignature.trim()].join('::'); return [parentRequestId.trim(), sourceMessageId, promptIndex, promptTitle.trim(), promptSignature.trim()].join('::');
} }
@@ -8379,24 +8386,131 @@ export function ChatSharePage() {
? renderEmbeddedSharePlayApp(programTarget.appId, closeProgramTarget, normalizedToken) ? renderEmbeddedSharePlayApp(programTarget.appId, closeProgramTarget, normalizedToken)
: null; : null;
const isServerCommandDrawerOpen = programTarget?.appId === 'server-command'; const isServerCommandDrawerOpen = programTarget?.appId === 'server-command';
const searchResults = useMemo<ShareSearchResult[]>(() => { const indexedContentSearchResults = useMemo<ShareSearchIndexedResult[]>(() => {
const keyword = normalizeSearchKeyword(searchKeyword); const results: ShareSearchIndexedResult[] = [];
const results: ShareSearchResult[] = [];
if (searchPanelMode === 'apps') { sortedRequests.forEach((request) => {
const requestText = buildShareVisibleText(request.userText);
results.push({
key: `request:${request.requestId}`,
title: requestText || '질문',
description: `질문 · ${formatTimeLabel(request.createdAt)}`,
category: 'request',
requestId: request.requestId,
scrollTarget: {
type: 'request',
value: request.requestId,
},
searchText: buildSearchKeywordText(requestText, request.statusMessage, request.requestId),
});
buildSharePreviewItemsFromText(request.userText, normalizedToken).forEach((item) => {
results.push({
key: `request-resource:${request.requestId}:${item.id}`,
title: item.label,
description: `질문 리소스 · ${item.kind}`,
category: 'resource',
requestId: request.requestId,
resource: {
key: `request-resource:${request.requestId}:${item.id}`,
label: item.label,
url: item.url,
kind: item.kind,
meta: `${item.kind} resource`,
},
scrollTarget: {
type: 'request',
value: request.requestId,
},
searchText: buildSearchKeywordText(item.label, item.url, item.kind),
});
});
});
sortedMessages.forEach((entry) => {
const payload = messageRenderPayloadById.get(entry.id);
const visibleText = buildVisibleMessageText(entry, payload);
const requestId = entry.clientRequestId?.trim() || snapshot?.rootRequestId?.trim() || '';
results.push({
key: `response:${entry.id}`,
title: visibleText || '응답',
description: `${entry.author === 'user' ? '질문 메시지' : '응답'} · ${formatTimeLabel(entry.timestamp)}`,
category: 'response',
requestId: requestId || undefined,
scrollTarget: {
type: 'response',
value: String(entry.id),
},
searchText: buildSearchKeywordText(visibleText, entry.author, requestId),
});
(payload?.promptParts ?? []).forEach((prompt, promptIndex) => {
results.push({
key: `prompt:${entry.id}:${promptIndex}`,
title: prompt.title || 'prompt',
description: `prompt · ${formatTimeLabel(entry.timestamp)}`,
category: 'response',
requestId: requestId || undefined,
scrollTarget: {
type: 'prompt',
value: buildSharePromptAnchorKey(entry.id, promptIndex),
},
searchText: buildSearchKeywordText(buildSharePromptSearchText(prompt), requestId, String(entry.id)),
});
});
(payload?.previewItems ?? []).forEach((item) => {
if (!requestId) {
return;
}
results.push({
key: `response-resource:${entry.id}:${item.id}`,
title: item.label,
description: `응답 리소스 · ${item.kind}`,
category: 'resource',
requestId,
resource: {
key: `response-resource:${entry.id}:${item.id}`,
label: item.label,
url: resolveShareScopedResourceUrl(item.url, normalizedToken),
kind: item.kind,
meta: `${item.kind} resource`,
},
scrollTarget: {
type: 'response',
value: String(entry.id),
},
searchText: buildSearchKeywordText(item.label, item.url, item.kind),
});
});
});
(snapshot?.activityLogs ?? []).forEach((activity, index) => {
const summary = summarizeActivityLogLines(activity.lines ?? []).join(' ');
results.push({
key: `activity:${activity.requestId}:${index}`,
title: summary || `활동 로그 ${index + 1}`,
description: `활동 로그 · ${activity.requestId}`,
category: 'activity',
requestId: activity.requestId,
scrollTarget: {
type: 'activity',
value: 'chat-share-activity-panel',
},
searchText: buildSearchKeywordText(activity.requestId, summary),
});
});
return results.filter((item) => !item.scrollTarget || item.scrollTarget.value.trim());
}, [messageRenderPayloadById, normalizedToken, snapshot?.activityLogs, snapshot?.rootRequestId, sortedMessages, sortedRequests]);
const indexedAppSearchResults = useMemo<ShareSearchIndexedResult[]>(() => {
const results: ShareSearchIndexedResult[] = [];
const photoprismLauncher = buildPhotoPrismProgramTarget(); const photoprismLauncher = buildPhotoPrismProgramTarget();
if (
matchesSearchKeyword(
keyword,
currentShareChatTarget.label,
currentShareChatTarget.appId,
'공유채팅',
'현재 토큰',
'현재 공유토큰 열기',
...APPS_LAUNCHER_SEARCH_TERMS,
)
) {
results.push({ results.push({
key: `management-app:${SHARE_CURRENT_CHAT_APP_ID}`, key: `management-app:${SHARE_CURRENT_CHAT_APP_ID}`,
title: currentShareChatTarget.label, title: currentShareChatTarget.label,
@@ -8405,14 +8519,17 @@ export function ChatSharePage() {
icon: <CommentOutlined />, icon: <CommentOutlined />,
usageBadge: resolveShareAppUsageBadge(appLaunchUsage[SHARE_CURRENT_CHAT_APP_ID]), usageBadge: resolveShareAppUsageBadge(appLaunchUsage[SHARE_CURRENT_CHAT_APP_ID]),
resource: currentShareChatTarget, resource: currentShareChatTarget,
searchText: buildSearchKeywordText(
currentShareChatTarget.label,
currentShareChatTarget.appId,
'공유채팅',
'현재 토큰',
'현재 공유토큰 열기',
...APPS_LAUNCHER_SEARCH_TERMS,
),
}); });
}
sortedAllowedManagementApps.forEach((item) => { sortedAllowedManagementApps.forEach((item) => {
if (!matchesSearchKeyword(keyword, item.value, item.label, item.description, ...APPS_LAUNCHER_SEARCH_TERMS)) {
return;
}
results.push({ results.push({
key: `management-app:${item.value}`, key: `management-app:${item.value}`,
title: item.label, title: item.label,
@@ -8421,23 +8538,11 @@ export function ChatSharePage() {
icon: item.icon, icon: item.icon,
usageBadge: resolveShareAppUsageBadge(appLaunchUsage[item.value]), usageBadge: resolveShareAppUsageBadge(appLaunchUsage[item.value]),
resource: buildShareManagementProgramTarget(item.value, item.label), resource: buildShareManagementProgramTarget(item.value, item.label),
searchText: buildSearchKeywordText(item.value, item.label, item.description, ...APPS_LAUNCHER_SEARCH_TERMS),
}); });
}); });
sortedAllowedPlayAppEntries.forEach((entry) => { sortedAllowedPlayAppEntries.forEach((entry) => {
if (
!matchesSearchKeyword(
keyword,
entry.id,
entry.name,
entry.searchDescription,
...APPS_LAUNCHER_SEARCH_TERMS,
...(entry.searchKeywords ?? []),
)
) {
return;
}
results.push({ results.push({
key: `app:${entry.id}`, key: `app:${entry.id}`,
title: entry.name, title: entry.name,
@@ -8450,148 +8555,32 @@ export function ChatSharePage() {
entry.id === 'photoprism' entry.id === 'photoprism'
? photoprismLauncher ? photoprismLauncher
: buildPlayAppProgramTarget(entry.id, entry.name), : buildPlayAppProgramTarget(entry.id, entry.name),
searchText: buildSearchKeywordText(
entry.id,
entry.name,
entry.searchDescription,
...APPS_LAUNCHER_SEARCH_TERMS,
...(entry.searchKeywords ?? []),
),
}); });
}); });
return results; return results;
}, [appLaunchUsage, currentShareChatTarget, selectedAppEnvironment, sortedAllowedManagementApps, sortedAllowedPlayAppEntries]);
const searchResults = useMemo<ShareSearchResult[]>(() => {
const keyword = normalizeSearchKeyword(searchKeyword);
const source = searchPanelMode === 'apps' ? indexedAppSearchResults : indexedContentSearchResults;
if (searchPanelMode !== 'apps' && !keyword) {
return [];
} }
if (!keyword) { const filtered = keyword
return results; ? source.filter((item) => item.searchText.includes(keyword))
} : source;
sortedRequests.forEach((request) => { return filtered.slice(0, 40).map(({ searchText: _searchText, ...result }) => result);
const requestText = buildShareVisibleText(request.userText); }, [indexedAppSearchResults, indexedContentSearchResults, searchKeyword, searchPanelMode]);
if (matchesSearchKeyword(keyword, requestText, request.statusMessage, request.requestId)) {
results.push({
key: `request:${request.requestId}`,
title: requestText || '질문',
description: `질문 · ${formatTimeLabel(request.createdAt)}`,
category: 'request',
requestId: request.requestId,
scrollTarget: {
type: 'request',
value: request.requestId,
},
});
}
buildSharePreviewItemsFromText(request.userText, normalizedToken).forEach((item) => {
if (!matchesSearchKeyword(keyword, item.label, item.url, item.kind)) {
return;
}
const scopedUrl = resolveShareScopedResourceUrl(item.url, normalizedToken);
results.push({
key: `request-resource:${request.requestId}:${item.id}`,
title: item.label,
description: `질문 리소스 · ${item.kind}`,
category: 'resource',
requestId: request.requestId,
resource: {
key: `request-resource:${request.requestId}:${item.id}`,
label: item.label,
url: scopedUrl,
kind: item.kind,
meta: `${item.kind} resource`,
},
scrollTarget: {
type: 'request',
value: request.requestId,
},
});
});
});
sortedMessages.forEach((entry) => {
const payload = messageRenderPayloadById.get(entry.id);
const visibleText = buildVisibleMessageText(entry, payload);
const requestId = entry.clientRequestId?.trim() || snapshot?.rootRequestId?.trim() || '';
if (matchesSearchKeyword(keyword, visibleText, entry.author, requestId)) {
results.push({
key: `response:${entry.id}`,
title: visibleText || '응답',
description: `${entry.author === 'user' ? '질문 메시지' : '응답'} · ${formatTimeLabel(entry.timestamp)}`,
category: 'response',
requestId: requestId || undefined,
scrollTarget: {
type: 'response',
value: String(entry.id),
},
});
}
(payload?.promptParts ?? []).forEach((prompt, promptIndex) => {
if (!matchesSearchKeyword(keyword, buildSharePromptSearchText(prompt), requestId, entry.id)) {
return;
}
results.push({
key: `prompt:${entry.id}:${promptIndex}`,
title: prompt.title || 'prompt',
description: `prompt · ${formatTimeLabel(entry.timestamp)}`,
category: 'response',
requestId: requestId || undefined,
scrollTarget: {
type: 'prompt',
value: buildSharePromptAnchorKey(entry.id, promptIndex),
},
});
});
(payload?.previewItems ?? []).forEach((item) => {
if (!requestId || !matchesSearchKeyword(keyword, item.label, item.url, item.kind)) {
return;
}
const scopedUrl = resolveShareScopedResourceUrl(item.url, normalizedToken);
results.push({
key: `response-resource:${entry.id}:${item.id}`,
title: item.label,
description: `응답 리소스 · ${item.kind}`,
category: 'resource',
requestId,
resource: {
key: `response-resource:${entry.id}:${item.id}`,
label: item.label,
url: scopedUrl,
kind: item.kind,
meta: `${item.kind} resource`,
},
scrollTarget: {
type: 'response',
value: String(entry.id),
},
});
});
});
(snapshot?.activityLogs ?? []).forEach((activity, index) => {
const summary = summarizeActivityLogLines(activity.lines ?? []).join(' ');
if (!matchesSearchKeyword(keyword, activity.requestId, summary)) {
return;
}
results.push({
key: `activity:${activity.requestId}:${index}`,
title: summary || `활동 로그 ${index + 1}`,
description: `활동 로그 · ${activity.requestId}`,
category: 'activity',
requestId: activity.requestId,
scrollTarget: {
type: 'activity',
value: 'chat-share-activity-panel',
},
});
});
return results
.filter((item) => !item.scrollTarget || item.scrollTarget.value.trim())
.slice(0, 40);
}, [appLaunchUsage, currentShareChatTarget, messageRenderPayloadById, normalizedToken, searchKeyword, searchPanelMode, selectedAppEnvironment, snapshot?.activityLogs, snapshot?.rootRequestId, sortedAllowedManagementApps, sortedAllowedPlayAppEntries, sortedMessages, sortedRequests]);
const selectedTokenUsageSetting = shareTokenSetting; const selectedTokenUsageSetting = shareTokenSetting;
const tokenUsageSummaryByPeriod = useMemo( const tokenUsageSummaryByPeriod = useMemo(
() => () =>