issue-sticker 0.10.0 → 0.12.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/CHANGELOG.md +60 -0
- package/dist/_commonjsHelpers-DWwsNxpa.js +8 -0
- package/dist/{externalUserPayload-DNGAp9bP.js → externalUserPayload-DWcXyz2U.js} +2538 -473
- package/dist/index-DA6SoSp7.js +3009 -0
- package/dist/issue-sticker-headless-react.js +3 -3
- package/dist/issue-sticker-headless.js +136 -74
- package/dist/issue-sticker-react.js +1489 -642
- package/dist/{three-_bdgDAmI.js → three-B228j4M6.js} +11 -11
- package/dist/{tooltip-UijEbdDX.js → tooltip-CT7e9xbU.js} +1 -1
- package/headless.d.ts +67 -0
- package/package.json +2 -1
|
@@ -15,35 +15,92 @@ var __privateWrapper = (obj, member, setter, getter) => ({
|
|
|
15
15
|
}
|
|
16
16
|
});
|
|
17
17
|
var _focused, _cleanup, _setup, _b, _provider, _providerCalled, _c, _online, _cleanup2, _setup2, _d, _gcTimeout, _e, _queryType, _initialState, _revertState, _cache, _client, _retryer, _defaultOptions, _abortSignalConsumed, _Query_instances, isInitialPausedFetch_fn, dispatch_fn, _f, _client2, _currentQuery, _currentQueryInitialState, _currentResult, _currentResultState, _currentResultOptions, _currentThenable, _selectError, _selectFn, _selectResult, _lastQueryWithDefinedData, _staleTimeoutId, _refetchIntervalId, _currentRefetchInterval, _trackedProps, _QueryObserver_instances, executeFetch_fn, updateStaleTimeout_fn, computeRefetchInterval_fn, updateRefetchInterval_fn, updateTimers_fn, clearStaleTimeout_fn, clearRefetchInterval_fn, updateQuery_fn, notify_fn, _g, _client3, _observers, _mutationCache, _retryer2, _Mutation_instances, dispatch_fn2, _h, _mutations, _scopes, _mutationId, _i, _client4, _currentResult2, _currentMutation, _mutateOptions, _MutationObserver_instances, updateResult_fn, notify_fn2, _j, _queries, _k, _queryCache, _mutationCache2, _defaultOptions2, _queryDefaults, _mutationDefaults, _mountCount, _unsubscribeFocus, _unsubscribeOnline, _l, _m;
|
|
18
|
-
import { O as OVERLAY_LAYER_INDEX, c as createLucideIcon$1, d as usePortalContainer, a as cn$1, e as OVERLAY_LAYER, s as stopEscapePropagation, P as Presence,
|
|
19
|
-
import {
|
|
18
|
+
import { O as OVERLAY_LAYER_INDEX, c as createLucideIcon$1, d as usePortalContainer, a as cn$1, e as OVERLAY_LAYER, s as stopEscapePropagation, f as useControllableState, g as useId$1, P as Presence, h as Primitive, i as composeEventHandlers, j as useDismissableLayerSurface, k as useComposedRefs$1, l as Portal$1, m as createContextScope, n as createSlot, D as DismissableLayer, o as useLayoutEffect2, p as useCallbackRef, u as ungzip_1, q as PortalContainerProvider, r as registerThree } from "./three-B228j4M6.js";
|
|
19
|
+
import { L as toast, M as trimLogBackupPayload, O as createSafeLogBackup, P as getLogBackupSessionId, N as NetworkCollector, C as ConsoleCollector, W as WebSocketCollector, Q as createIndexedDbLogBackup, e as buildJiraCreateFields, R as Popover, T as PopoverTrigger, U as Input, X as PopoverContent, Y as Label, Z as Select, _ as SelectTrigger, $ as SelectValue, a0 as SelectContent, a1 as SelectItem, a2 as JIRA_ISSUE_TYPE_VALUE_KEY, a3 as getJiraMetadata, a4 as buildJiraIssueFields, a5 as getJiraIssueTypeLabel, a6 as Button, a7 as Root2$2, a8 as Trigger$2, a9 as ChevronDown, aa as Portal, ab as Content2$1, ac as Check, ad as X$1, ae as DatePicker, f as buildNotionCreateFields, af as getNotionPageMetadata, ag as buildNotionFieldViews, b as captureWithModernScreenshot, p as pristineFetch, d as directUploadToSignedUrl, ah as DirectUploadError, a as uploadSignedMediaWithApi, v as sanitizeString, ai as getRegisteredUploadId, h as buildGuestOwnerQuery, z as createIssueUploadQueues, G as loadTusClient, H as createIndexedDbPendingUploadStore, K as createMediaUploadApi, J as ATTACHMENT_UPLOAD_DB_NAME, V as VIDEO_UPLOAD_DB_NAME, aj as getSnapshot, ak as subscribe$1, k as devLogger, y as getCurrentPageUrl, al as ChevronRight, am as ReactRemoveScroll, an as hideOthers, ao as useFocusGuards, ap as FocusScope, aq as createShadowDismissRoot, ar as getExposedJiraFields, as as isJiraFieldExposed, at as normalizeExposedJiraFields, c as captureViewportWithElementCrop, B as preloadModernScreenshot, g as getElementPosition, u as uploadScreenshotDataUrls, l as filterExcludeServiceRelatedLogs, m as filterExcludeServiceRelatedConsoleLogs, s as selectNetworkLogsForSubmit, n as compactNetworkLogsForTransport, o as CONSOLE_LOG_SUBMIT_LIMIT, q as buildExternalUserPayload, w as collectAttachmentUploads, x as hasReplayUpload, r as shrinkNetworkLogsToFit, t as measureJsonBytes, j as buildIssueIntegrationsPayload, D as enqueueIssueUploads, E as normalizeUploadMimeType, A as SessionReplayCaptureController, au as resolveJiraIssueTypeId, av as pickJiraIssueTypeFields, aw as switchJiraIssueType, ax as getPrimaryJiraMapping, ay as createCollection, az as useDirection, aA as useShadowOutsideDismiss, aB as getPrimaryNotionMapping, aC as buttonVariants, aD as TEST_ATTR_KEYS, aE as jaccard, aF as textSimilarity, aG as collectHeadings, aH as buildElementLocator, aI as normalizeForCompare, aJ as CONTAINER_SELECTOR, aK as safeRole, aL as safeAccessibleName, aM as sameDigitGroups, aN as labelSimilarityKeepingDigits, aO as landmarkRole, aP as findElementBySelector, aQ as findElementByXPath, S as ScreenRecordingController, i as composeIssueFilterGroups, aR as OnboardingProvider, aS as useOnboardingTarget, aT as useOnboardingFlow, aU as PopoverAnchor } from "./externalUserPayload-DWcXyz2U.js";
|
|
20
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
20
21
|
import * as React from "react";
|
|
21
22
|
import { useRef, useState, useEffect, useId, useLayoutEffect, useCallback, forwardRef, createElement, createContext, useContext, Component, Suspense, useInsertionEffect, useMemo, Children, isValidElement, Fragment as Fragment$1, useSyncExternalStore, useDeferredValue } from "react";
|
|
22
|
-
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
23
23
|
import { createRoot } from "react-dom/client";
|
|
24
24
|
import { createPortal } from "react-dom";
|
|
25
|
-
import { T as TooltipProvider, a as Tooltip, b as TooltipTrigger, c as TooltipContent } from "./tooltip-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
25
|
+
import { T as TooltipProvider, a as Tooltip, b as TooltipTrigger, c as TooltipContent } from "./tooltip-CT7e9xbU.js";
|
|
26
|
+
import { g as getDefaultExportFromCjs } from "./_commonjsHelpers-DWwsNxpa.js";
|
|
27
|
+
const ISSUE_STICKER_REPORT_FORM_CLASS = "issue-sticker-report-form";
|
|
28
|
+
function summarizeVideoUploads(states) {
|
|
29
|
+
if (states.length === 0) return void 0;
|
|
30
|
+
const remaining = states.filter((state) => state.status !== "failed");
|
|
31
|
+
const current = states.find((state) => state.status === "uploading");
|
|
32
|
+
const percent2 = current && current.bytesTotal > 0 ? Math.floor(current.bytesSent / current.bytesTotal * 100) : 0;
|
|
33
|
+
return { percent: percent2, count: remaining.length, failed: remaining.length === 0 };
|
|
34
|
+
}
|
|
35
|
+
const VIDEO_UPLOAD_COPY = {
|
|
36
|
+
created: "이슈가 생성되었습니다. 영상 업로드가 끝나면 이슈에 첨부됩니다.",
|
|
37
|
+
resumed: (count) => `업로드하지 못한 영상 ${count}개를 이어서 업로드합니다.`
|
|
38
|
+
};
|
|
39
|
+
function visibleKinds(kinds) {
|
|
40
|
+
const video = kinds.includes("video");
|
|
41
|
+
const replay = kinds.includes("replay");
|
|
42
|
+
if (video && replay) return "both";
|
|
43
|
+
if (video) return "video";
|
|
44
|
+
return replay ? "replay" : null;
|
|
45
|
+
}
|
|
46
|
+
const ISSUE_UPLOAD_COPY = {
|
|
47
|
+
failedMessage: "네트워크 연결을 확인한 뒤 다시 시도해주세요.",
|
|
48
|
+
retry: "다시 시도",
|
|
49
|
+
failedTitle: {
|
|
50
|
+
video: "영상 업로드 실패",
|
|
51
|
+
replay: "세션 리플레이 업로드 실패",
|
|
52
|
+
both: "영상·세션 리플레이 업로드 실패"
|
|
53
|
+
},
|
|
54
|
+
aborted: {
|
|
55
|
+
video: "영상을 업로드하지 못했습니다.",
|
|
56
|
+
replay: "세션 리플레이를 업로드하지 못했습니다.",
|
|
57
|
+
both: "영상과 세션 리플레이를 업로드하지 못했습니다."
|
|
58
|
+
},
|
|
59
|
+
/** 완료는 영상이 있을 때만 알린다. 리플레이만 붙은 경우의 문구는 없다. */
|
|
60
|
+
attached: {
|
|
61
|
+
video: "영상이 이슈에 첨부되었습니다.",
|
|
62
|
+
both: "영상과 세션 리플레이가 이슈에 첨부되었습니다."
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
function createIssueUploadToaster() {
|
|
66
|
+
const failureToastIds = /* @__PURE__ */ new Map();
|
|
67
|
+
return {
|
|
68
|
+
show(notice, retry) {
|
|
69
|
+
const previous2 = failureToastIds.get(notice.issueId);
|
|
70
|
+
if (previous2 !== void 0) {
|
|
71
|
+
toast.dismiss(previous2);
|
|
72
|
+
failureToastIds.delete(notice.issueId);
|
|
73
|
+
}
|
|
74
|
+
const failed = visibleKinds(notice.failed);
|
|
75
|
+
if (failed) {
|
|
76
|
+
const id2 = toast.error(ISSUE_UPLOAD_COPY.failedMessage, {
|
|
77
|
+
title: ISSUE_UPLOAD_COPY.failedTitle[failed],
|
|
78
|
+
duration: 0,
|
|
79
|
+
action: {
|
|
80
|
+
label: ISSUE_UPLOAD_COPY.retry,
|
|
81
|
+
onClick: () => {
|
|
82
|
+
failureToastIds.delete(notice.issueId);
|
|
83
|
+
retry(notice.issueId);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
failureToastIds.set(notice.issueId, id2);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const aborted = visibleKinds(notice.aborted);
|
|
91
|
+
if (aborted) {
|
|
92
|
+
toast.error(ISSUE_UPLOAD_COPY.aborted[aborted], {
|
|
93
|
+
title: ISSUE_UPLOAD_COPY.failedTitle[aborted]
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const completed = visibleKinds(notice.completed);
|
|
98
|
+
if (completed === "video" || completed === "both") {
|
|
99
|
+
toast.success(ISSUE_UPLOAD_COPY.attached[completed]);
|
|
40
100
|
}
|
|
41
101
|
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return null;
|
|
102
|
+
};
|
|
45
103
|
}
|
|
46
|
-
const ISSUE_STICKER_REPORT_FORM_CLASS = "issue-sticker-report-form";
|
|
47
104
|
const DEFAULT_DEBOUNCE_MS = 1e3;
|
|
48
105
|
function bindLogBackup(options) {
|
|
49
106
|
const { store, sessionId, snapshot, restoreInto } = options;
|
|
@@ -235,6 +292,82 @@ function getIntegrationFailureMessage(service) {
|
|
|
235
292
|
const action = FAILED_ACTION_TEXT[service] ?? "연동 작업을 마치지 못했습니다.";
|
|
236
293
|
return `이슈는 저장했지만 ${action}`;
|
|
237
294
|
}
|
|
295
|
+
function debounce(func, debounceMs, { signal, edges } = {}) {
|
|
296
|
+
let pendingThis = void 0;
|
|
297
|
+
let pendingArgs = null;
|
|
298
|
+
const leading = edges != null && edges.includes("leading");
|
|
299
|
+
const trailing = edges == null || edges.includes("trailing");
|
|
300
|
+
const invoke = () => {
|
|
301
|
+
if (pendingArgs !== null) {
|
|
302
|
+
func.apply(pendingThis, pendingArgs);
|
|
303
|
+
pendingThis = void 0;
|
|
304
|
+
pendingArgs = null;
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
const onTimerEnd = () => {
|
|
308
|
+
if (trailing) invoke();
|
|
309
|
+
cancel();
|
|
310
|
+
};
|
|
311
|
+
let timeoutId = null;
|
|
312
|
+
const schedule = () => {
|
|
313
|
+
if (timeoutId != null) clearTimeout(timeoutId);
|
|
314
|
+
timeoutId = setTimeout(() => {
|
|
315
|
+
timeoutId = null;
|
|
316
|
+
onTimerEnd();
|
|
317
|
+
}, debounceMs);
|
|
318
|
+
};
|
|
319
|
+
const cancelTimer = () => {
|
|
320
|
+
if (timeoutId !== null) {
|
|
321
|
+
clearTimeout(timeoutId);
|
|
322
|
+
timeoutId = null;
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
const cancel = () => {
|
|
326
|
+
cancelTimer();
|
|
327
|
+
pendingThis = void 0;
|
|
328
|
+
pendingArgs = null;
|
|
329
|
+
};
|
|
330
|
+
const flush = () => {
|
|
331
|
+
invoke();
|
|
332
|
+
};
|
|
333
|
+
const debounced = function(...args) {
|
|
334
|
+
if (signal == null ? void 0 : signal.aborted) return;
|
|
335
|
+
pendingThis = this;
|
|
336
|
+
pendingArgs = args;
|
|
337
|
+
const isFirstCall = timeoutId == null;
|
|
338
|
+
schedule();
|
|
339
|
+
if (leading && isFirstCall) invoke();
|
|
340
|
+
};
|
|
341
|
+
debounced.schedule = schedule;
|
|
342
|
+
debounced.cancel = cancel;
|
|
343
|
+
debounced.flush = flush;
|
|
344
|
+
signal == null ? void 0 : signal.addEventListener("abort", cancel, { once: true });
|
|
345
|
+
return debounced;
|
|
346
|
+
}
|
|
347
|
+
function throttle(func, throttleMs, { signal, edges = ["leading", "trailing"] } = {}) {
|
|
348
|
+
let pendingAt = null;
|
|
349
|
+
const debounced = debounce(function(...args) {
|
|
350
|
+
pendingAt = Date.now();
|
|
351
|
+
func.apply(this, args);
|
|
352
|
+
}, throttleMs, {
|
|
353
|
+
signal,
|
|
354
|
+
edges
|
|
355
|
+
});
|
|
356
|
+
const throttled = function(...args) {
|
|
357
|
+
if (pendingAt == null) pendingAt = Date.now();
|
|
358
|
+
if (Date.now() - pendingAt >= throttleMs) {
|
|
359
|
+
pendingAt = Date.now();
|
|
360
|
+
func.apply(this, args);
|
|
361
|
+
debounced.cancel();
|
|
362
|
+
debounced.schedule();
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
debounced.apply(this, args);
|
|
366
|
+
};
|
|
367
|
+
throttled.cancel = debounced.cancel;
|
|
368
|
+
throttled.flush = debounced.flush;
|
|
369
|
+
return throttled;
|
|
370
|
+
}
|
|
238
371
|
function getJiraFieldDefaultValues(exposedFields, fieldDefinitions = []) {
|
|
239
372
|
const values = {};
|
|
240
373
|
for (const field of buildJiraCreateFields(exposedFields, fieldDefinitions)) {
|
|
@@ -447,17 +580,148 @@ const HOST_POINTER_GUARD_EVENTS = [
|
|
|
447
580
|
"touchstart",
|
|
448
581
|
"touchend"
|
|
449
582
|
];
|
|
583
|
+
const HOST_CAPTURE_GUARD_EVENTS = [
|
|
584
|
+
"pointerdown",
|
|
585
|
+
"mousedown",
|
|
586
|
+
"click",
|
|
587
|
+
"dblclick",
|
|
588
|
+
"auxclick",
|
|
589
|
+
"contextmenu"
|
|
590
|
+
];
|
|
591
|
+
const CLICK_EVENTS = /* @__PURE__ */ new Set(["click", "dblclick", "auxclick", "contextmenu"]);
|
|
592
|
+
const ACTIVATION_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
593
|
+
"checkbox",
|
|
594
|
+
"radio",
|
|
595
|
+
"submit",
|
|
596
|
+
"reset",
|
|
597
|
+
"image",
|
|
598
|
+
"file",
|
|
599
|
+
"color",
|
|
600
|
+
"date",
|
|
601
|
+
"datetime-local",
|
|
602
|
+
"month",
|
|
603
|
+
"time",
|
|
604
|
+
"week"
|
|
605
|
+
]);
|
|
606
|
+
function hasActivationBehavior(node2) {
|
|
607
|
+
if (!(node2 instanceof Element)) return false;
|
|
608
|
+
if (node2.matches("a[href], area[href], label, summary, [popovertarget], [commandfor]")) {
|
|
609
|
+
return true;
|
|
610
|
+
}
|
|
611
|
+
if (node2 instanceof HTMLButtonElement) return node2.type !== "button" && node2.form !== null;
|
|
612
|
+
if (node2 instanceof HTMLInputElement) return ACTIVATION_INPUT_TYPES.has(node2.type);
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
function cloneInsideShadow(event) {
|
|
616
|
+
const init = {
|
|
617
|
+
bubbles: event.bubbles,
|
|
618
|
+
cancelable: event.cancelable,
|
|
619
|
+
composed: false,
|
|
620
|
+
view: event.view,
|
|
621
|
+
detail: event.detail,
|
|
622
|
+
screenX: event.screenX,
|
|
623
|
+
screenY: event.screenY,
|
|
624
|
+
clientX: event.clientX,
|
|
625
|
+
clientY: event.clientY,
|
|
626
|
+
movementX: event.movementX,
|
|
627
|
+
movementY: event.movementY,
|
|
628
|
+
button: event.button,
|
|
629
|
+
buttons: event.buttons,
|
|
630
|
+
relatedTarget: event.relatedTarget,
|
|
631
|
+
ctrlKey: event.ctrlKey,
|
|
632
|
+
shiftKey: event.shiftKey,
|
|
633
|
+
altKey: event.altKey,
|
|
634
|
+
metaKey: event.metaKey
|
|
635
|
+
};
|
|
636
|
+
if (typeof PointerEvent !== "undefined" && event instanceof PointerEvent) {
|
|
637
|
+
return new PointerEvent(event.type, {
|
|
638
|
+
...init,
|
|
639
|
+
pointerId: event.pointerId,
|
|
640
|
+
pointerType: event.pointerType,
|
|
641
|
+
isPrimary: event.isPrimary,
|
|
642
|
+
width: event.width,
|
|
643
|
+
height: event.height,
|
|
644
|
+
pressure: event.pressure,
|
|
645
|
+
tangentialPressure: event.tangentialPressure,
|
|
646
|
+
tiltX: event.tiltX,
|
|
647
|
+
tiltY: event.tiltY,
|
|
648
|
+
twist: event.twist
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
return new MouseEvent(event.type, init);
|
|
652
|
+
}
|
|
450
653
|
function attachHostPointerGuard(shadowRoot) {
|
|
654
|
+
const view = shadowRoot.ownerDocument.defaultView;
|
|
451
655
|
const stopLeakingToPage = (event) => {
|
|
452
656
|
event.stopPropagation();
|
|
453
657
|
};
|
|
658
|
+
const forwardInsideWidget = (event) => {
|
|
659
|
+
const path2 = event.composedPath();
|
|
660
|
+
const origin = path2[0];
|
|
661
|
+
if (!(origin instanceof Node) || origin.getRootNode() !== shadowRoot) return;
|
|
662
|
+
if (CLICK_EVENTS.has(event.type)) {
|
|
663
|
+
const insideWidget = path2.slice(0, path2.indexOf(shadowRoot));
|
|
664
|
+
if (insideWidget.some(hasActivationBehavior)) return;
|
|
665
|
+
}
|
|
666
|
+
event.stopImmediatePropagation();
|
|
667
|
+
if (!origin.dispatchEvent(cloneInsideShadow(event))) event.preventDefault();
|
|
668
|
+
};
|
|
454
669
|
HOST_POINTER_GUARD_EVENTS.forEach((type) => {
|
|
455
670
|
shadowRoot.addEventListener(type, stopLeakingToPage);
|
|
456
671
|
});
|
|
672
|
+
HOST_CAPTURE_GUARD_EVENTS.forEach((type) => {
|
|
673
|
+
view == null ? void 0 : view.addEventListener(type, forwardInsideWidget, true);
|
|
674
|
+
});
|
|
457
675
|
return () => {
|
|
458
676
|
HOST_POINTER_GUARD_EVENTS.forEach((type) => {
|
|
459
677
|
shadowRoot.removeEventListener(type, stopLeakingToPage);
|
|
460
678
|
});
|
|
679
|
+
HOST_CAPTURE_GUARD_EVENTS.forEach((type) => {
|
|
680
|
+
view == null ? void 0 : view.removeEventListener(type, forwardInsideWidget, true);
|
|
681
|
+
});
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
const HOST_INTERACTION_GUARD_EVENTS = [
|
|
685
|
+
"focusin",
|
|
686
|
+
"focusout",
|
|
687
|
+
"input",
|
|
688
|
+
"beforeinput",
|
|
689
|
+
"wheel",
|
|
690
|
+
"touchmove",
|
|
691
|
+
"dragstart",
|
|
692
|
+
"dragenter",
|
|
693
|
+
"dragover",
|
|
694
|
+
"dragleave",
|
|
695
|
+
"drop",
|
|
696
|
+
"dragend"
|
|
697
|
+
];
|
|
698
|
+
function attachHostInteractionGuard(shadowRoot) {
|
|
699
|
+
const host = shadowRoot.host;
|
|
700
|
+
const view = shadowRoot.ownerDocument.defaultView;
|
|
701
|
+
const stopLeakingToPage = (event) => {
|
|
702
|
+
event.stopPropagation();
|
|
703
|
+
};
|
|
704
|
+
const hideFocusLeavingForWidget = (event) => {
|
|
705
|
+
if (event.relatedTarget !== host) return;
|
|
706
|
+
if (event.composedPath().includes(host)) return;
|
|
707
|
+
event.stopPropagation();
|
|
708
|
+
};
|
|
709
|
+
const hideWidgetFocus = (event) => {
|
|
710
|
+
if (event.composedPath().includes(host)) event.stopImmediatePropagation();
|
|
711
|
+
};
|
|
712
|
+
HOST_INTERACTION_GUARD_EVENTS.forEach((type) => {
|
|
713
|
+
shadowRoot.addEventListener(type, stopLeakingToPage, { passive: true });
|
|
714
|
+
});
|
|
715
|
+
view == null ? void 0 : view.addEventListener("focusout", hideFocusLeavingForWidget, true);
|
|
716
|
+
view == null ? void 0 : view.addEventListener("blur", hideFocusLeavingForWidget, true);
|
|
717
|
+
view == null ? void 0 : view.addEventListener("focus", hideWidgetFocus, true);
|
|
718
|
+
return () => {
|
|
719
|
+
HOST_INTERACTION_GUARD_EVENTS.forEach((type) => {
|
|
720
|
+
shadowRoot.removeEventListener(type, stopLeakingToPage);
|
|
721
|
+
});
|
|
722
|
+
view == null ? void 0 : view.removeEventListener("focusout", hideFocusLeavingForWidget, true);
|
|
723
|
+
view == null ? void 0 : view.removeEventListener("blur", hideFocusLeavingForWidget, true);
|
|
724
|
+
view == null ? void 0 : view.removeEventListener("focus", hideWidgetFocus, true);
|
|
461
725
|
};
|
|
462
726
|
}
|
|
463
727
|
const RUNTIME_OWNER_MARKER_KEY = "__issueStickerRuntimeOwner";
|
|
@@ -1379,7 +1643,7 @@ function MultiSelect({
|
|
|
1379
1643
|
const contentRef = React.useRef(null);
|
|
1380
1644
|
const portalContainer = usePortalContainer();
|
|
1381
1645
|
React.useEffect(() => {
|
|
1382
|
-
var _a, _b2;
|
|
1646
|
+
var _a, _b2, _c2, _d2;
|
|
1383
1647
|
if (!open) {
|
|
1384
1648
|
return;
|
|
1385
1649
|
}
|
|
@@ -1390,8 +1654,12 @@ function MultiSelect({
|
|
|
1390
1654
|
}
|
|
1391
1655
|
setOpen(false);
|
|
1392
1656
|
};
|
|
1393
|
-
|
|
1394
|
-
|
|
1657
|
+
const scopes = /* @__PURE__ */ new Set([
|
|
1658
|
+
ownerDocument,
|
|
1659
|
+
((_c2 = contentRef.current) == null ? void 0 : _c2.getRootNode()) ?? ((_d2 = triggerRef.current) == null ? void 0 : _d2.getRootNode()) ?? ownerDocument
|
|
1660
|
+
]);
|
|
1661
|
+
scopes.forEach((scope) => scope.addEventListener("click", closeOnOutsideClick, true));
|
|
1662
|
+
return () => scopes.forEach((scope) => scope.removeEventListener("click", closeOnOutsideClick, true));
|
|
1395
1663
|
}, [open]);
|
|
1396
1664
|
const emitChange = (next) => {
|
|
1397
1665
|
onValueChange == null ? void 0 : onValueChange(next);
|
|
@@ -5024,149 +5292,44 @@ class WebPlatform {
|
|
|
5024
5292
|
this.eventBus = eventBus;
|
|
5025
5293
|
}
|
|
5026
5294
|
}
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
this.listeners = /* @__PURE__ */ new Map();
|
|
5030
|
-
}
|
|
5031
|
-
on(event, callback) {
|
|
5032
|
-
if (!this.listeners.has(event)) {
|
|
5033
|
-
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
5034
|
-
}
|
|
5035
|
-
this.listeners.get(event).add(callback);
|
|
5036
|
-
return () => this.off(event, callback);
|
|
5037
|
-
}
|
|
5038
|
-
off(event, callback) {
|
|
5039
|
-
var _a;
|
|
5040
|
-
(_a = this.listeners.get(event)) == null ? void 0 : _a.delete(callback);
|
|
5041
|
-
}
|
|
5042
|
-
emit(event, ...args) {
|
|
5043
|
-
var _a, _b2;
|
|
5044
|
-
(_a = this.listeners.get(event)) == null ? void 0 : _a.forEach((cb) => cb(...args));
|
|
5045
|
-
(_b2 = this.listeners.get("*")) == null ? void 0 : _b2.forEach((cb) => cb(event, ...args));
|
|
5046
|
-
}
|
|
5047
|
-
once(event, callback) {
|
|
5048
|
-
const wrapper = (...args) => {
|
|
5049
|
-
this.off(event, wrapper);
|
|
5050
|
-
callback(...args);
|
|
5051
|
-
};
|
|
5052
|
-
return this.on(event, wrapper);
|
|
5053
|
-
}
|
|
5054
|
-
removeAllListeners() {
|
|
5055
|
-
this.listeners.clear();
|
|
5056
|
-
}
|
|
5295
|
+
function isHttpStatusError(error) {
|
|
5296
|
+
return typeof error === "object" && error !== null && typeof error.status === "number";
|
|
5057
5297
|
}
|
|
5058
|
-
const
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
};
|
|
5068
|
-
class FeatureFlags {
|
|
5069
|
-
constructor(overrides) {
|
|
5070
|
-
this.flags = { ...DEFAULT_FLAGS, ...overrides };
|
|
5071
|
-
}
|
|
5072
|
-
get(name2) {
|
|
5073
|
-
return this.flags[name2];
|
|
5074
|
-
}
|
|
5075
|
-
set(name2, enabled) {
|
|
5076
|
-
this.flags[name2] = enabled;
|
|
5077
|
-
}
|
|
5078
|
-
getAll() {
|
|
5079
|
-
return { ...this.flags };
|
|
5080
|
-
}
|
|
5298
|
+
const MAX_RETRIES = 3;
|
|
5299
|
+
const MAX_BACKOFF_MS = 3e4;
|
|
5300
|
+
const MAX_RETRY_AFTER_MS = 6e4;
|
|
5301
|
+
function parseRetryAfterMs(value, now2 = Date.now()) {
|
|
5302
|
+
const trimmed = value == null ? void 0 : value.trim();
|
|
5303
|
+
if (!trimmed) return void 0;
|
|
5304
|
+
if (/^\d+$/.test(trimmed)) return Number(trimmed) * 1e3;
|
|
5305
|
+
const date = Date.parse(trimmed);
|
|
5306
|
+
return Number.isNaN(date) ? void 0 : Math.max(0, date - now2);
|
|
5081
5307
|
}
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
this._platform = null;
|
|
5086
|
-
this._eventBus = null;
|
|
5087
|
-
this._featureFlags = null;
|
|
5088
|
-
this._initialized = false;
|
|
5089
|
-
}
|
|
5090
|
-
static getInstance() {
|
|
5091
|
-
if (!_SDKManager.instance) {
|
|
5092
|
-
_SDKManager.instance = new _SDKManager();
|
|
5093
|
-
}
|
|
5094
|
-
return _SDKManager.instance;
|
|
5095
|
-
}
|
|
5096
|
-
init(config) {
|
|
5097
|
-
if (this._initialized) {
|
|
5098
|
-
this.updateConfig(config);
|
|
5099
|
-
return;
|
|
5100
|
-
}
|
|
5101
|
-
this._config = config;
|
|
5102
|
-
this._eventBus = new EventBus();
|
|
5103
|
-
this._platform = new WebPlatform(this._eventBus);
|
|
5104
|
-
this._featureFlags = new FeatureFlags({});
|
|
5105
|
-
this._initialized = true;
|
|
5106
|
-
}
|
|
5107
|
-
updateConfig(config) {
|
|
5108
|
-
if (!this._initialized) {
|
|
5109
|
-
this.init(config);
|
|
5110
|
-
return;
|
|
5111
|
-
}
|
|
5112
|
-
this._config = config;
|
|
5113
|
-
}
|
|
5114
|
-
destroy() {
|
|
5115
|
-
var _a;
|
|
5116
|
-
(_a = this._eventBus) == null ? void 0 : _a.removeAllListeners();
|
|
5117
|
-
this._config = null;
|
|
5118
|
-
this._platform = null;
|
|
5119
|
-
this._eventBus = null;
|
|
5120
|
-
this._featureFlags = null;
|
|
5121
|
-
this._initialized = false;
|
|
5122
|
-
}
|
|
5123
|
-
isInitialized() {
|
|
5124
|
-
return this._initialized;
|
|
5125
|
-
}
|
|
5126
|
-
get config() {
|
|
5127
|
-
if (!this._config) throw new Error("SDK not initialized");
|
|
5128
|
-
return this._config;
|
|
5129
|
-
}
|
|
5130
|
-
get platform() {
|
|
5131
|
-
if (!this._platform) throw new Error("SDK not initialized");
|
|
5132
|
-
return this._platform;
|
|
5133
|
-
}
|
|
5134
|
-
get eventBus() {
|
|
5135
|
-
if (!this._eventBus) throw new Error("SDK not initialized");
|
|
5136
|
-
return this._eventBus;
|
|
5137
|
-
}
|
|
5138
|
-
get featureFlags() {
|
|
5139
|
-
if (!this._featureFlags) throw new Error("SDK not initialized");
|
|
5140
|
-
return this._featureFlags;
|
|
5141
|
-
}
|
|
5142
|
-
static resetInstance() {
|
|
5143
|
-
if (_SDKManager.instance) {
|
|
5144
|
-
_SDKManager.instance.destroy();
|
|
5145
|
-
_SDKManager.instance = null;
|
|
5146
|
-
}
|
|
5147
|
-
}
|
|
5148
|
-
};
|
|
5149
|
-
_SDKManager.instance = null;
|
|
5150
|
-
let SDKManager = _SDKManager;
|
|
5151
|
-
const pendingListeners = /* @__PURE__ */ new Map();
|
|
5152
|
-
function addPendingSdkEventListener(event, callback) {
|
|
5153
|
-
if (!pendingListeners.has(event)) {
|
|
5154
|
-
pendingListeners.set(event, /* @__PURE__ */ new Set());
|
|
5155
|
-
}
|
|
5156
|
-
pendingListeners.get(event).add(callback);
|
|
5308
|
+
function retryAfterMsOf(response) {
|
|
5309
|
+
var _a;
|
|
5310
|
+
return parseRetryAfterMs((_a = response.headers) == null ? void 0 : _a.get("Retry-After"));
|
|
5157
5311
|
}
|
|
5158
|
-
function
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5312
|
+
function jitteredBackoffMs(failureCount, capMs = MAX_BACKOFF_MS) {
|
|
5313
|
+
return Math.random() * Math.min(capMs, 1e3 * 2 ** failureCount);
|
|
5314
|
+
}
|
|
5315
|
+
function statusOf(error) {
|
|
5316
|
+
return isHttpStatusError(error) ? error.status : 0;
|
|
5317
|
+
}
|
|
5318
|
+
function retryAfterOf(error) {
|
|
5319
|
+
return isHttpStatusError(error) ? error.retryAfterMs : void 0;
|
|
5320
|
+
}
|
|
5321
|
+
function queryRetry(failureCount, error) {
|
|
5322
|
+
const status = statusOf(error);
|
|
5323
|
+
if (status === 429) {
|
|
5324
|
+
const retryAfterMs = retryAfterOf(error);
|
|
5325
|
+
return failureCount < 1 && retryAfterMs !== void 0 && retryAfterMs <= MAX_RETRY_AFTER_MS;
|
|
5163
5326
|
}
|
|
5327
|
+
if (status >= 400 && status < 500 && status !== 408) return false;
|
|
5328
|
+
return failureCount < MAX_RETRIES;
|
|
5164
5329
|
}
|
|
5165
|
-
function
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
});
|
|
5169
|
-
pendingListeners.clear();
|
|
5330
|
+
function queryRetryDelay(failureCount, error) {
|
|
5331
|
+
const retryAfterMs = retryAfterOf(error);
|
|
5332
|
+
return retryAfterMs !== void 0 ? Math.min(retryAfterMs, MAX_RETRY_AFTER_MS) : jitteredBackoffMs(failureCount);
|
|
5170
5333
|
}
|
|
5171
5334
|
function readApiBaseUrlFromEnv() {
|
|
5172
5335
|
if (typeof process !== "undefined" && process.env && Object.prototype.hasOwnProperty.call(process.env, "VITE_API_BASE_URL")) {
|
|
@@ -5268,10 +5431,11 @@ async function clearToken() {
|
|
|
5268
5431
|
tokenMemoryCache = null;
|
|
5269
5432
|
}
|
|
5270
5433
|
class ApiError extends Error {
|
|
5271
|
-
constructor(message, status, data) {
|
|
5434
|
+
constructor(message, status, data, retryAfterMs) {
|
|
5272
5435
|
super(message);
|
|
5273
5436
|
this.status = status;
|
|
5274
5437
|
this.data = data;
|
|
5438
|
+
this.retryAfterMs = retryAfterMs;
|
|
5275
5439
|
this.name = "ApiError";
|
|
5276
5440
|
}
|
|
5277
5441
|
}
|
|
@@ -5340,7 +5504,8 @@ async function requestResponse(endpoint, options = {}) {
|
|
|
5340
5504
|
throw new ApiError(
|
|
5341
5505
|
extractErrorMessage(json.error) || json.message || `요청 실패: ${response.status}`,
|
|
5342
5506
|
response.status,
|
|
5343
|
-
json
|
|
5507
|
+
json,
|
|
5508
|
+
retryAfterMsOf(response)
|
|
5344
5509
|
);
|
|
5345
5510
|
}
|
|
5346
5511
|
return json;
|
|
@@ -5403,7 +5568,8 @@ async function uploadRequest(endpoint, formData, skipAuth = false) {
|
|
|
5403
5568
|
throw new ApiError(
|
|
5404
5569
|
extractErrorMessage(json.error) || json.message || `업로드 실패: ${response.status}`,
|
|
5405
5570
|
response.status,
|
|
5406
|
-
json
|
|
5571
|
+
json,
|
|
5572
|
+
retryAfterMsOf(response)
|
|
5407
5573
|
);
|
|
5408
5574
|
}
|
|
5409
5575
|
return json.data;
|
|
@@ -5441,6 +5607,442 @@ const apiClient = {
|
|
|
5441
5607
|
return uploadRequest(endpoint, formData, skipAuth);
|
|
5442
5608
|
}
|
|
5443
5609
|
};
|
|
5610
|
+
const queryClient = new QueryClient({
|
|
5611
|
+
defaultOptions: {
|
|
5612
|
+
queries: {
|
|
5613
|
+
staleTime: 5 * 60 * 1e3,
|
|
5614
|
+
// 5분
|
|
5615
|
+
gcTime: 10 * 60 * 1e3,
|
|
5616
|
+
// 10분 (v5에서는 cacheTime 대신 gcTime 사용)
|
|
5617
|
+
refetchOnWindowFocus: true,
|
|
5618
|
+
refetchOnReconnect: true,
|
|
5619
|
+
retry: queryRetry,
|
|
5620
|
+
retryDelay: queryRetryDelay
|
|
5621
|
+
},
|
|
5622
|
+
mutations: {
|
|
5623
|
+
retry: 0
|
|
5624
|
+
}
|
|
5625
|
+
}
|
|
5626
|
+
});
|
|
5627
|
+
async function uploadSignedMedia(file, mediaType, issueId) {
|
|
5628
|
+
return uploadSignedMediaWithApi(
|
|
5629
|
+
{
|
|
5630
|
+
post: (endpoint, body) => apiClient.post(endpoint, body),
|
|
5631
|
+
directUpload
|
|
5632
|
+
},
|
|
5633
|
+
file,
|
|
5634
|
+
mediaType,
|
|
5635
|
+
{ issueId }
|
|
5636
|
+
);
|
|
5637
|
+
}
|
|
5638
|
+
function issuesEndpoint() {
|
|
5639
|
+
return "/api/sdk/issues";
|
|
5640
|
+
}
|
|
5641
|
+
function guestOwnerQuery() {
|
|
5642
|
+
var _a;
|
|
5643
|
+
const mode = getSdkMode();
|
|
5644
|
+
if (mode !== "public") return "";
|
|
5645
|
+
return buildGuestOwnerQuery(mode, (_a = SDKManager.getInstance().config.user) == null ? void 0 : _a.id);
|
|
5646
|
+
}
|
|
5647
|
+
let issuesCache = null;
|
|
5648
|
+
const CACHE_DURATION_MS = 30 * 1e3;
|
|
5649
|
+
const STICKER_PAGE_SIZE = 500;
|
|
5650
|
+
const STICKER_FETCH_LIMIT = 2e3;
|
|
5651
|
+
function getFilterKey(filters) {
|
|
5652
|
+
var _a;
|
|
5653
|
+
const normalizeArray = (value) => {
|
|
5654
|
+
if (value === "all" || !value) return "all";
|
|
5655
|
+
if (Array.isArray(value)) {
|
|
5656
|
+
return value.length === 0 ? "all" : [...value].sort().join(",");
|
|
5657
|
+
}
|
|
5658
|
+
return String(value);
|
|
5659
|
+
};
|
|
5660
|
+
const normalizeRecord = (record) => {
|
|
5661
|
+
if (!record) return "";
|
|
5662
|
+
const entries = Object.entries(record).map(([k, v]) => [k, [...v].sort()]).sort(([a], [b]) => a.localeCompare(b));
|
|
5663
|
+
return JSON.stringify(entries);
|
|
5664
|
+
};
|
|
5665
|
+
return JSON.stringify({
|
|
5666
|
+
sdkMode: getSdkMode() || "public",
|
|
5667
|
+
url: normalizeArray(filters.url),
|
|
5668
|
+
projectId: filters.projectId || "all",
|
|
5669
|
+
externalUserId: filters.externalUserId || "",
|
|
5670
|
+
search: ((_a = filters.search) == null ? void 0 : _a.trim()) || "",
|
|
5671
|
+
dateFrom: filters.dateFrom || "",
|
|
5672
|
+
dateTo: filters.dateTo || "",
|
|
5673
|
+
page: filters.page ?? 0,
|
|
5674
|
+
pageSize: filters.pageSize ?? 0,
|
|
5675
|
+
custom: normalizeRecord(filters.customFieldValues),
|
|
5676
|
+
jira: normalizeRecord(filters.jiraFieldValues),
|
|
5677
|
+
notion: normalizeRecord(filters.notionFieldValues),
|
|
5678
|
+
sortOrder: filters.sortOrder || ""
|
|
5679
|
+
});
|
|
5680
|
+
}
|
|
5681
|
+
function invalidateIssuesCache() {
|
|
5682
|
+
issuesCache = null;
|
|
5683
|
+
}
|
|
5684
|
+
function recordToFilterArray(record, keyFieldName) {
|
|
5685
|
+
if (!record) return null;
|
|
5686
|
+
const entries = Object.entries(record).filter(([, values]) => values.length > 0);
|
|
5687
|
+
if (entries.length === 0) return null;
|
|
5688
|
+
return entries.map(
|
|
5689
|
+
([key, values]) => ({ [keyFieldName]: key, values })
|
|
5690
|
+
);
|
|
5691
|
+
}
|
|
5692
|
+
function buildFilterQueryParams(filters) {
|
|
5693
|
+
var _a;
|
|
5694
|
+
const params = new URLSearchParams();
|
|
5695
|
+
if (filters.projectId) {
|
|
5696
|
+
params.set("project_id", filters.projectId);
|
|
5697
|
+
}
|
|
5698
|
+
if (filters.url && filters.url !== "all") {
|
|
5699
|
+
if (Array.isArray(filters.url) && filters.url.length > 0) {
|
|
5700
|
+
params.set("url", filters.url.join(","));
|
|
5701
|
+
} else if (typeof filters.url === "string") {
|
|
5702
|
+
params.set("url", filters.url);
|
|
5703
|
+
}
|
|
5704
|
+
}
|
|
5705
|
+
const search2 = (_a = filters.search) == null ? void 0 : _a.trim();
|
|
5706
|
+
if (search2) {
|
|
5707
|
+
params.set("search", search2);
|
|
5708
|
+
}
|
|
5709
|
+
if (filters.dateFrom) {
|
|
5710
|
+
params.set("date_from", filters.dateFrom);
|
|
5711
|
+
}
|
|
5712
|
+
if (filters.dateTo) {
|
|
5713
|
+
params.set("date_to", filters.dateTo);
|
|
5714
|
+
}
|
|
5715
|
+
const customArr = recordToFilterArray(filters.customFieldValues, "fieldDefinitionId");
|
|
5716
|
+
if (customArr) {
|
|
5717
|
+
params.set("custom_field_filters", JSON.stringify(customArr));
|
|
5718
|
+
}
|
|
5719
|
+
const jiraArr = recordToFilterArray(filters.jiraFieldValues, "fieldName");
|
|
5720
|
+
if (jiraArr) {
|
|
5721
|
+
params.set("jira_field_filters", JSON.stringify(jiraArr));
|
|
5722
|
+
}
|
|
5723
|
+
const notionArr = recordToFilterArray(filters.notionFieldValues, "fieldName");
|
|
5724
|
+
if (notionArr) {
|
|
5725
|
+
params.set("notion_field_filters", JSON.stringify(notionArr));
|
|
5726
|
+
}
|
|
5727
|
+
if (filters.externalUserId) {
|
|
5728
|
+
params.set("external_user_id", filters.externalUserId);
|
|
5729
|
+
}
|
|
5730
|
+
if (typeof filters.page === "number" && filters.page > 0) {
|
|
5731
|
+
params.set("page", String(filters.page));
|
|
5732
|
+
}
|
|
5733
|
+
if (typeof filters.pageSize === "number" && filters.pageSize > 0) {
|
|
5734
|
+
params.set("page_size", String(filters.pageSize));
|
|
5735
|
+
}
|
|
5736
|
+
if (filters.sortOrder) {
|
|
5737
|
+
params.set("sort_order", filters.sortOrder);
|
|
5738
|
+
}
|
|
5739
|
+
const queryString = params.toString();
|
|
5740
|
+
return queryString ? `?${queryString}` : "";
|
|
5741
|
+
}
|
|
5742
|
+
const issueService = {
|
|
5743
|
+
async createIssue(issueData) {
|
|
5744
|
+
const sanitizedData = sanitizeString(issueData);
|
|
5745
|
+
const screenshotUploadIds = (sanitizedData.screenshot || []).map((url) => getRegisteredUploadId(url, "screenshot")).filter((id2) => typeof id2 === "string" && id2.length > 0);
|
|
5746
|
+
const videoUploadId = sanitizedData.video ? getRegisteredUploadId(sanitizedData.video, "video") : void 0;
|
|
5747
|
+
if (screenshotUploadIds.length > 0) {
|
|
5748
|
+
sanitizedData.screenshot_upload_ids = screenshotUploadIds;
|
|
5749
|
+
}
|
|
5750
|
+
if (videoUploadId) {
|
|
5751
|
+
sanitizedData.video_upload_id = videoUploadId;
|
|
5752
|
+
}
|
|
5753
|
+
const issue = await apiClient.post(issuesEndpoint(), sanitizedData);
|
|
5754
|
+
invalidateIssuesCache();
|
|
5755
|
+
return issue;
|
|
5756
|
+
},
|
|
5757
|
+
async getIssues(filters = {}, options = {}) {
|
|
5758
|
+
if (!filters.projectId) {
|
|
5759
|
+
throw new Error("프로젝트 ID가 필요합니다. 로그인 상태를 확인해주세요.");
|
|
5760
|
+
}
|
|
5761
|
+
const filterKey = getFilterKey(filters);
|
|
5762
|
+
const now2 = Date.now();
|
|
5763
|
+
if (!options.forceRefresh && issuesCache && issuesCache.filterKey === filterKey && now2 - issuesCache.timestamp < CACHE_DURATION_MS) {
|
|
5764
|
+
return issuesCache.data;
|
|
5765
|
+
}
|
|
5766
|
+
const queryParams = buildFilterQueryParams(filters);
|
|
5767
|
+
const { items: issues = [] } = await apiClient.get(
|
|
5768
|
+
`${issuesEndpoint()}${queryParams}`
|
|
5769
|
+
);
|
|
5770
|
+
issuesCache = {
|
|
5771
|
+
data: issues,
|
|
5772
|
+
timestamp: now2,
|
|
5773
|
+
filterKey
|
|
5774
|
+
};
|
|
5775
|
+
return issues;
|
|
5776
|
+
},
|
|
5777
|
+
async getIssueStickers(filters = {}, options = {}) {
|
|
5778
|
+
var _a, _b2;
|
|
5779
|
+
if (!filters.projectId) {
|
|
5780
|
+
throw new Error("프로젝트 ID가 필요합니다. 로그인 상태를 확인해주세요.");
|
|
5781
|
+
}
|
|
5782
|
+
const filterKey = `${getFilterKey(filters)}:stickers`;
|
|
5783
|
+
const now2 = Date.now();
|
|
5784
|
+
if (!options.forceRefresh && issuesCache && issuesCache.filterKey === filterKey && now2 - issuesCache.timestamp < CACHE_DURATION_MS) {
|
|
5785
|
+
return issuesCache.data;
|
|
5786
|
+
}
|
|
5787
|
+
const issues = [];
|
|
5788
|
+
let page = 1;
|
|
5789
|
+
let hasMore = true;
|
|
5790
|
+
while (hasMore && issues.length <= STICKER_FETCH_LIMIT) {
|
|
5791
|
+
const response = await apiClient.getResponse(
|
|
5792
|
+
`${issuesEndpoint()}/stickers${buildFilterQueryParams({
|
|
5793
|
+
...filters,
|
|
5794
|
+
page,
|
|
5795
|
+
pageSize: STICKER_PAGE_SIZE
|
|
5796
|
+
})}`
|
|
5797
|
+
);
|
|
5798
|
+
const pageItems = ((_a = response.data) == null ? void 0 : _a.items) ?? [];
|
|
5799
|
+
issues.push(...pageItems);
|
|
5800
|
+
hasMore = Boolean((_b2 = response.data) == null ? void 0 : _b2.hasMore) && pageItems.length > 0;
|
|
5801
|
+
page += 1;
|
|
5802
|
+
}
|
|
5803
|
+
issuesCache = {
|
|
5804
|
+
data: issues,
|
|
5805
|
+
timestamp: now2,
|
|
5806
|
+
filterKey
|
|
5807
|
+
};
|
|
5808
|
+
return issues;
|
|
5809
|
+
},
|
|
5810
|
+
invalidateCache() {
|
|
5811
|
+
invalidateIssuesCache();
|
|
5812
|
+
},
|
|
5813
|
+
// 계약 기준: 백엔드 GET /api/sdk/issues/:id 는 back#101부터 읽기-시점-스크럽된
|
|
5814
|
+
// llm_prompt 를 상세 응답에 포함한다(목록 응답에는 없음) — 스코프를 이 반환 타입에만 한정한다.
|
|
5815
|
+
async getIssue(id2) {
|
|
5816
|
+
return apiClient.get(
|
|
5817
|
+
`${issuesEndpoint()}/${id2}${guestOwnerQuery()}`
|
|
5818
|
+
);
|
|
5819
|
+
},
|
|
5820
|
+
async updateIssue(id2, updates) {
|
|
5821
|
+
const sanitizedUpdates = sanitizeString(updates);
|
|
5822
|
+
const issue = await apiClient.put(
|
|
5823
|
+
`${issuesEndpoint()}/${id2}${guestOwnerQuery()}`,
|
|
5824
|
+
sanitizedUpdates
|
|
5825
|
+
);
|
|
5826
|
+
invalidateIssuesCache();
|
|
5827
|
+
return issue;
|
|
5828
|
+
},
|
|
5829
|
+
/**
|
|
5830
|
+
* 재생성. 스펙상 세 채널 모두에서 되고 역할과 무관하다 — 검사는 역할이 아니라
|
|
5831
|
+
* 그 이슈에 접근할 수 있는지다(백엔드가 판정한다).
|
|
5832
|
+
*
|
|
5833
|
+
* 응답으로 갱신된 이슈가 오지만 상태는 다시 pending 이다. 호출부는 응답을
|
|
5834
|
+
* 그대로 쓰지 말고 폴링으로 이어받아야 한다.
|
|
5835
|
+
*/
|
|
5836
|
+
async regenerateIssue(id2) {
|
|
5837
|
+
const issue = await apiClient.post(
|
|
5838
|
+
`${issuesEndpoint()}/${encodeURIComponent(id2)}/regenerate${guestOwnerQuery()}`,
|
|
5839
|
+
{}
|
|
5840
|
+
);
|
|
5841
|
+
invalidateIssuesCache();
|
|
5842
|
+
return issue;
|
|
5843
|
+
},
|
|
5844
|
+
async deleteIssue(id2, options) {
|
|
5845
|
+
const endpoint = `${issuesEndpoint()}/${id2}${guestOwnerQuery()}`;
|
|
5846
|
+
if (options == null ? void 0 : options.deleteExternal) {
|
|
5847
|
+
await apiClient.delete(endpoint, {
|
|
5848
|
+
body: { deleteExternal: true }
|
|
5849
|
+
});
|
|
5850
|
+
} else {
|
|
5851
|
+
await apiClient.delete(endpoint);
|
|
5852
|
+
}
|
|
5853
|
+
invalidateIssuesCache();
|
|
5854
|
+
}
|
|
5855
|
+
};
|
|
5856
|
+
function invalidateIssueQueries(issueId) {
|
|
5857
|
+
issueService.invalidateCache();
|
|
5858
|
+
queryClient.invalidateQueries({ queryKey: ["contentIssues"] });
|
|
5859
|
+
queryClient.invalidateQueries({ queryKey: ["issue", issueId] });
|
|
5860
|
+
}
|
|
5861
|
+
function notifyIssueUpdated(issueId) {
|
|
5862
|
+
SDKManager.getInstance().eventBus.emit("issue:updated", issueId);
|
|
5863
|
+
}
|
|
5864
|
+
function applyIssueMutationEffects(issueId) {
|
|
5865
|
+
invalidateIssueQueries(issueId);
|
|
5866
|
+
notifyIssueUpdated(issueId);
|
|
5867
|
+
}
|
|
5868
|
+
let queues = null;
|
|
5869
|
+
const toaster = createIssueUploadToaster();
|
|
5870
|
+
function getQueues() {
|
|
5871
|
+
if (queues) return queues;
|
|
5872
|
+
queues = createIssueUploadQueues({
|
|
5873
|
+
api: createMediaUploadApi({ post: (endpoint, body) => apiClient.post(endpoint, body) }),
|
|
5874
|
+
clientInfo: "issue-sticker-sdk",
|
|
5875
|
+
videoStore: createIndexedDbPendingUploadStore(VIDEO_UPLOAD_DB_NAME),
|
|
5876
|
+
attachmentStore: createIndexedDbPendingUploadStore(ATTACHMENT_UPLOAD_DB_NAME),
|
|
5877
|
+
loadTus: loadTusClient,
|
|
5878
|
+
onEvent: (event) => {
|
|
5879
|
+
if (event.type !== "failed") invalidateIssueQueries(event.issueId);
|
|
5880
|
+
},
|
|
5881
|
+
onNotice: (notice) => toaster.show(notice, retryWidgetIssueUploads)
|
|
5882
|
+
});
|
|
5883
|
+
return queues;
|
|
5884
|
+
}
|
|
5885
|
+
function getWidgetVideoUploadQueue() {
|
|
5886
|
+
return getQueues().video;
|
|
5887
|
+
}
|
|
5888
|
+
function getWidgetAttachmentUploadQueue() {
|
|
5889
|
+
return getQueues().attachments;
|
|
5890
|
+
}
|
|
5891
|
+
function resumeWidgetIssueUploads(projectKey) {
|
|
5892
|
+
return getQueues().resume([projectKey]);
|
|
5893
|
+
}
|
|
5894
|
+
function retryWidgetIssueUploads(issueId) {
|
|
5895
|
+
queues == null ? void 0 : queues.retry(issueId);
|
|
5896
|
+
}
|
|
5897
|
+
function stopWidgetUploadQueues() {
|
|
5898
|
+
queues == null ? void 0 : queues.stop();
|
|
5899
|
+
queues = null;
|
|
5900
|
+
}
|
|
5901
|
+
class EventBus {
|
|
5902
|
+
constructor() {
|
|
5903
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
5904
|
+
}
|
|
5905
|
+
on(event, callback) {
|
|
5906
|
+
if (!this.listeners.has(event)) {
|
|
5907
|
+
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
5908
|
+
}
|
|
5909
|
+
this.listeners.get(event).add(callback);
|
|
5910
|
+
return () => this.off(event, callback);
|
|
5911
|
+
}
|
|
5912
|
+
off(event, callback) {
|
|
5913
|
+
var _a;
|
|
5914
|
+
(_a = this.listeners.get(event)) == null ? void 0 : _a.delete(callback);
|
|
5915
|
+
}
|
|
5916
|
+
emit(event, ...args) {
|
|
5917
|
+
var _a, _b2;
|
|
5918
|
+
(_a = this.listeners.get(event)) == null ? void 0 : _a.forEach((cb) => cb(...args));
|
|
5919
|
+
(_b2 = this.listeners.get("*")) == null ? void 0 : _b2.forEach((cb) => cb(event, ...args));
|
|
5920
|
+
}
|
|
5921
|
+
once(event, callback) {
|
|
5922
|
+
const wrapper = (...args) => {
|
|
5923
|
+
this.off(event, wrapper);
|
|
5924
|
+
callback(...args);
|
|
5925
|
+
};
|
|
5926
|
+
return this.on(event, wrapper);
|
|
5927
|
+
}
|
|
5928
|
+
removeAllListeners() {
|
|
5929
|
+
this.listeners.clear();
|
|
5930
|
+
}
|
|
5931
|
+
}
|
|
5932
|
+
const DEFAULT_FLAGS = {
|
|
5933
|
+
networkLogs: true,
|
|
5934
|
+
consoleLogs: true,
|
|
5935
|
+
screenshot: true,
|
|
5936
|
+
screenRecording: true,
|
|
5937
|
+
stickers: true,
|
|
5938
|
+
keyboardShortcuts: true,
|
|
5939
|
+
jiraIntegration: false,
|
|
5940
|
+
fab: true
|
|
5941
|
+
};
|
|
5942
|
+
class FeatureFlags {
|
|
5943
|
+
constructor(overrides) {
|
|
5944
|
+
this.flags = { ...DEFAULT_FLAGS, ...overrides };
|
|
5945
|
+
}
|
|
5946
|
+
get(name2) {
|
|
5947
|
+
return this.flags[name2];
|
|
5948
|
+
}
|
|
5949
|
+
set(name2, enabled) {
|
|
5950
|
+
this.flags[name2] = enabled;
|
|
5951
|
+
}
|
|
5952
|
+
getAll() {
|
|
5953
|
+
return { ...this.flags };
|
|
5954
|
+
}
|
|
5955
|
+
}
|
|
5956
|
+
const _SDKManager = class _SDKManager {
|
|
5957
|
+
constructor() {
|
|
5958
|
+
this._config = null;
|
|
5959
|
+
this._platform = null;
|
|
5960
|
+
this._eventBus = null;
|
|
5961
|
+
this._featureFlags = null;
|
|
5962
|
+
this._initialized = false;
|
|
5963
|
+
}
|
|
5964
|
+
static getInstance() {
|
|
5965
|
+
if (!_SDKManager.instance) {
|
|
5966
|
+
_SDKManager.instance = new _SDKManager();
|
|
5967
|
+
}
|
|
5968
|
+
return _SDKManager.instance;
|
|
5969
|
+
}
|
|
5970
|
+
init(config) {
|
|
5971
|
+
if (this._initialized) {
|
|
5972
|
+
this.updateConfig(config);
|
|
5973
|
+
return;
|
|
5974
|
+
}
|
|
5975
|
+
this._config = config;
|
|
5976
|
+
this._eventBus = new EventBus();
|
|
5977
|
+
this._platform = new WebPlatform(this._eventBus);
|
|
5978
|
+
this._featureFlags = new FeatureFlags({});
|
|
5979
|
+
this._initialized = true;
|
|
5980
|
+
}
|
|
5981
|
+
updateConfig(config) {
|
|
5982
|
+
if (!this._initialized) {
|
|
5983
|
+
this.init(config);
|
|
5984
|
+
return;
|
|
5985
|
+
}
|
|
5986
|
+
this._config = config;
|
|
5987
|
+
}
|
|
5988
|
+
destroy() {
|
|
5989
|
+
var _a;
|
|
5990
|
+
stopWidgetUploadQueues();
|
|
5991
|
+
(_a = this._eventBus) == null ? void 0 : _a.removeAllListeners();
|
|
5992
|
+
this._config = null;
|
|
5993
|
+
this._platform = null;
|
|
5994
|
+
this._eventBus = null;
|
|
5995
|
+
this._featureFlags = null;
|
|
5996
|
+
this._initialized = false;
|
|
5997
|
+
}
|
|
5998
|
+
isInitialized() {
|
|
5999
|
+
return this._initialized;
|
|
6000
|
+
}
|
|
6001
|
+
get config() {
|
|
6002
|
+
if (!this._config) throw new Error("SDK not initialized");
|
|
6003
|
+
return this._config;
|
|
6004
|
+
}
|
|
6005
|
+
get platform() {
|
|
6006
|
+
if (!this._platform) throw new Error("SDK not initialized");
|
|
6007
|
+
return this._platform;
|
|
6008
|
+
}
|
|
6009
|
+
get eventBus() {
|
|
6010
|
+
if (!this._eventBus) throw new Error("SDK not initialized");
|
|
6011
|
+
return this._eventBus;
|
|
6012
|
+
}
|
|
6013
|
+
get featureFlags() {
|
|
6014
|
+
if (!this._featureFlags) throw new Error("SDK not initialized");
|
|
6015
|
+
return this._featureFlags;
|
|
6016
|
+
}
|
|
6017
|
+
static resetInstance() {
|
|
6018
|
+
if (_SDKManager.instance) {
|
|
6019
|
+
_SDKManager.instance.destroy();
|
|
6020
|
+
_SDKManager.instance = null;
|
|
6021
|
+
}
|
|
6022
|
+
}
|
|
6023
|
+
};
|
|
6024
|
+
_SDKManager.instance = null;
|
|
6025
|
+
let SDKManager = _SDKManager;
|
|
6026
|
+
const pendingListeners = /* @__PURE__ */ new Map();
|
|
6027
|
+
function addPendingSdkEventListener(event, callback) {
|
|
6028
|
+
if (!pendingListeners.has(event)) {
|
|
6029
|
+
pendingListeners.set(event, /* @__PURE__ */ new Set());
|
|
6030
|
+
}
|
|
6031
|
+
pendingListeners.get(event).add(callback);
|
|
6032
|
+
}
|
|
6033
|
+
function removePendingSdkEventListener(event, callback) {
|
|
6034
|
+
var _a, _b2;
|
|
6035
|
+
(_a = pendingListeners.get(event)) == null ? void 0 : _a.delete(callback);
|
|
6036
|
+
if (((_b2 = pendingListeners.get(event)) == null ? void 0 : _b2.size) === 0) {
|
|
6037
|
+
pendingListeners.delete(event);
|
|
6038
|
+
}
|
|
6039
|
+
}
|
|
6040
|
+
function flushPendingSdkEventListeners(eventBus) {
|
|
6041
|
+
pendingListeners.forEach((listeners2, event) => {
|
|
6042
|
+
listeners2.forEach((callback) => eventBus.on(event, callback));
|
|
6043
|
+
});
|
|
6044
|
+
pendingListeners.clear();
|
|
6045
|
+
}
|
|
5444
6046
|
let session = null;
|
|
5445
6047
|
function resolveApiBaseUrl() {
|
|
5446
6048
|
try {
|
|
@@ -5607,25 +6209,6 @@ const createShadowRoot = (hostElement, cssTexts = []) => {
|
|
|
5607
6209
|
const getPortalContainer = () => {
|
|
5608
6210
|
return _portalTarget || document.body;
|
|
5609
6211
|
};
|
|
5610
|
-
const queryClient = new QueryClient({
|
|
5611
|
-
defaultOptions: {
|
|
5612
|
-
queries: {
|
|
5613
|
-
staleTime: 5 * 60 * 1e3,
|
|
5614
|
-
// 5분
|
|
5615
|
-
gcTime: 10 * 60 * 1e3,
|
|
5616
|
-
// 10분 (v5에서는 cacheTime 대신 gcTime 사용)
|
|
5617
|
-
refetchOnWindowFocus: true,
|
|
5618
|
-
refetchOnReconnect: true,
|
|
5619
|
-
retry: 3,
|
|
5620
|
-
retryDelay: (attemptIndex) => Math.min(1e3 * 2 ** attemptIndex, 3e4)
|
|
5621
|
-
},
|
|
5622
|
-
mutations: {
|
|
5623
|
-
retry: 1,
|
|
5624
|
-
// Mutation은 1번만 재시도
|
|
5625
|
-
retryDelay: 1e3
|
|
5626
|
-
}
|
|
5627
|
-
}
|
|
5628
|
-
});
|
|
5629
6212
|
/**
|
|
5630
6213
|
* @license lucide-react v0.562.0 - ISC
|
|
5631
6214
|
*
|
|
@@ -14554,7 +15137,10 @@ const ToastItem = forwardRef(function ToastItem2({ t, behindCount, onDismiss },
|
|
|
14554
15137
|
),
|
|
14555
15138
|
children: /* @__PURE__ */ jsxs("div", { className: cn$1("flex items-center gap-2.5 px-3 py-2.5", style.tint), children: [
|
|
14556
15139
|
t.type !== "default" ? /* @__PURE__ */ jsx(Icon2, { className: cn$1("h-4 w-4 shrink-0", style.iconColor) }) : null,
|
|
14557
|
-
/* @__PURE__ */
|
|
15140
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1 text-sm text-text-primary", children: [
|
|
15141
|
+
t.title ? /* @__PURE__ */ jsx("span", { className: "block font-semibold", children: t.title }) : null,
|
|
15142
|
+
/* @__PURE__ */ jsx("span", { className: t.title ? "block" : "font-medium", children: t.message })
|
|
15143
|
+
] }),
|
|
14558
15144
|
t.action ? /* @__PURE__ */ jsx(
|
|
14559
15145
|
"button",
|
|
14560
15146
|
{
|
|
@@ -14662,235 +15248,6 @@ function toIssueFilterParams(filters) {
|
|
|
14662
15248
|
...countRecord(filters.notionFieldValues) ? { notionFieldValues: filters.notionFieldValues } : {}
|
|
14663
15249
|
};
|
|
14664
15250
|
}
|
|
14665
|
-
async function uploadSignedMedia(file, mediaType, issueId) {
|
|
14666
|
-
return uploadSignedMediaWithApi(
|
|
14667
|
-
{
|
|
14668
|
-
post: (endpoint, body) => apiClient.post(endpoint, body),
|
|
14669
|
-
directUpload
|
|
14670
|
-
},
|
|
14671
|
-
file,
|
|
14672
|
-
mediaType,
|
|
14673
|
-
{ issueId }
|
|
14674
|
-
);
|
|
14675
|
-
}
|
|
14676
|
-
function issuesEndpoint() {
|
|
14677
|
-
return "/api/sdk/issues";
|
|
14678
|
-
}
|
|
14679
|
-
function guestOwnerQuery() {
|
|
14680
|
-
var _a;
|
|
14681
|
-
const mode = getSdkMode();
|
|
14682
|
-
if (mode !== "public") return "";
|
|
14683
|
-
return buildGuestOwnerQuery(mode, (_a = SDKManager.getInstance().config.user) == null ? void 0 : _a.id);
|
|
14684
|
-
}
|
|
14685
|
-
let issuesCache = null;
|
|
14686
|
-
const CACHE_DURATION_MS = 30 * 1e3;
|
|
14687
|
-
const STICKER_PAGE_SIZE = 500;
|
|
14688
|
-
const STICKER_FETCH_LIMIT = 2e3;
|
|
14689
|
-
function getFilterKey(filters) {
|
|
14690
|
-
var _a;
|
|
14691
|
-
const normalizeArray = (value) => {
|
|
14692
|
-
if (value === "all" || !value) return "all";
|
|
14693
|
-
if (Array.isArray(value)) {
|
|
14694
|
-
return value.length === 0 ? "all" : [...value].sort().join(",");
|
|
14695
|
-
}
|
|
14696
|
-
return String(value);
|
|
14697
|
-
};
|
|
14698
|
-
const normalizeRecord = (record) => {
|
|
14699
|
-
if (!record) return "";
|
|
14700
|
-
const entries = Object.entries(record).map(([k, v]) => [k, [...v].sort()]).sort(([a], [b]) => a.localeCompare(b));
|
|
14701
|
-
return JSON.stringify(entries);
|
|
14702
|
-
};
|
|
14703
|
-
return JSON.stringify({
|
|
14704
|
-
sdkMode: getSdkMode() || "public",
|
|
14705
|
-
url: normalizeArray(filters.url),
|
|
14706
|
-
projectId: filters.projectId || "all",
|
|
14707
|
-
externalUserId: filters.externalUserId || "",
|
|
14708
|
-
search: ((_a = filters.search) == null ? void 0 : _a.trim()) || "",
|
|
14709
|
-
dateFrom: filters.dateFrom || "",
|
|
14710
|
-
dateTo: filters.dateTo || "",
|
|
14711
|
-
page: filters.page ?? 0,
|
|
14712
|
-
pageSize: filters.pageSize ?? 0,
|
|
14713
|
-
custom: normalizeRecord(filters.customFieldValues),
|
|
14714
|
-
jira: normalizeRecord(filters.jiraFieldValues),
|
|
14715
|
-
notion: normalizeRecord(filters.notionFieldValues),
|
|
14716
|
-
sortOrder: filters.sortOrder || ""
|
|
14717
|
-
});
|
|
14718
|
-
}
|
|
14719
|
-
function invalidateIssuesCache() {
|
|
14720
|
-
issuesCache = null;
|
|
14721
|
-
}
|
|
14722
|
-
function recordToFilterArray(record, keyFieldName) {
|
|
14723
|
-
if (!record) return null;
|
|
14724
|
-
const entries = Object.entries(record).filter(([, values]) => values.length > 0);
|
|
14725
|
-
if (entries.length === 0) return null;
|
|
14726
|
-
return entries.map(
|
|
14727
|
-
([key, values]) => ({ [keyFieldName]: key, values })
|
|
14728
|
-
);
|
|
14729
|
-
}
|
|
14730
|
-
function buildFilterQueryParams(filters) {
|
|
14731
|
-
var _a;
|
|
14732
|
-
const params = new URLSearchParams();
|
|
14733
|
-
if (filters.projectId) {
|
|
14734
|
-
params.set("project_id", filters.projectId);
|
|
14735
|
-
}
|
|
14736
|
-
if (filters.url && filters.url !== "all") {
|
|
14737
|
-
if (Array.isArray(filters.url) && filters.url.length > 0) {
|
|
14738
|
-
params.set("url", filters.url.join(","));
|
|
14739
|
-
} else if (typeof filters.url === "string") {
|
|
14740
|
-
params.set("url", filters.url);
|
|
14741
|
-
}
|
|
14742
|
-
}
|
|
14743
|
-
const search2 = (_a = filters.search) == null ? void 0 : _a.trim();
|
|
14744
|
-
if (search2) {
|
|
14745
|
-
params.set("search", search2);
|
|
14746
|
-
}
|
|
14747
|
-
if (filters.dateFrom) {
|
|
14748
|
-
params.set("date_from", filters.dateFrom);
|
|
14749
|
-
}
|
|
14750
|
-
if (filters.dateTo) {
|
|
14751
|
-
params.set("date_to", filters.dateTo);
|
|
14752
|
-
}
|
|
14753
|
-
const customArr = recordToFilterArray(filters.customFieldValues, "fieldDefinitionId");
|
|
14754
|
-
if (customArr) {
|
|
14755
|
-
params.set("custom_field_filters", JSON.stringify(customArr));
|
|
14756
|
-
}
|
|
14757
|
-
const jiraArr = recordToFilterArray(filters.jiraFieldValues, "fieldName");
|
|
14758
|
-
if (jiraArr) {
|
|
14759
|
-
params.set("jira_field_filters", JSON.stringify(jiraArr));
|
|
14760
|
-
}
|
|
14761
|
-
const notionArr = recordToFilterArray(filters.notionFieldValues, "fieldName");
|
|
14762
|
-
if (notionArr) {
|
|
14763
|
-
params.set("notion_field_filters", JSON.stringify(notionArr));
|
|
14764
|
-
}
|
|
14765
|
-
if (filters.externalUserId) {
|
|
14766
|
-
params.set("external_user_id", filters.externalUserId);
|
|
14767
|
-
}
|
|
14768
|
-
if (typeof filters.page === "number" && filters.page > 0) {
|
|
14769
|
-
params.set("page", String(filters.page));
|
|
14770
|
-
}
|
|
14771
|
-
if (typeof filters.pageSize === "number" && filters.pageSize > 0) {
|
|
14772
|
-
params.set("page_size", String(filters.pageSize));
|
|
14773
|
-
}
|
|
14774
|
-
if (filters.sortOrder) {
|
|
14775
|
-
params.set("sort_order", filters.sortOrder);
|
|
14776
|
-
}
|
|
14777
|
-
const queryString = params.toString();
|
|
14778
|
-
return queryString ? `?${queryString}` : "";
|
|
14779
|
-
}
|
|
14780
|
-
const issueService = {
|
|
14781
|
-
async createIssue(issueData) {
|
|
14782
|
-
const sanitizedData = sanitizeString(issueData);
|
|
14783
|
-
const screenshotUploadIds = (sanitizedData.screenshot || []).map((url) => getRegisteredUploadId(url, "screenshot")).filter((id2) => typeof id2 === "string" && id2.length > 0);
|
|
14784
|
-
const videoUploadId = sanitizedData.video ? getRegisteredUploadId(sanitizedData.video, "video") : void 0;
|
|
14785
|
-
if (screenshotUploadIds.length > 0) {
|
|
14786
|
-
sanitizedData.screenshot_upload_ids = screenshotUploadIds;
|
|
14787
|
-
}
|
|
14788
|
-
if (videoUploadId) {
|
|
14789
|
-
sanitizedData.video_upload_id = videoUploadId;
|
|
14790
|
-
}
|
|
14791
|
-
const issue = await apiClient.post(issuesEndpoint(), sanitizedData);
|
|
14792
|
-
invalidateIssuesCache();
|
|
14793
|
-
return issue;
|
|
14794
|
-
},
|
|
14795
|
-
async getIssues(filters = {}, options = {}) {
|
|
14796
|
-
if (!filters.projectId) {
|
|
14797
|
-
throw new Error("프로젝트 ID가 필요합니다. 로그인 상태를 확인해주세요.");
|
|
14798
|
-
}
|
|
14799
|
-
const filterKey = getFilterKey(filters);
|
|
14800
|
-
const now2 = Date.now();
|
|
14801
|
-
if (!options.forceRefresh && issuesCache && issuesCache.filterKey === filterKey && now2 - issuesCache.timestamp < CACHE_DURATION_MS) {
|
|
14802
|
-
return issuesCache.data;
|
|
14803
|
-
}
|
|
14804
|
-
const queryParams = buildFilterQueryParams(filters);
|
|
14805
|
-
const { items: issues = [] } = await apiClient.get(
|
|
14806
|
-
`${issuesEndpoint()}${queryParams}`
|
|
14807
|
-
);
|
|
14808
|
-
issuesCache = {
|
|
14809
|
-
data: issues,
|
|
14810
|
-
timestamp: now2,
|
|
14811
|
-
filterKey
|
|
14812
|
-
};
|
|
14813
|
-
return issues;
|
|
14814
|
-
},
|
|
14815
|
-
async getIssueStickers(filters = {}, options = {}) {
|
|
14816
|
-
var _a, _b2;
|
|
14817
|
-
if (!filters.projectId) {
|
|
14818
|
-
throw new Error("프로젝트 ID가 필요합니다. 로그인 상태를 확인해주세요.");
|
|
14819
|
-
}
|
|
14820
|
-
const filterKey = `${getFilterKey(filters)}:stickers`;
|
|
14821
|
-
const now2 = Date.now();
|
|
14822
|
-
if (!options.forceRefresh && issuesCache && issuesCache.filterKey === filterKey && now2 - issuesCache.timestamp < CACHE_DURATION_MS) {
|
|
14823
|
-
return issuesCache.data;
|
|
14824
|
-
}
|
|
14825
|
-
const issues = [];
|
|
14826
|
-
let page = 1;
|
|
14827
|
-
let hasMore = true;
|
|
14828
|
-
while (hasMore && issues.length <= STICKER_FETCH_LIMIT) {
|
|
14829
|
-
const response = await apiClient.getResponse(
|
|
14830
|
-
`${issuesEndpoint()}/stickers${buildFilterQueryParams({
|
|
14831
|
-
...filters,
|
|
14832
|
-
page,
|
|
14833
|
-
pageSize: STICKER_PAGE_SIZE
|
|
14834
|
-
})}`
|
|
14835
|
-
);
|
|
14836
|
-
const pageItems = ((_a = response.data) == null ? void 0 : _a.items) ?? [];
|
|
14837
|
-
issues.push(...pageItems);
|
|
14838
|
-
hasMore = Boolean((_b2 = response.data) == null ? void 0 : _b2.hasMore) && pageItems.length > 0;
|
|
14839
|
-
page += 1;
|
|
14840
|
-
}
|
|
14841
|
-
issuesCache = {
|
|
14842
|
-
data: issues,
|
|
14843
|
-
timestamp: now2,
|
|
14844
|
-
filterKey
|
|
14845
|
-
};
|
|
14846
|
-
return issues;
|
|
14847
|
-
},
|
|
14848
|
-
invalidateCache() {
|
|
14849
|
-
invalidateIssuesCache();
|
|
14850
|
-
},
|
|
14851
|
-
// 계약 기준: 백엔드 GET /api/sdk/issues/:id 는 back#101부터 읽기-시점-스크럽된
|
|
14852
|
-
// llm_prompt 를 상세 응답에 포함한다(목록 응답에는 없음) — 스코프를 이 반환 타입에만 한정한다.
|
|
14853
|
-
async getIssue(id2) {
|
|
14854
|
-
return apiClient.get(
|
|
14855
|
-
`${issuesEndpoint()}/${id2}${guestOwnerQuery()}`
|
|
14856
|
-
);
|
|
14857
|
-
},
|
|
14858
|
-
async updateIssue(id2, updates) {
|
|
14859
|
-
const sanitizedUpdates = sanitizeString(updates);
|
|
14860
|
-
const issue = await apiClient.put(
|
|
14861
|
-
`${issuesEndpoint()}/${id2}${guestOwnerQuery()}`,
|
|
14862
|
-
sanitizedUpdates
|
|
14863
|
-
);
|
|
14864
|
-
invalidateIssuesCache();
|
|
14865
|
-
return issue;
|
|
14866
|
-
},
|
|
14867
|
-
/**
|
|
14868
|
-
* 재생성. 스펙상 세 채널 모두에서 되고 역할과 무관하다 — 검사는 역할이 아니라
|
|
14869
|
-
* 그 이슈에 접근할 수 있는지다(백엔드가 판정한다).
|
|
14870
|
-
*
|
|
14871
|
-
* 응답으로 갱신된 이슈가 오지만 상태는 다시 pending 이다. 호출부는 응답을
|
|
14872
|
-
* 그대로 쓰지 말고 폴링으로 이어받아야 한다.
|
|
14873
|
-
*/
|
|
14874
|
-
async regenerateIssue(id2) {
|
|
14875
|
-
const issue = await apiClient.post(
|
|
14876
|
-
`${issuesEndpoint()}/${encodeURIComponent(id2)}/regenerate${guestOwnerQuery()}`,
|
|
14877
|
-
{}
|
|
14878
|
-
);
|
|
14879
|
-
invalidateIssuesCache();
|
|
14880
|
-
return issue;
|
|
14881
|
-
},
|
|
14882
|
-
async deleteIssue(id2, options) {
|
|
14883
|
-
const endpoint = `${issuesEndpoint()}/${id2}${guestOwnerQuery()}`;
|
|
14884
|
-
if (options == null ? void 0 : options.deleteExternal) {
|
|
14885
|
-
await apiClient.delete(endpoint, {
|
|
14886
|
-
body: { deleteExternal: true }
|
|
14887
|
-
});
|
|
14888
|
-
} else {
|
|
14889
|
-
await apiClient.delete(endpoint);
|
|
14890
|
-
}
|
|
14891
|
-
invalidateIssuesCache();
|
|
14892
|
-
}
|
|
14893
|
-
};
|
|
14894
15251
|
async function loadIssuesFromServer(url, projectId, externalUserId, filters, options = {}) {
|
|
14895
15252
|
if (!projectId) return [];
|
|
14896
15253
|
try {
|
|
@@ -15068,18 +15425,18 @@ function UnrenderedStickerDialog({
|
|
|
15068
15425
|
{
|
|
15069
15426
|
role: "dialog",
|
|
15070
15427
|
"aria-modal": "false",
|
|
15071
|
-
"aria-label": "
|
|
15428
|
+
"aria-label": "표시하지 못한 스티커 목록",
|
|
15072
15429
|
className: "pointer-events-auto flex h-[min(480px,calc(100vh-64px))] w-[min(560px,calc(100vw-32px))] flex-col overflow-hidden rounded-xl border border-slate-200 bg-white text-slate-900 shadow-2xl",
|
|
15073
15430
|
children: [
|
|
15074
15431
|
/* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between border-b border-slate-100 px-5 py-3.5", children: [
|
|
15075
|
-
/* @__PURE__ */ jsx("h2", { className: "min-w-0 truncate text-sm font-semibold", children: "
|
|
15432
|
+
/* @__PURE__ */ jsx("h2", { className: "min-w-0 truncate text-sm font-semibold", children: "표시하지 못한 스티커" }),
|
|
15076
15433
|
/* @__PURE__ */ jsx(
|
|
15077
15434
|
"button",
|
|
15078
15435
|
{
|
|
15079
15436
|
type: "button",
|
|
15080
15437
|
className: "inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-slate-500 hover:bg-slate-100 hover:text-slate-900",
|
|
15081
15438
|
onClick: () => onOpenChange(false),
|
|
15082
|
-
"aria-label": "
|
|
15439
|
+
"aria-label": "표시하지 못한 스티커 목록 닫기",
|
|
15083
15440
|
children: /* @__PURE__ */ jsx(X$1, { className: "h-4 w-4" })
|
|
15084
15441
|
}
|
|
15085
15442
|
)
|
|
@@ -15141,7 +15498,7 @@ const FOCUSABLE_SELECTOR = [
|
|
|
15141
15498
|
"textarea:not([disabled])",
|
|
15142
15499
|
'[tabindex]:not([tabindex="-1"])'
|
|
15143
15500
|
].join(",");
|
|
15144
|
-
function isVisible(element2, container) {
|
|
15501
|
+
function isVisible$1(element2, container) {
|
|
15145
15502
|
if (getComputedStyle(element2).visibility === "hidden") return false;
|
|
15146
15503
|
let node2 = element2;
|
|
15147
15504
|
while (node2) {
|
|
@@ -15153,7 +15510,7 @@ function isVisible(element2, container) {
|
|
|
15153
15510
|
}
|
|
15154
15511
|
function tabbables(container) {
|
|
15155
15512
|
return Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)).filter(
|
|
15156
|
-
(element2) => element2.tabIndex >= 0 && isVisible(element2, container)
|
|
15513
|
+
(element2) => element2.tabIndex >= 0 && isVisible$1(element2, container)
|
|
15157
15514
|
);
|
|
15158
15515
|
}
|
|
15159
15516
|
function useOverlayFocus({
|
|
@@ -15257,7 +15614,8 @@ function OverlayShell({
|
|
|
15257
15614
|
}
|
|
15258
15615
|
const ISSUE_DRAFT_DISABLED_MESSAGE = "지금은 AI 작성을 사용할 수 없습니다. 제목과 설명을 직접 작성해주세요.";
|
|
15259
15616
|
const LABEL = "기대한 동작";
|
|
15260
|
-
const
|
|
15617
|
+
const ISSUE_INTENT_PLACEHOLDER = "기대한 동작을 적으면 AI가 제목과 설명을 작성합니다.\n예: 쿠폰을 적용하면 결제 금액이 바로 줄어야 합니다";
|
|
15618
|
+
const PLACEHOLDER = ISSUE_INTENT_PLACEHOLDER;
|
|
15261
15619
|
const SWITCH_TO_MANUAL = "직접 작성";
|
|
15262
15620
|
const SWITCH_TO_MANUAL_TOOLTIP = "제목, 설명 등 이슈 필드를 직접 작성합니다";
|
|
15263
15621
|
const SUBMIT = "보내기";
|
|
@@ -15294,20 +15652,28 @@ function IssueIntentBubble({
|
|
|
15294
15652
|
if (isFocused(node2) || Date.now() >= deadline) return;
|
|
15295
15653
|
frame2 = requestAnimationFrame(attempt);
|
|
15296
15654
|
};
|
|
15297
|
-
|
|
15655
|
+
const pointerScopes = /* @__PURE__ */ new Set([window, node2.getRootNode()]);
|
|
15656
|
+
pointerScopes.forEach(
|
|
15657
|
+
(scope) => scope.addEventListener("pointerdown", abandon, { capture: true })
|
|
15658
|
+
);
|
|
15298
15659
|
window.addEventListener("keydown", abandon, { capture: true });
|
|
15299
15660
|
attempt();
|
|
15300
15661
|
return () => {
|
|
15301
|
-
|
|
15662
|
+
pointerScopes.forEach(
|
|
15663
|
+
(scope) => scope.removeEventListener("pointerdown", abandon, { capture: true })
|
|
15664
|
+
);
|
|
15302
15665
|
window.removeEventListener("keydown", abandon, { capture: true });
|
|
15303
15666
|
if (frame2) cancelAnimationFrame(frame2);
|
|
15304
15667
|
};
|
|
15305
15668
|
}, []);
|
|
15669
|
+
const minHeightRef = useRef(null);
|
|
15306
15670
|
useLayoutEffect(() => {
|
|
15307
15671
|
const node2 = inputRef.current;
|
|
15308
15672
|
if (!node2) return;
|
|
15309
15673
|
node2.style.height = "auto";
|
|
15310
|
-
|
|
15674
|
+
minHeightRef.current ?? (minHeightRef.current = node2.scrollHeight);
|
|
15675
|
+
const next = Math.max(node2.scrollHeight, minHeightRef.current);
|
|
15676
|
+
node2.style.height = `${Math.min(next, MAX_INPUT_HEIGHT)}px`;
|
|
15311
15677
|
}, [value]);
|
|
15312
15678
|
return (
|
|
15313
15679
|
/*
|
|
@@ -15323,7 +15689,7 @@ function IssueIntentBubble({
|
|
|
15323
15689
|
/* @__PURE__ */ jsxs(
|
|
15324
15690
|
"div",
|
|
15325
15691
|
{
|
|
15326
|
-
className: `${ISSUE_STICKER_REPORT_FORM_CLASS} w-[min(
|
|
15692
|
+
className: `${ISSUE_STICKER_REPORT_FORM_CLASS} w-[min(24rem,calc(100vw_-_1.5rem))] space-y-1.5 rounded-md border border-line-subtle bg-surface-1 p-1.5 transition-colors duration-fast ease-out focus-within:border-line-focus`,
|
|
15327
15693
|
children: [
|
|
15328
15694
|
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-1.5", children: [
|
|
15329
15695
|
/* @__PURE__ */ jsx(
|
|
@@ -15633,7 +15999,9 @@ DialogClose$1.displayName = CLOSE_NAME;
|
|
|
15633
15999
|
function getState$2(open) {
|
|
15634
16000
|
return open ? "open" : "closed";
|
|
15635
16001
|
}
|
|
15636
|
-
const Dialog = Dialog$1
|
|
16002
|
+
const [Dialog, useDialogContentDismissRef] = createShadowDismissRoot(Dialog$1, {
|
|
16003
|
+
focusOutside: false
|
|
16004
|
+
});
|
|
15637
16005
|
const DialogClose = DialogClose$1;
|
|
15638
16006
|
const DialogPortal = ({ children }) => {
|
|
15639
16007
|
const portalContainer = usePortalContainer();
|
|
@@ -15666,13 +16034,13 @@ const DialogContent = React.forwardRef(
|
|
|
15666
16034
|
...props
|
|
15667
16035
|
}, ref) => {
|
|
15668
16036
|
const [content2, setContent] = React.useState(null);
|
|
16037
|
+
const dismissRef = useDialogContentDismissRef(ref);
|
|
15669
16038
|
const composedRef = React.useCallback(
|
|
15670
16039
|
(node2) => {
|
|
15671
16040
|
setContent(node2);
|
|
15672
|
-
|
|
15673
|
-
else if (ref) ref.current = node2;
|
|
16041
|
+
dismissRef(node2);
|
|
15674
16042
|
},
|
|
15675
|
-
[
|
|
16043
|
+
[dismissRef]
|
|
15676
16044
|
);
|
|
15677
16045
|
useOverlayFocus({ enabled: true, container: content2, initialFocusRef });
|
|
15678
16046
|
return /* @__PURE__ */ jsxs(DialogPortal, { children: [
|
|
@@ -15945,7 +16313,11 @@ function appendSnippet(base, body) {
|
|
|
15945
16313
|
${trimmedBody}`;
|
|
15946
16314
|
}
|
|
15947
16315
|
const TRIGGER_LABEL = "고정 문구 선택";
|
|
16316
|
+
const TRIGGER_HINT = "이슈 설명 끝에 추가할 고정 문구를 선택합니다";
|
|
15948
16317
|
const CLEAR_OPTION_LABEL = "선택 안 함";
|
|
16318
|
+
const EMPTY_TITLE = "등록된 고정 문구가 없습니다";
|
|
16319
|
+
const EMPTY_DESCRIPTION = "테스트 계정처럼 이슈마다 반복해서 적는 내용을 등록해두면 이슈 설명 끝에 추가됩니다.";
|
|
16320
|
+
const DEFAULT_EMPTY_HINT = "어드민 프로젝트 상세의 고정 문구 탭에서 등록합니다.";
|
|
15949
16321
|
const GROUP_LABEL = {
|
|
15950
16322
|
project: "공용 문구",
|
|
15951
16323
|
personal: "내 문구"
|
|
@@ -15954,7 +16326,8 @@ function SnippetPicker({
|
|
|
15954
16326
|
snippets,
|
|
15955
16327
|
selectedId,
|
|
15956
16328
|
onSelect,
|
|
15957
|
-
disabled = false
|
|
16329
|
+
disabled = false,
|
|
16330
|
+
emptyHint = DEFAULT_EMPTY_HINT
|
|
15958
16331
|
}) {
|
|
15959
16332
|
const [open, setOpen] = useState(false);
|
|
15960
16333
|
const listRef = useRef(null);
|
|
@@ -16013,7 +16386,8 @@ function SnippetPicker({
|
|
|
16013
16386
|
"button",
|
|
16014
16387
|
{
|
|
16015
16388
|
type: "button",
|
|
16016
|
-
|
|
16389
|
+
title: TRIGGER_HINT,
|
|
16390
|
+
disabled,
|
|
16017
16391
|
className: cn$1(
|
|
16018
16392
|
"inline-flex max-w-[10rem] items-center gap-1 rounded-sm border border-transparent px-2 py-1 text-xs text-text-secondary outline-none",
|
|
16019
16393
|
"hover:bg-surface-2 focus-visible:border-line-focus disabled:cursor-not-allowed disabled:opacity-40"
|
|
@@ -16031,8 +16405,18 @@ function SnippetPicker({
|
|
|
16031
16405
|
align: "start",
|
|
16032
16406
|
sideOffset: 4,
|
|
16033
16407
|
onEscapeKeyDown: stopEscapePropagation(),
|
|
16034
|
-
className: cn$1(
|
|
16035
|
-
|
|
16408
|
+
className: cn$1(
|
|
16409
|
+
`panel-shell-subtle p-1.5 shadow-lg ${OVERLAY_LAYER.floating}`,
|
|
16410
|
+
isEmpty ? "w-[min(30rem,calc(100vw_-_1.5rem))]" : "w-72"
|
|
16411
|
+
),
|
|
16412
|
+
children: isEmpty ? (
|
|
16413
|
+
// 비어 있다고 버튼을 막으면 무엇을 하는 버튼인지조차 알 수 없다. 열어서 이유를 알려준다.
|
|
16414
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1 break-keep px-2.5 py-2 [overflow-wrap:anywhere]", children: [
|
|
16415
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-text-primary", children: EMPTY_TITLE }),
|
|
16416
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-text-secondary", children: EMPTY_DESCRIPTION }),
|
|
16417
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-text-secondary", children: emptyHint })
|
|
16418
|
+
] })
|
|
16419
|
+
) : /* @__PURE__ */ jsxs(
|
|
16036
16420
|
"div",
|
|
16037
16421
|
{
|
|
16038
16422
|
ref: listRef,
|
|
@@ -16428,6 +16812,16 @@ async function captureScreenshot(options) {
|
|
|
16428
16812
|
}
|
|
16429
16813
|
return void 0;
|
|
16430
16814
|
}
|
|
16815
|
+
async function captureViewportAndElement(element2, excludeSelectors) {
|
|
16816
|
+
try {
|
|
16817
|
+
if (SDKManager.getInstance().isInitialized()) {
|
|
16818
|
+
return await captureViewportWithElementCrop({ element: element2, excludeSelectors });
|
|
16819
|
+
}
|
|
16820
|
+
} catch (err) {
|
|
16821
|
+
devLogger.error("screenshotCapture", "스크린샷 캡처 실패:", err);
|
|
16822
|
+
}
|
|
16823
|
+
return {};
|
|
16824
|
+
}
|
|
16431
16825
|
function toScreenshotList(dataUrls, dataUrl) {
|
|
16432
16826
|
if (dataUrls && dataUrls.length > 0) {
|
|
16433
16827
|
return dataUrls;
|
|
@@ -16549,15 +16943,17 @@ function useClickPosition(isCreatingIssueRef, recordedVideoFile) {
|
|
|
16549
16943
|
useEffect(() => {
|
|
16550
16944
|
const captureScreenshotsInBackground = async (position2) => {
|
|
16551
16945
|
await waitForFormOpen();
|
|
16552
|
-
const
|
|
16553
|
-
|
|
16554
|
-
|
|
16555
|
-
|
|
16556
|
-
|
|
16557
|
-
|
|
16558
|
-
|
|
16559
|
-
|
|
16560
|
-
|
|
16946
|
+
const { fullScreenshotDataUrl, elementScreenshotDataUrl } = await pauseNetworkLogCollectionWhile(async () => {
|
|
16947
|
+
const { fullDataUrl, elementDataUrl } = await captureViewportAndElement(
|
|
16948
|
+
position2.element,
|
|
16949
|
+
SCREENSHOT_EXCLUDE_SELECTORS
|
|
16950
|
+
);
|
|
16951
|
+
const element2 = elementDataUrl ?? await captureScreenshot({
|
|
16952
|
+
element: position2.element,
|
|
16953
|
+
excludeSelectors: SCREENSHOT_EXCLUDE_SELECTORS
|
|
16954
|
+
});
|
|
16955
|
+
return { fullScreenshotDataUrl: fullDataUrl, elementScreenshotDataUrl: element2 };
|
|
16956
|
+
});
|
|
16561
16957
|
const captured = {
|
|
16562
16958
|
screenshotDataUrl: elementScreenshotDataUrl,
|
|
16563
16959
|
screenshotDataUrls: [fullScreenshotDataUrl, elementScreenshotDataUrl].filter(
|
|
@@ -16597,6 +16993,7 @@ function useClickPosition(isCreatingIssueRef, recordedVideoFile) {
|
|
|
16597
16993
|
x: e.clientX + window.scrollX,
|
|
16598
16994
|
y: e.clientY + window.scrollY,
|
|
16599
16995
|
element: selectedElement,
|
|
16996
|
+
tracked: getElementPosition(selectedElement, e),
|
|
16600
16997
|
videoFile: recordedVideoFile ?? void 0
|
|
16601
16998
|
};
|
|
16602
16999
|
if (!newClickPosition.videoFile) {
|
|
@@ -16683,43 +17080,6 @@ function useScreenshotUpload() {
|
|
|
16683
17080
|
uploadMany
|
|
16684
17081
|
};
|
|
16685
17082
|
}
|
|
16686
|
-
async function uploadVideo(videoFile, issueId) {
|
|
16687
|
-
devLogger.log("videoUpload", "영상 파일 업로드 시작", {
|
|
16688
|
-
크기: `${(videoFile.size / 1024 / 1024).toFixed(2)}MB`,
|
|
16689
|
-
타입: videoFile.type
|
|
16690
|
-
});
|
|
16691
|
-
const result = await uploadSignedMedia(videoFile, "video", issueId);
|
|
16692
|
-
devLogger.log("videoUpload", "영상 파일 업로드 성공:", result.url);
|
|
16693
|
-
return result.url;
|
|
16694
|
-
}
|
|
16695
|
-
function useVideoUpload() {
|
|
16696
|
-
const upload = async (videoFile) => uploadVideo(videoFile);
|
|
16697
|
-
return {
|
|
16698
|
-
upload
|
|
16699
|
-
};
|
|
16700
|
-
}
|
|
16701
|
-
async function uploadSessionReplay(snapshot) {
|
|
16702
|
-
try {
|
|
16703
|
-
const file = new File([snapshot.payload], "session-replay.gz", {
|
|
16704
|
-
type: "application/gzip"
|
|
16705
|
-
});
|
|
16706
|
-
const { id: id2 } = await uploadSignedMedia(file, "replay");
|
|
16707
|
-
const marker = readReleaseMarker();
|
|
16708
|
-
return {
|
|
16709
|
-
uploadId: id2,
|
|
16710
|
-
meta: {
|
|
16711
|
-
window_seconds: snapshot.windowSeconds,
|
|
16712
|
-
event_count: snapshot.eventCount,
|
|
16713
|
-
size_bytes: snapshot.sizeBytes,
|
|
16714
|
-
rrweb_version: snapshot.rrwebVersion,
|
|
16715
|
-
privacy_config_version: snapshot.privacyConfigVersion,
|
|
16716
|
-
...marker ? { release: marker.release, release_source: marker.release_source } : {}
|
|
16717
|
-
}
|
|
16718
|
-
};
|
|
16719
|
-
} catch {
|
|
16720
|
-
return null;
|
|
16721
|
-
}
|
|
16722
|
-
}
|
|
16723
17083
|
function assertSubmittable(data) {
|
|
16724
17084
|
var _a, _b2, _c2;
|
|
16725
17085
|
const hasIntent = Boolean((_a = data.intent) == null ? void 0 : _a.trim());
|
|
@@ -16745,18 +17105,13 @@ function useIssueSubmission({
|
|
|
16745
17105
|
const { fetchConsoleLogs } = useConsoleLogs();
|
|
16746
17106
|
const { getAuthInfo } = useAuthInfo();
|
|
16747
17107
|
const { uploadMany: uploadScreenshots2 } = useScreenshotUpload();
|
|
16748
|
-
const { upload: uploadVideo2 } = useVideoUpload();
|
|
16749
17108
|
const createIssueMutation = useDebouncedMutation({
|
|
16750
17109
|
mutationFn: async (data) => {
|
|
16751
17110
|
var _a, _b2, _c2, _d2, _e2, _f2;
|
|
16752
17111
|
assertSubmittable(data);
|
|
16753
17112
|
let screenshotUrls;
|
|
16754
|
-
|
|
16755
|
-
if (
|
|
16756
|
-
videoUrl = await uploadVideo2(data.videoFile);
|
|
16757
|
-
} else if (data.position.videoFile) {
|
|
16758
|
-
videoUrl = await uploadVideo2(data.position.videoFile);
|
|
16759
|
-
} else {
|
|
17113
|
+
const videoFile2 = data.videoFile ?? data.position.videoFile;
|
|
17114
|
+
if (!videoFile2) {
|
|
16760
17115
|
const screenshotDataUrls = await resolveClickPositionScreenshots(data.position);
|
|
16761
17116
|
if (screenshotDataUrls.length > 0) {
|
|
16762
17117
|
const uploadedUrls = await uploadScreenshots2(screenshotDataUrls, {
|
|
@@ -16774,7 +17129,7 @@ function useIssueSubmission({
|
|
|
16774
17129
|
const limitedNetworkLogs = selectNetworkLogsForSubmit(filteredNetworkLogs);
|
|
16775
17130
|
const networkLogs = compactNetworkLogsForTransport(limitedNetworkLogs) ?? [];
|
|
16776
17131
|
const consoleLogs = filteredConsoleLogs.slice(-CONSOLE_LOG_SUBMIT_LIMIT);
|
|
16777
|
-
const positionData = getElementPosition(data.position.element, {
|
|
17132
|
+
const positionData = data.position.tracked ?? getElementPosition(data.position.element, {
|
|
16778
17133
|
clientX: data.position.x - window.scrollX,
|
|
16779
17134
|
clientY: data.position.y - window.scrollY
|
|
16780
17135
|
});
|
|
@@ -16792,10 +17147,12 @@ function useIssueSubmission({
|
|
|
16792
17147
|
selector: positionData.selector,
|
|
16793
17148
|
xpath: positionData.xpath,
|
|
16794
17149
|
viewport: positionData.viewport,
|
|
16795
|
-
elementRect: positionData.elementRect
|
|
17150
|
+
elementRect: positionData.elementRect,
|
|
17151
|
+
locator: positionData.locator,
|
|
17152
|
+
component: positionData.component
|
|
16796
17153
|
},
|
|
16797
17154
|
screenshot: screenshotUrls && screenshotUrls.length > 0 ? screenshotUrls : void 0,
|
|
16798
|
-
|
|
17155
|
+
...videoFile2 ? { video_pending: true } : {},
|
|
16799
17156
|
network_logs: networkLogs,
|
|
16800
17157
|
console_logs: consoleLogs.length > 0 ? consoleLogs : void 0,
|
|
16801
17158
|
user_agent: userAgent,
|
|
@@ -16810,39 +17167,17 @@ function useIssueSubmission({
|
|
|
16810
17167
|
if (((_e2 = issueData.custom_field_values) == null ? void 0 : _e2.length) === 0) {
|
|
16811
17168
|
delete issueData.custom_field_values;
|
|
16812
17169
|
}
|
|
16813
|
-
|
|
16814
|
-
|
|
16815
|
-
|
|
16816
|
-
|
|
16817
|
-
|
|
16818
|
-
|
|
16819
|
-
|
|
16820
|
-
|
|
16821
|
-
|
|
16822
|
-
|
|
16823
|
-
|
|
16824
|
-
devLogger.error(
|
|
16825
|
-
"useIssueSubmission",
|
|
16826
|
-
"세션 리플레이 첨부 실패 (이슈 생성 계속):",
|
|
16827
|
-
replayError
|
|
16828
|
-
);
|
|
16829
|
-
}
|
|
16830
|
-
}
|
|
16831
|
-
try {
|
|
16832
|
-
const socketSnapshot = await serializeSocketTranscript(
|
|
16833
|
-
getCollectorSession().socket.getConnections()
|
|
16834
|
-
);
|
|
16835
|
-
if (socketSnapshot) {
|
|
16836
|
-
const uploaded = await uploadSocketTranscript(
|
|
16837
|
-
{ post: (endpoint, body) => apiClient.post(endpoint, body), directUpload },
|
|
16838
|
-
socketSnapshot
|
|
16839
|
-
);
|
|
16840
|
-
if (uploaded) {
|
|
16841
|
-
issueData.socket_log_upload_id = uploaded.uploadId;
|
|
16842
|
-
}
|
|
16843
|
-
}
|
|
16844
|
-
} catch (socketError) {
|
|
16845
|
-
devLogger.error("useIssueSubmission", "소켓 전사 첨부 실패 (이슈 생성 계속):", socketError);
|
|
17170
|
+
const attachments = await collectAttachmentUploads({
|
|
17171
|
+
takeReplay: (sessionReplay == null ? void 0 : sessionReplay.canAttach) ? () => sessionReplay.takeSnapshot() : void 0,
|
|
17172
|
+
getConnections: () => getCollectorSession().socket.getConnections(),
|
|
17173
|
+
onError: (kind, error) => devLogger.error(
|
|
17174
|
+
"useIssueSubmission",
|
|
17175
|
+
kind === "replay" ? "세션 리플레이 스냅샷 실패 (이슈 생성 계속):" : "소켓 전사 직렬화 실패 (이슈 생성 계속):",
|
|
17176
|
+
error
|
|
17177
|
+
)
|
|
17178
|
+
});
|
|
17179
|
+
if (hasReplayUpload(attachments)) {
|
|
17180
|
+
issueData.session_replay_pending = true;
|
|
16846
17181
|
}
|
|
16847
17182
|
const { networkLogs: fittedNetworkLogs, stillOverBudget } = shrinkNetworkLogsToFit(
|
|
16848
17183
|
issueData.network_logs ?? [],
|
|
@@ -16861,6 +17196,16 @@ function useIssueSubmission({
|
|
|
16861
17196
|
notionProperties: data.notionFields
|
|
16862
17197
|
});
|
|
16863
17198
|
const createdIssue = await issueService.createIssue({ ...issueData, integrations });
|
|
17199
|
+
if (videoFile2 || attachments.length > 0) {
|
|
17200
|
+
await enqueueIssueUploads({
|
|
17201
|
+
videoQueue: getWidgetVideoUploadQueue(),
|
|
17202
|
+
attachmentQueue: getWidgetAttachmentUploadQueue(),
|
|
17203
|
+
issueId: createdIssue.id,
|
|
17204
|
+
projectKey: authInfo.projectId,
|
|
17205
|
+
video: videoFile2 ? { blob: videoFile2, mimeType: normalizeUploadMimeType(videoFile2.type, "video") } : null,
|
|
17206
|
+
attachments
|
|
17207
|
+
});
|
|
17208
|
+
}
|
|
16864
17209
|
for (const service of createdIssue.failed_services ?? []) {
|
|
16865
17210
|
toast.error(getIntegrationFailureMessage(service));
|
|
16866
17211
|
}
|
|
@@ -17502,6 +17847,7 @@ function IssueFormError({ error }) {
|
|
|
17502
17847
|
return /* @__PURE__ */ jsx("div", { className: "rounded-[18px] border border-danger/20 bg-danger-subtle px-4 py-3 text-sm text-danger", children: error });
|
|
17503
17848
|
}
|
|
17504
17849
|
const SWITCH_TO_AI = "AI 작성으로 돌아가기";
|
|
17850
|
+
const SWITCH_TO_AI_HINT = "기대한 동작만 적으면 AI가 제목과 설명을 작성합니다.";
|
|
17505
17851
|
function IssueFormHeader({ title, onClose, onSwitchToAi }) {
|
|
17506
17852
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
17507
17853
|
/* @__PURE__ */ jsx("h2", { className: "m-0 text-lg font-semibold text-text-primary", children: title }),
|
|
@@ -17518,7 +17864,10 @@ function IssueFormHeader({ title, onClose, onSwitchToAi }) {
|
|
|
17518
17864
|
children: /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4" })
|
|
17519
17865
|
}
|
|
17520
17866
|
) }),
|
|
17521
|
-
/* @__PURE__ */
|
|
17867
|
+
/* @__PURE__ */ jsxs(TooltipContent, { side: "top", sideOffset: 10, className: "max-w-[16rem]", children: [
|
|
17868
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium", children: SWITCH_TO_AI }),
|
|
17869
|
+
/* @__PURE__ */ jsx("p", { className: "mt-0.5 opacity-80", children: SWITCH_TO_AI_HINT })
|
|
17870
|
+
] })
|
|
17522
17871
|
] }) }) : null,
|
|
17523
17872
|
/* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: onClose, "aria-label": "닫기", children: /* @__PURE__ */ jsx(X$1, { className: "h-4 w-4" }) })
|
|
17524
17873
|
] })
|
|
@@ -18069,18 +18418,6 @@ function IssueForm({
|
|
|
18069
18418
|
getPortalContainer()
|
|
18070
18419
|
);
|
|
18071
18420
|
}
|
|
18072
|
-
function invalidateIssueQueries(issueId) {
|
|
18073
|
-
issueService.invalidateCache();
|
|
18074
|
-
queryClient.invalidateQueries({ queryKey: ["contentIssues"] });
|
|
18075
|
-
queryClient.invalidateQueries({ queryKey: ["issue", issueId] });
|
|
18076
|
-
}
|
|
18077
|
-
function notifyIssueUpdated(issueId) {
|
|
18078
|
-
SDKManager.getInstance().eventBus.emit("issue:updated", issueId);
|
|
18079
|
-
}
|
|
18080
|
-
function applyIssueMutationEffects(issueId) {
|
|
18081
|
-
invalidateIssueQueries(issueId);
|
|
18082
|
-
notifyIssueUpdated(issueId);
|
|
18083
|
-
}
|
|
18084
18421
|
function useJiraIssueSync(issue) {
|
|
18085
18422
|
const [isSyncingAll, setIsSyncingAll] = useState(false);
|
|
18086
18423
|
const mapping = getPrimaryJiraMapping(issue);
|
|
@@ -18640,7 +18977,7 @@ function ResizableSplitPane({
|
|
|
18640
18977
|
dividerClassName,
|
|
18641
18978
|
separatorLabel = "패널 크기 조절"
|
|
18642
18979
|
}) {
|
|
18643
|
-
const [
|
|
18980
|
+
const [ratio2, setRatio] = React.useState(() => clamp$1(defaultRatio, minRatio, maxRatio));
|
|
18644
18981
|
const containerRef = React.useRef(null);
|
|
18645
18982
|
const dragCleanupRef = React.useRef(null);
|
|
18646
18983
|
const updateRatio = React.useCallback(
|
|
@@ -18664,12 +19001,13 @@ function ResizableSplitPane({
|
|
|
18664
19001
|
var _a;
|
|
18665
19002
|
event.preventDefault();
|
|
18666
19003
|
(_a = dragCleanupRef.current) == null ? void 0 : _a.call(dragCleanupRef);
|
|
19004
|
+
const releaseScopes = /* @__PURE__ */ new Set([document, event.currentTarget.getRootNode()]);
|
|
18667
19005
|
const handleDocumentMouseMove = (moveEvent) => {
|
|
18668
19006
|
updateRatio(moveEvent.clientX);
|
|
18669
19007
|
};
|
|
18670
19008
|
const stopDragging = () => {
|
|
18671
19009
|
document.removeEventListener("mousemove", handleDocumentMouseMove);
|
|
18672
|
-
|
|
19010
|
+
releaseScopes.forEach((scope) => scope.removeEventListener("mouseup", stopDragging));
|
|
18673
19011
|
window.removeEventListener("blur", stopDragging);
|
|
18674
19012
|
if (dragCleanupRef.current === stopDragging) {
|
|
18675
19013
|
dragCleanupRef.current = null;
|
|
@@ -18677,7 +19015,7 @@ function ResizableSplitPane({
|
|
|
18677
19015
|
};
|
|
18678
19016
|
dragCleanupRef.current = stopDragging;
|
|
18679
19017
|
document.addEventListener("mousemove", handleDocumentMouseMove);
|
|
18680
|
-
|
|
19018
|
+
releaseScopes.forEach((scope) => scope.addEventListener("mouseup", stopDragging));
|
|
18681
19019
|
window.addEventListener("blur", stopDragging);
|
|
18682
19020
|
},
|
|
18683
19021
|
[updateRatio]
|
|
@@ -18697,14 +19035,14 @@ function ResizableSplitPane({
|
|
|
18697
19035
|
);
|
|
18698
19036
|
const minPercent = toPercent(minRatio);
|
|
18699
19037
|
const maxPercent = toPercent(maxRatio);
|
|
18700
|
-
const currentPercent = toPercent(
|
|
19038
|
+
const currentPercent = toPercent(ratio2);
|
|
18701
19039
|
const hasEnd = end !== null && end !== void 0 && end !== false;
|
|
18702
19040
|
return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: cn$1("flex overflow-hidden", className), children: [
|
|
18703
19041
|
/* @__PURE__ */ jsx(
|
|
18704
19042
|
"div",
|
|
18705
19043
|
{
|
|
18706
19044
|
className: cn$1("flex min-w-0 flex-col overflow-hidden", startClassName),
|
|
18707
|
-
style: { width: hasEnd ? formatRatioWidth(
|
|
19045
|
+
style: { width: hasEnd ? formatRatioWidth(ratio2) : "100%" },
|
|
18708
19046
|
children: start
|
|
18709
19047
|
}
|
|
18710
19048
|
),
|
|
@@ -21126,9 +21464,6 @@ const svg = merge([aria, svg$1, xlink, xmlns, xml], "svg");
|
|
|
21126
21464
|
function stringify(values) {
|
|
21127
21465
|
return values.join(" ").trim();
|
|
21128
21466
|
}
|
|
21129
|
-
function getDefaultExportFromCjs(x) {
|
|
21130
|
-
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
21131
|
-
}
|
|
21132
21467
|
var cjs$2 = {};
|
|
21133
21468
|
var cjs$1;
|
|
21134
21469
|
var hasRequiredCjs$2;
|
|
@@ -34733,9 +35068,20 @@ const markdownComponents = {
|
|
|
34733
35068
|
h2: ({ children }) => /* @__PURE__ */ jsx("h2", { className: "my-3 whitespace-pre-wrap text-lg font-semibold text-text-primary", children }),
|
|
34734
35069
|
h3: ({ children }) => /* @__PURE__ */ jsx("h3", { className: "my-2.5 whitespace-pre-wrap text-base font-semibold text-text-primary", children }),
|
|
34735
35070
|
p: ({ children }) => /* @__PURE__ */ jsx("p", { className: "my-2 whitespace-pre-wrap text-text-primary", children }),
|
|
34736
|
-
|
|
35071
|
+
// 노션 할 일 블록은 GFM 작업 목록(`- [ ]`)으로 들어온다. 글머리표 대신 체크박스가 그 자리를 맡는다.
|
|
35072
|
+
ul: ({ children, className }) => (className == null ? void 0 : className.includes("contains-task-list")) ? /* @__PURE__ */ jsx("ul", { className: "my-2 list-none pl-0 text-text-primary [li_&]:pl-5", children }) : /* @__PURE__ */ jsx("ul", { className: "my-2 list-disc pl-5 text-text-primary", children }),
|
|
34737
35073
|
ol: ({ children }) => /* @__PURE__ */ jsx("ol", { className: "my-2 list-decimal pl-5 text-text-primary", children }),
|
|
34738
35074
|
li: ({ children }) => /* @__PURE__ */ jsx("li", { className: "my-1", children }),
|
|
35075
|
+
input: ({ type, checked }) => type === "checkbox" ? /* @__PURE__ */ jsx(
|
|
35076
|
+
"input",
|
|
35077
|
+
{
|
|
35078
|
+
type: "checkbox",
|
|
35079
|
+
checked,
|
|
35080
|
+
disabled: true,
|
|
35081
|
+
readOnly: true,
|
|
35082
|
+
className: "mr-2 align-middle accent-brand"
|
|
35083
|
+
}
|
|
35084
|
+
) : null,
|
|
34739
35085
|
code: ({ children, className }) => {
|
|
34740
35086
|
const isInline = !className;
|
|
34741
35087
|
if (isInline) {
|
|
@@ -34752,7 +35098,8 @@ const markdownComponents = {
|
|
|
34752
35098
|
// 깊이만큼 밀린다.
|
|
34753
35099
|
details: ({ children }) => /* @__PURE__ */ jsx("details", { className: "my-2 [&>*:not(summary)]:pl-4", children }),
|
|
34754
35100
|
summary: ({ children }) => /* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-text-primary marker:text-text-tertiary", children }),
|
|
34755
|
-
|
|
35101
|
+
// 노션의 빈 파일·PDF 블록은 `[pdf]()` 로 들어온다. 빈 href 는 현재 페이지로 이동하므로 글자로만 둔다.
|
|
35102
|
+
a: ({ href, children }) => href ? /* @__PURE__ */ jsx(
|
|
34756
35103
|
"a",
|
|
34757
35104
|
{
|
|
34758
35105
|
href,
|
|
@@ -34761,7 +35108,7 @@ const markdownComponents = {
|
|
|
34761
35108
|
className: "text-brand underline underline-offset-2 hover:text-brand-hover",
|
|
34762
35109
|
children
|
|
34763
35110
|
}
|
|
34764
|
-
),
|
|
35111
|
+
) : /* @__PURE__ */ jsx("span", { className: "text-text-secondary", children }),
|
|
34765
35112
|
blockquote: ({ children }) => /* @__PURE__ */ jsx("blockquote", { className: "my-2 border-l-4 border-line-strong pl-3 italic text-text-secondary", children }),
|
|
34766
35113
|
hr: () => /* @__PURE__ */ jsx("hr", { className: "my-4 border-0 border-t border-line" }),
|
|
34767
35114
|
strong: ({ children }) => /* @__PURE__ */ jsx("strong", { className: "font-semibold", children }),
|
|
@@ -35248,6 +35595,10 @@ function ImageViewer({
|
|
|
35248
35595
|
title = "이미지 전체화면 보기"
|
|
35249
35596
|
}) {
|
|
35250
35597
|
const portalContainer = usePortalContainer();
|
|
35598
|
+
const dismissRef = useShadowOutsideDismiss(
|
|
35599
|
+
useCallback(() => onOpenChange(false), [onOpenChange]),
|
|
35600
|
+
{ focusOutside: false }
|
|
35601
|
+
);
|
|
35251
35602
|
return /* @__PURE__ */ jsx(Dialog$1, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogPortal$1, { container: portalContainer, children: [
|
|
35252
35603
|
/* @__PURE__ */ jsx(
|
|
35253
35604
|
DialogOverlay$1,
|
|
@@ -35262,6 +35613,7 @@ function ImageViewer({
|
|
|
35262
35613
|
/* @__PURE__ */ jsxs(
|
|
35263
35614
|
DialogContent$1,
|
|
35264
35615
|
{
|
|
35616
|
+
ref: dismissRef,
|
|
35265
35617
|
onEscapeKeyDown: stopEscapePropagation(),
|
|
35266
35618
|
style: {
|
|
35267
35619
|
left: `calc(50% - (var(${BODY_SCROLLBAR_WIDTH_CSS_VAR}, 0px) / 2))`
|
|
@@ -35299,6 +35651,11 @@ function ImageViewer({
|
|
|
35299
35651
|
)
|
|
35300
35652
|
] }) });
|
|
35301
35653
|
}
|
|
35654
|
+
function orderScreenshotsForDisplay(urls) {
|
|
35655
|
+
if (urls.length !== 2) return [...urls];
|
|
35656
|
+
const [full, element2] = urls;
|
|
35657
|
+
return [element2, full];
|
|
35658
|
+
}
|
|
35302
35659
|
function parseScreenshotUrls(screenshot) {
|
|
35303
35660
|
if (Array.isArray(screenshot)) {
|
|
35304
35661
|
return screenshot.filter((url) => typeof url === "string" && url.length > 0);
|
|
@@ -35321,7 +35678,7 @@ function parseScreenshotUrls(screenshot) {
|
|
|
35321
35678
|
return [screenshot];
|
|
35322
35679
|
}
|
|
35323
35680
|
function IssueScreenshot({ screenshot }) {
|
|
35324
|
-
const screenshotUrls = parseScreenshotUrls(screenshot);
|
|
35681
|
+
const screenshotUrls = orderScreenshotsForDisplay(parseScreenshotUrls(screenshot));
|
|
35325
35682
|
const [selectedImageUrl, setSelectedImageUrl] = useState(
|
|
35326
35683
|
screenshotUrls[0] ?? null
|
|
35327
35684
|
);
|
|
@@ -36584,81 +36941,372 @@ function IssuePopover({ issueId, onClose, onUpdate }) {
|
|
|
36584
36941
|
getPortalContainer()
|
|
36585
36942
|
);
|
|
36586
36943
|
}
|
|
36587
|
-
|
|
36588
|
-
|
|
36589
|
-
|
|
36590
|
-
|
|
36591
|
-
|
|
36592
|
-
|
|
36593
|
-
|
|
36594
|
-
|
|
36595
|
-
|
|
36596
|
-
|
|
36597
|
-
|
|
36598
|
-
|
|
36599
|
-
|
|
36600
|
-
|
|
36601
|
-
|
|
36602
|
-
|
|
36603
|
-
|
|
36604
|
-
|
|
36605
|
-
|
|
36606
|
-
|
|
36607
|
-
|
|
36608
|
-
|
|
36609
|
-
|
|
36610
|
-
|
|
36611
|
-
|
|
36612
|
-
|
|
36613
|
-
|
|
36614
|
-
|
|
36944
|
+
const LOCATOR_WEIGHTS = {
|
|
36945
|
+
testAttr: 3,
|
|
36946
|
+
role: 1.5,
|
|
36947
|
+
name: 1.5,
|
|
36948
|
+
text: 1.5,
|
|
36949
|
+
rowText: 1.5,
|
|
36950
|
+
tag: 1,
|
|
36951
|
+
heading: 1,
|
|
36952
|
+
landmark: 1,
|
|
36953
|
+
attrs: 1,
|
|
36954
|
+
roleIndex: 0.5,
|
|
36955
|
+
classes: 0.5,
|
|
36956
|
+
structural: 0.5,
|
|
36957
|
+
location: 0.5,
|
|
36958
|
+
size: 0.5
|
|
36959
|
+
};
|
|
36960
|
+
const LOCATION_RANGE_PX = 100;
|
|
36961
|
+
function textScore(stored, observed) {
|
|
36962
|
+
return observed ? textSimilarity(stored, observed) : 0;
|
|
36963
|
+
}
|
|
36964
|
+
function locationScore(stored, observed) {
|
|
36965
|
+
const dx = stored.left + stored.width / 2 - (observed.left + observed.width / 2);
|
|
36966
|
+
const dy = stored.top + stored.height / 2 - (observed.top + observed.height / 2);
|
|
36967
|
+
const distance2 = Math.hypot(dx, dy);
|
|
36968
|
+
return distance2 >= LOCATION_RANGE_PX ? 0 : 1 - distance2 / LOCATION_RANGE_PX;
|
|
36969
|
+
}
|
|
36970
|
+
function ratio(a, b) {
|
|
36971
|
+
if (a <= 0 || b <= 0) return a === b ? 1 : 0;
|
|
36972
|
+
return Math.min(a, b) / Math.max(a, b);
|
|
36973
|
+
}
|
|
36974
|
+
function sizeScore(stored, observed) {
|
|
36975
|
+
return ratio(stored.width, observed.width) * ratio(stored.height, observed.height);
|
|
36976
|
+
}
|
|
36977
|
+
function scoreLocator(stored, storedRect, observed) {
|
|
36978
|
+
var _a, _b2, _c2, _d2, _e2, _f2, _g2;
|
|
36979
|
+
const seen = observed.locator;
|
|
36980
|
+
let weighted = 0;
|
|
36981
|
+
let total = 0;
|
|
36982
|
+
const add = (weight, similarity) => {
|
|
36983
|
+
weighted += weight * similarity;
|
|
36984
|
+
total += weight;
|
|
36985
|
+
};
|
|
36986
|
+
const testKey = TEST_ATTR_KEYS.find((key) => {
|
|
36987
|
+
var _a2;
|
|
36988
|
+
return ((_a2 = stored.attrs) == null ? void 0 : _a2[key]) !== void 0;
|
|
36989
|
+
});
|
|
36990
|
+
if (testKey)
|
|
36991
|
+
add(LOCATOR_WEIGHTS.testAttr, ((_a = seen.attrs) == null ? void 0 : _a[testKey]) === ((_b2 = stored.attrs) == null ? void 0 : _b2[testKey]) ? 1 : 0);
|
|
36992
|
+
add(LOCATOR_WEIGHTS.tag, seen.tag === stored.tag ? 1 : 0);
|
|
36993
|
+
if (stored.role) add(LOCATOR_WEIGHTS.role, seen.role === stored.role ? 1 : 0);
|
|
36994
|
+
if (stored.name) add(LOCATOR_WEIGHTS.name, textScore(stored.name, seen.name));
|
|
36995
|
+
if (stored.text) add(LOCATOR_WEIGHTS.text, textScore(stored.text, seen.text));
|
|
36996
|
+
const context = stored.context;
|
|
36997
|
+
if (context == null ? void 0 : context.rowText) {
|
|
36998
|
+
add(
|
|
36999
|
+
LOCATOR_WEIGHTS.rowText,
|
|
37000
|
+
context.rowText.toLowerCase() === ((_d2 = (_c2 = seen.context) == null ? void 0 : _c2.rowText) == null ? void 0 : _d2.toLowerCase()) ? 1 : 0
|
|
37001
|
+
);
|
|
37002
|
+
}
|
|
37003
|
+
if (context == null ? void 0 : context.heading)
|
|
37004
|
+
add(LOCATOR_WEIGHTS.heading, context.heading === ((_e2 = seen.context) == null ? void 0 : _e2.heading) ? 1 : 0);
|
|
37005
|
+
if (context == null ? void 0 : context.landmark)
|
|
37006
|
+
add(LOCATOR_WEIGHTS.landmark, context.landmark === ((_f2 = seen.context) == null ? void 0 : _f2.landmark) ? 1 : 0);
|
|
37007
|
+
const otherKeys = Object.keys(stored.attrs ?? {}).filter(
|
|
37008
|
+
(key) => !TEST_ATTR_KEYS.includes(key)
|
|
37009
|
+
);
|
|
37010
|
+
if (otherKeys.length) {
|
|
37011
|
+
const matched = otherKeys.filter((key) => {
|
|
37012
|
+
var _a2, _b3;
|
|
37013
|
+
return ((_a2 = seen.attrs) == null ? void 0 : _a2[key]) === ((_b3 = stored.attrs) == null ? void 0 : _b3[key]);
|
|
37014
|
+
}).length;
|
|
37015
|
+
add(LOCATOR_WEIGHTS.attrs, matched / otherKeys.length);
|
|
37016
|
+
}
|
|
37017
|
+
if (stored.roleIndex !== void 0 && seen.roleIndex !== void 0) {
|
|
37018
|
+
const gap = Math.abs(stored.roleIndex - seen.roleIndex);
|
|
37019
|
+
add(LOCATOR_WEIGHTS.roleIndex, gap === 0 ? 1 : gap === 1 ? 0.5 : 0);
|
|
37020
|
+
}
|
|
37021
|
+
if ((_g2 = stored.classes) == null ? void 0 : _g2.length)
|
|
37022
|
+
add(LOCATOR_WEIGHTS.classes, jaccard(stored.classes, seen.classes ?? []));
|
|
37023
|
+
if (observed.structuralHit !== void 0)
|
|
37024
|
+
add(LOCATOR_WEIGHTS.structural, observed.structuralHit ? 1 : 0);
|
|
37025
|
+
if (storedRect) {
|
|
37026
|
+
add(LOCATOR_WEIGHTS.location, locationScore(storedRect, observed.rect));
|
|
37027
|
+
add(LOCATOR_WEIGHTS.size, sizeScore(storedRect, observed.rect));
|
|
37028
|
+
}
|
|
37029
|
+
return total === 0 ? 0 : weighted / total;
|
|
37030
|
+
}
|
|
37031
|
+
const LOCATOR_ACCEPT_SCORE = 0.65;
|
|
37032
|
+
const LOCATOR_MIN_MARGIN = 0.1;
|
|
37033
|
+
const CANDIDATE_LIMIT = 300;
|
|
37034
|
+
const NAME_EVAL_LIMIT = 20;
|
|
37035
|
+
const TEXT_PROBE_LENGTH = 640;
|
|
37036
|
+
const LABEL_MIN_SIMILARITY = 0.5;
|
|
37037
|
+
const LOCATOR_MISS_RETRY_MS = 1e3;
|
|
37038
|
+
function documentRect(element2) {
|
|
37039
|
+
const rect = element2.getBoundingClientRect();
|
|
37040
|
+
const view = element2.ownerDocument.defaultView;
|
|
37041
|
+
return {
|
|
37042
|
+
left: rect.left + ((view == null ? void 0 : view.scrollX) ?? 0),
|
|
37043
|
+
top: rect.top + ((view == null ? void 0 : view.scrollY) ?? 0),
|
|
37044
|
+
width: rect.width,
|
|
37045
|
+
height: rect.height
|
|
37046
|
+
};
|
|
37047
|
+
}
|
|
37048
|
+
const HIDDEN_ANCESTOR_LIMIT = 50;
|
|
37049
|
+
function isVisible(element2) {
|
|
37050
|
+
return typeof element2.checkVisibility !== "function" || element2.checkVisibility();
|
|
37051
|
+
}
|
|
37052
|
+
function isRendered(node2) {
|
|
37053
|
+
if (!isVisible(node2)) return false;
|
|
37054
|
+
const view = node2.ownerDocument.defaultView;
|
|
37055
|
+
let current = node2;
|
|
37056
|
+
for (let depth = 0; current && depth < HIDDEN_ANCESTOR_LIMIT; depth += 1) {
|
|
37057
|
+
if (current.hasAttribute("hidden") || current.getAttribute("aria-hidden") === "true") {
|
|
37058
|
+
return false;
|
|
36615
37059
|
}
|
|
36616
|
-
|
|
36617
|
-
|
|
36618
|
-
|
|
36619
|
-
|
|
36620
|
-
|
|
36621
|
-
}
|
|
36622
|
-
|
|
36623
|
-
invoke();
|
|
36624
|
-
};
|
|
36625
|
-
const debounced = function(...args) {
|
|
36626
|
-
if (signal == null ? void 0 : signal.aborted) return;
|
|
36627
|
-
pendingThis = this;
|
|
36628
|
-
pendingArgs = args;
|
|
36629
|
-
const isFirstCall = timeoutId == null;
|
|
36630
|
-
schedule();
|
|
36631
|
-
if (leading && isFirstCall) invoke();
|
|
36632
|
-
};
|
|
36633
|
-
debounced.schedule = schedule;
|
|
36634
|
-
debounced.cancel = cancel;
|
|
36635
|
-
debounced.flush = flush;
|
|
36636
|
-
signal == null ? void 0 : signal.addEventListener("abort", cancel, { once: true });
|
|
36637
|
-
return debounced;
|
|
37060
|
+
if (current.localName === "dialog" && !current.hasAttribute("open")) return false;
|
|
37061
|
+
const style = view == null ? void 0 : view.getComputedStyle(current);
|
|
37062
|
+
if ((style == null ? void 0 : style.display) === "none") return false;
|
|
37063
|
+
if (current === node2 && (style == null ? void 0 : style.visibility) === "hidden") return false;
|
|
37064
|
+
current = current.parentElement;
|
|
37065
|
+
}
|
|
37066
|
+
return true;
|
|
36638
37067
|
}
|
|
36639
|
-
function
|
|
36640
|
-
|
|
36641
|
-
const
|
|
36642
|
-
|
|
36643
|
-
|
|
36644
|
-
|
|
36645
|
-
|
|
36646
|
-
|
|
37068
|
+
function findScopes(locator, doc) {
|
|
37069
|
+
var _a;
|
|
37070
|
+
const container = (_a = locator.context) == null ? void 0 : _a.container;
|
|
37071
|
+
if (!container) return [doc];
|
|
37072
|
+
const wanted = container.name ? normalizeForCompare(container.name) : null;
|
|
37073
|
+
const scopes = Array.from(doc.querySelectorAll(CONTAINER_SELECTOR)).filter((node2) => {
|
|
37074
|
+
if (safeRole(node2) !== container.role) return false;
|
|
37075
|
+
if (!isRendered(node2)) return false;
|
|
37076
|
+
if (!wanted) return true;
|
|
37077
|
+
const name2 = safeAccessibleName(node2);
|
|
37078
|
+
return name2 !== void 0 && normalizeForCompare(name2) === wanted;
|
|
36647
37079
|
});
|
|
36648
|
-
|
|
36649
|
-
|
|
36650
|
-
|
|
36651
|
-
|
|
36652
|
-
|
|
36653
|
-
|
|
36654
|
-
|
|
36655
|
-
|
|
37080
|
+
return scopes.length ? scopes : null;
|
|
37081
|
+
}
|
|
37082
|
+
function collectCandidates(locator, scopes) {
|
|
37083
|
+
const found = /* @__PURE__ */ new Set();
|
|
37084
|
+
const role = locator.role && /^[a-z]+$/.test(locator.role) ? locator.role : void 0;
|
|
37085
|
+
for (const scope of scopes) {
|
|
37086
|
+
for (const el of Array.from(scope.getElementsByTagName(locator.tag))) {
|
|
37087
|
+
found.add(el);
|
|
37088
|
+
}
|
|
37089
|
+
if (role) {
|
|
37090
|
+
scope.querySelectorAll(`[role="${role}"]`).forEach((el) => found.add(el));
|
|
37091
|
+
}
|
|
37092
|
+
}
|
|
37093
|
+
const candidates = Array.from(found);
|
|
37094
|
+
if (candidates.length <= CANDIDATE_LIMIT) return candidates;
|
|
37095
|
+
const label = locator.text ?? locator.name;
|
|
37096
|
+
if (!label) return null;
|
|
37097
|
+
const firstWord = normalizeForCompare(label).split(" ")[0] ?? "";
|
|
37098
|
+
const narrowed = candidates.filter(
|
|
37099
|
+
(el) => normalizeForCompare((el.textContent ?? "").slice(0, TEXT_PROBE_LENGTH)).includes(firstWord)
|
|
37100
|
+
);
|
|
37101
|
+
return narrowed.length <= CANDIDATE_LIMIT ? narrowed : null;
|
|
37102
|
+
}
|
|
37103
|
+
function isInScopes(element2, scopes) {
|
|
37104
|
+
return scopes.some((scope) => scope === element2.ownerDocument || scope.contains(element2));
|
|
37105
|
+
}
|
|
37106
|
+
function testAttrKey(stored) {
|
|
37107
|
+
return TEST_ATTR_KEYS.find((key) => {
|
|
37108
|
+
var _a;
|
|
37109
|
+
return ((_a = stored.attrs) == null ? void 0 : _a[key]) !== void 0;
|
|
37110
|
+
});
|
|
37111
|
+
}
|
|
37112
|
+
const IDENTITY_ATTR_KEYS = ["href", "src", "alt", "placeholder", "name"];
|
|
37113
|
+
const URL_ATTR_KEYS = ["href", "src"];
|
|
37114
|
+
function sameAttrs(stored, seen, keys2) {
|
|
37115
|
+
return keys2.every((key) => {
|
|
37116
|
+
var _a, _b2;
|
|
37117
|
+
const wanted = (_a = stored.attrs) == null ? void 0 : _a[key];
|
|
37118
|
+
return wanted === void 0 || ((_b2 = seen.attrs) == null ? void 0 : _b2[key]) === wanted;
|
|
37119
|
+
});
|
|
37120
|
+
}
|
|
37121
|
+
function sameRowText(stored, seen) {
|
|
37122
|
+
var _a, _b2, _c2;
|
|
37123
|
+
const wanted = (_a = stored.context) == null ? void 0 : _a.rowText;
|
|
37124
|
+
return !wanted || wanted.toLowerCase() === ((_c2 = (_b2 = seen.context) == null ? void 0 : _b2.rowText) == null ? void 0 : _c2.toLowerCase());
|
|
37125
|
+
}
|
|
37126
|
+
function sameLandmark(stored, seen) {
|
|
37127
|
+
var _a, _b2;
|
|
37128
|
+
const wanted = (_a = stored.context) == null ? void 0 : _a.landmark;
|
|
37129
|
+
return !wanted || wanted === ((_b2 = seen.context) == null ? void 0 : _b2.landmark);
|
|
37130
|
+
}
|
|
37131
|
+
function sameHeading(stored, seen, exact) {
|
|
37132
|
+
var _a, _b2;
|
|
37133
|
+
const wanted = (_a = stored.context) == null ? void 0 : _a.heading;
|
|
37134
|
+
if (!wanted) return true;
|
|
37135
|
+
const got = (_b2 = seen.context) == null ? void 0 : _b2.heading;
|
|
37136
|
+
if (got === void 0) return false;
|
|
37137
|
+
return exact ? got.toLowerCase() === wanted.toLowerCase() : normalizeForCompare(got) === normalizeForCompare(wanted);
|
|
37138
|
+
}
|
|
37139
|
+
const WRAPPER_LANDMARKS = /* @__PURE__ */ new Set(["region", "form"]);
|
|
37140
|
+
function withinStoredLandmark(stored, element2) {
|
|
37141
|
+
var _a;
|
|
37142
|
+
const wanted = (_a = stored.context) == null ? void 0 : _a.landmark;
|
|
37143
|
+
if (!wanted) return true;
|
|
37144
|
+
for (let node2 = element2.parentElement; node2; node2 = node2.parentElement) {
|
|
37145
|
+
const role = landmarkRole(node2);
|
|
37146
|
+
if (role === wanted) return true;
|
|
37147
|
+
if (role !== void 0 && !WRAPPER_LANDMARKS.has(role)) return false;
|
|
37148
|
+
}
|
|
37149
|
+
return false;
|
|
37150
|
+
}
|
|
37151
|
+
function matchesContainerPresence(stored, element2) {
|
|
37152
|
+
var _a, _b2;
|
|
37153
|
+
return Boolean((_a = stored.context) == null ? void 0 : _a.container) || !((_b2 = element2.parentElement) == null ? void 0 : _b2.closest(CONTAINER_SELECTOR));
|
|
37154
|
+
}
|
|
37155
|
+
function sameFastIdentity(stored, seen) {
|
|
37156
|
+
var _a, _b2;
|
|
37157
|
+
const testKey = testAttrKey(stored);
|
|
37158
|
+
if (testKey && ((_a = seen.attrs) == null ? void 0 : _a[testKey]) !== ((_b2 = stored.attrs) == null ? void 0 : _b2[testKey])) return false;
|
|
37159
|
+
for (const field of ["name", "text"]) {
|
|
37160
|
+
const wanted = stored[field];
|
|
37161
|
+
if (wanted === void 0) continue;
|
|
37162
|
+
const got = seen[field];
|
|
37163
|
+
if (got === void 0 || normalizeForCompare(got) !== normalizeForCompare(wanted)) return false;
|
|
37164
|
+
}
|
|
37165
|
+
return sameAttrs(stored, seen, URL_ATTR_KEYS) && sameHeading(stored, seen, false) && sameRowText(stored, seen);
|
|
37166
|
+
}
|
|
37167
|
+
function acceptsKnownElement(target, element2, observed) {
|
|
37168
|
+
const { locator, elementRect } = target;
|
|
37169
|
+
return matchesContainerPresence(locator, element2) && withinStoredLandmark(locator, element2) && sameFastIdentity(locator, observed.locator) && scoreLocator(locator, elementRect, observed) >= LOCATOR_ACCEPT_SCORE;
|
|
37170
|
+
}
|
|
37171
|
+
function passesScoredIdentity(stored, seen, nameKnown) {
|
|
37172
|
+
var _a, _b2;
|
|
37173
|
+
const testKey = testAttrKey(stored);
|
|
37174
|
+
if (testKey && ((_a = seen.attrs) == null ? void 0 : _a[testKey]) !== ((_b2 = stored.attrs) == null ? void 0 : _b2[testKey])) return false;
|
|
37175
|
+
if (!sameRowText(stored, seen) || !sameLandmark(stored, seen)) return false;
|
|
37176
|
+
if (!sameHeading(stored, seen, true) || !sameAttrs(stored, seen, IDENTITY_ATTR_KEYS))
|
|
37177
|
+
return false;
|
|
37178
|
+
for (const field of ["name", "text"]) {
|
|
37179
|
+
const wanted = stored[field];
|
|
37180
|
+
const got = seen[field];
|
|
37181
|
+
if (wanted && got && !sameDigitGroups(wanted, got)) return false;
|
|
37182
|
+
}
|
|
37183
|
+
if (stored.name && !nameKnown) return true;
|
|
37184
|
+
const similarities = [];
|
|
37185
|
+
for (const field of ["name", "text"]) {
|
|
37186
|
+
const wanted = stored[field];
|
|
37187
|
+
if (!wanted) continue;
|
|
37188
|
+
const got = seen[field];
|
|
37189
|
+
similarities.push(got ? labelSimilarityKeepingDigits(wanted, got) : 0);
|
|
37190
|
+
}
|
|
37191
|
+
return similarities.length === 0 || Math.max(...similarities) >= LABEL_MIN_SIMILARITY;
|
|
37192
|
+
}
|
|
37193
|
+
function hasContextIdentity(stored) {
|
|
37194
|
+
const context = stored.context;
|
|
37195
|
+
return Boolean(
|
|
37196
|
+
testAttrKey(stored) || (context == null ? void 0 : context.rowText) || (context == null ? void 0 : context.heading) || (context == null ? void 0 : context.landmark) || (context == null ? void 0 : context.container)
|
|
37197
|
+
);
|
|
37198
|
+
}
|
|
37199
|
+
function hasElementIdentity(stored) {
|
|
37200
|
+
var _a;
|
|
37201
|
+
return Boolean(
|
|
37202
|
+
testAttrKey(stored) || stored.name || stored.text || ((_a = stored.context) == null ? void 0 : _a.rowText) || IDENTITY_ATTR_KEYS.some((key) => {
|
|
37203
|
+
var _a2;
|
|
37204
|
+
return ((_a2 = stored.attrs) == null ? void 0 : _a2[key]) !== void 0;
|
|
37205
|
+
})
|
|
37206
|
+
);
|
|
37207
|
+
}
|
|
37208
|
+
function resolveByLocator(target, structuralElement, doc = document, options = {}) {
|
|
37209
|
+
var _a, _b2;
|
|
37210
|
+
try {
|
|
37211
|
+
const { locator, elementRect } = target;
|
|
37212
|
+
const scopes = findScopes(locator, doc);
|
|
37213
|
+
if (!scopes) {
|
|
37214
|
+
return { kind: "container-closed", containerName: (_b2 = (_a = locator.context) == null ? void 0 : _a.container) == null ? void 0 : _b2.name };
|
|
37215
|
+
}
|
|
37216
|
+
const headings = options.headings ?? collectHeadings(doc);
|
|
37217
|
+
const observe = (element2, includeName) => ({
|
|
37218
|
+
locator: buildElementLocator(element2, { includeName, headings, uniqueUrlAttrs: false }),
|
|
37219
|
+
rect: documentRect(element2),
|
|
37220
|
+
structuralHit: structuralElement ? element2 === structuralElement : void 0
|
|
37221
|
+
});
|
|
37222
|
+
if (structuralElement && isInScopes(structuralElement, scopes)) {
|
|
37223
|
+
if (acceptsKnownElement(target, structuralElement, observe(structuralElement, true))) {
|
|
37224
|
+
return { kind: "found", element: structuralElement, via: "fast" };
|
|
37225
|
+
}
|
|
36656
37226
|
}
|
|
36657
|
-
|
|
37227
|
+
if (!hasElementIdentity(locator)) return { kind: "not-found" };
|
|
37228
|
+
const collected = collectCandidates(locator, scopes);
|
|
37229
|
+
if (!collected) return { kind: "not-found" };
|
|
37230
|
+
const candidates = collected.filter((el) => matchesContainerPresence(locator, el));
|
|
37231
|
+
const withoutName = { ...locator, name: void 0 };
|
|
37232
|
+
const shortlist = candidates.map((element2) => ({ element: element2, observed: observe(element2, false) })).filter(({ observed }) => passesScoredIdentity(locator, observed.locator, false)).map(({ element: element2, observed }) => ({
|
|
37233
|
+
element: element2,
|
|
37234
|
+
score: scoreLocator(withoutName, elementRect, observed)
|
|
37235
|
+
})).sort((a, b) => b.score - a.score).slice(0, NAME_EVAL_LIMIT);
|
|
37236
|
+
const identified = shortlist.map(({ element: element2 }) => ({ element: element2, observed: observe(element2, true) })).filter(({ observed }) => passesScoredIdentity(locator, observed.locator, true));
|
|
37237
|
+
if (identified.some(({ element: element2 }) => !isVisible(element2))) return { kind: "not-found" };
|
|
37238
|
+
const ranked = identified.map(({ element: element2, observed }) => ({
|
|
37239
|
+
element: element2,
|
|
37240
|
+
score: scoreLocator(locator, elementRect, observed)
|
|
37241
|
+
})).sort((a, b) => b.score - a.score);
|
|
37242
|
+
const [first, second] = ranked;
|
|
37243
|
+
if (!first || first.score < LOCATOR_ACCEPT_SCORE) return { kind: "not-found" };
|
|
37244
|
+
if (second && first.score - second.score < LOCATOR_MIN_MARGIN) return { kind: "ambiguous" };
|
|
37245
|
+
if (!hasContextIdentity(locator)) return { kind: "not-found" };
|
|
37246
|
+
return { kind: "found", element: first.element, via: "scored" };
|
|
37247
|
+
} catch {
|
|
37248
|
+
return { kind: "not-found" };
|
|
37249
|
+
}
|
|
37250
|
+
}
|
|
37251
|
+
function acceptsCachedElement(target, element2, doc, headings) {
|
|
37252
|
+
if (!element2.isConnected || !isVisible(element2)) return false;
|
|
37253
|
+
const scopes = findScopes(target.locator, doc);
|
|
37254
|
+
if (!scopes || !isInScopes(element2, scopes)) return false;
|
|
37255
|
+
const observed = {
|
|
37256
|
+
locator: buildElementLocator(element2, {
|
|
37257
|
+
includeName: Boolean(target.locator.name),
|
|
37258
|
+
includeRoleIndex: false,
|
|
37259
|
+
uniqueUrlAttrs: false,
|
|
37260
|
+
headings
|
|
37261
|
+
}),
|
|
37262
|
+
rect: documentRect(element2)
|
|
36658
37263
|
};
|
|
36659
|
-
|
|
36660
|
-
|
|
36661
|
-
|
|
37264
|
+
return acceptsKnownElement(target, element2, observed);
|
|
37265
|
+
}
|
|
37266
|
+
function createLocatorResolverCache() {
|
|
37267
|
+
return { found: /* @__PURE__ */ new Map(), missed: /* @__PURE__ */ new Map() };
|
|
37268
|
+
}
|
|
37269
|
+
function pruneLocatorCache(cache, liveKeys) {
|
|
37270
|
+
for (const [key, element2] of cache.found) {
|
|
37271
|
+
if (!liveKeys.has(key) || !element2.isConnected) cache.found.delete(key);
|
|
37272
|
+
}
|
|
37273
|
+
for (const key of cache.missed.keys()) {
|
|
37274
|
+
if (!liveKeys.has(key)) cache.missed.delete(key);
|
|
37275
|
+
}
|
|
37276
|
+
}
|
|
37277
|
+
function reusableMiss(cache, key, childListVersion, now2) {
|
|
37278
|
+
const missed = cache.missed.get(key);
|
|
37279
|
+
if (!missed) return void 0;
|
|
37280
|
+
if (missed.version === childListVersion) return missed.result;
|
|
37281
|
+
const due = missed.at + LOCATOR_MISS_RETRY_MS;
|
|
37282
|
+
if (now2 >= due) return void 0;
|
|
37283
|
+
cache.retryAt = Math.min(cache.retryAt ?? due, due);
|
|
37284
|
+
return missed.result;
|
|
37285
|
+
}
|
|
37286
|
+
function resolveByLocatorCached(key, target, structural, cache, childListVersion, doc = document, options = {}) {
|
|
37287
|
+
try {
|
|
37288
|
+
const cached2 = cache.found.get(key);
|
|
37289
|
+
if (cached2 && acceptsCachedElement(target, cached2, doc, options.headings)) {
|
|
37290
|
+
return { kind: "found", element: cached2, via: "fast" };
|
|
37291
|
+
}
|
|
37292
|
+
cache.found.delete(key);
|
|
37293
|
+
const now2 = Date.now();
|
|
37294
|
+
const reused = reusableMiss(cache, key, childListVersion, now2);
|
|
37295
|
+
if (reused) return reused;
|
|
37296
|
+
const structuralElement = typeof structural === "function" ? structural() : structural;
|
|
37297
|
+
const result = resolveByLocator(target, structuralElement, doc, options);
|
|
37298
|
+
if (result.kind === "found") {
|
|
37299
|
+
cache.found.set(key, result.element);
|
|
37300
|
+
cache.missed.delete(key);
|
|
37301
|
+
} else if (result.kind === "container-closed") {
|
|
37302
|
+
cache.missed.delete(key);
|
|
37303
|
+
} else {
|
|
37304
|
+
cache.missed.set(key, { version: childListVersion, at: now2, result });
|
|
37305
|
+
}
|
|
37306
|
+
return result;
|
|
37307
|
+
} catch {
|
|
37308
|
+
return { kind: "not-found" };
|
|
37309
|
+
}
|
|
36662
37310
|
}
|
|
36663
37311
|
const STICKER_RENDER_MODE_SETTING_KEY = "sticker_render_mode";
|
|
36664
37312
|
const DEFAULT_STICKER_RENDER_MODE = "absolute";
|
|
@@ -36735,7 +37383,7 @@ function isUniqueSelectorMatch(selector, element2) {
|
|
|
36735
37383
|
return false;
|
|
36736
37384
|
}
|
|
36737
37385
|
}
|
|
36738
|
-
function
|
|
37386
|
+
function resolveStructuralElement(position2) {
|
|
36739
37387
|
const selectorElement = position2.selector ? findElementBySelector(position2.selector) : null;
|
|
36740
37388
|
if (selectorElement && position2.selector && isUniqueSelectorMatch(position2.selector, selectorElement)) {
|
|
36741
37389
|
return selectorElement;
|
|
@@ -36747,10 +37395,30 @@ function resolveTargetElement(position2) {
|
|
|
36747
37395
|
if (selectorElement) return selectorElement;
|
|
36748
37396
|
return null;
|
|
36749
37397
|
}
|
|
36750
|
-
function
|
|
36751
|
-
|
|
36752
|
-
|
|
37398
|
+
function locatorOf(position2) {
|
|
37399
|
+
var _a;
|
|
37400
|
+
return ((_a = position2.locator) == null ? void 0 : _a.v) === 1 ? position2.locator : void 0;
|
|
37401
|
+
}
|
|
37402
|
+
function resolveTarget(issueId, position2, cache, childListVersion, options) {
|
|
37403
|
+
const locator = locatorOf(position2);
|
|
37404
|
+
if (!locator) {
|
|
37405
|
+
const structural = resolveStructuralElement(position2);
|
|
37406
|
+
return structural ? { kind: "found", element: structural } : { kind: "not-found" };
|
|
37407
|
+
}
|
|
37408
|
+
const target = { locator, elementRect: position2.elementRect };
|
|
37409
|
+
return cache ? resolveByLocatorCached(
|
|
37410
|
+
issueId,
|
|
37411
|
+
target,
|
|
37412
|
+
() => resolveStructuralElement(position2),
|
|
37413
|
+
cache,
|
|
37414
|
+
childListVersion,
|
|
37415
|
+
document,
|
|
37416
|
+
options
|
|
37417
|
+
) : resolveByLocator(target, resolveStructuralElement(position2), document, options);
|
|
37418
|
+
}
|
|
37419
|
+
function pointOfElement(element2, metrics) {
|
|
36753
37420
|
const rect = element2.getBoundingClientRect();
|
|
37421
|
+
if (rect.width === 0 && rect.height === 0) return null;
|
|
36754
37422
|
return {
|
|
36755
37423
|
x: metrics.scrollX + rect.right,
|
|
36756
37424
|
y: metrics.scrollY + rect.top
|
|
@@ -36775,6 +37443,12 @@ function resolveStickerPlacements(issues, mode, options = {}) {
|
|
|
36775
37443
|
const limit = options.limit ?? DEFAULT_STICKER_LIMIT;
|
|
36776
37444
|
const placements = [];
|
|
36777
37445
|
const unrendered = [];
|
|
37446
|
+
const cache = options.resolverCache;
|
|
37447
|
+
if (cache) {
|
|
37448
|
+
cache.retryAt = void 0;
|
|
37449
|
+
pruneLocatorCache(cache, new Set(issues.map((issue) => issue.id)));
|
|
37450
|
+
}
|
|
37451
|
+
const resolveOptions = mode === "target" && typeof document !== "undefined" && issues.some((issue) => issue.position && locatorOf(issue.position)) ? { headings: collectHeadings(document) } : {};
|
|
36778
37452
|
for (const issue of issues) {
|
|
36779
37453
|
if (placements.length >= limit) {
|
|
36780
37454
|
unrendered.push({
|
|
@@ -36792,7 +37466,35 @@ function resolveStickerPlacements(issues, mode, options = {}) {
|
|
|
36792
37466
|
});
|
|
36793
37467
|
continue;
|
|
36794
37468
|
}
|
|
36795
|
-
|
|
37469
|
+
let point2;
|
|
37470
|
+
if (mode === "target") {
|
|
37471
|
+
const resolution = resolveTarget(
|
|
37472
|
+
issue.id,
|
|
37473
|
+
issue.position,
|
|
37474
|
+
cache,
|
|
37475
|
+
options.childListVersion ?? 0,
|
|
37476
|
+
resolveOptions
|
|
37477
|
+
);
|
|
37478
|
+
if (resolution.kind === "container-closed") {
|
|
37479
|
+
unrendered.push({
|
|
37480
|
+
issue,
|
|
37481
|
+
status: "container-closed",
|
|
37482
|
+
reason: resolution.containerName ? `'${resolution.containerName}' 팝업을 열면 표시됩니다.` : "팝업을 열면 표시됩니다."
|
|
37483
|
+
});
|
|
37484
|
+
continue;
|
|
37485
|
+
}
|
|
37486
|
+
if (resolution.kind === "ambiguous") {
|
|
37487
|
+
unrendered.push({
|
|
37488
|
+
issue,
|
|
37489
|
+
status: "ambiguous",
|
|
37490
|
+
reason: "비슷한 요소가 여러 개라 위치를 정할 수 없습니다."
|
|
37491
|
+
});
|
|
37492
|
+
continue;
|
|
37493
|
+
}
|
|
37494
|
+
point2 = resolution.kind === "found" ? pointOfElement(resolution.element, metrics) : null;
|
|
37495
|
+
} else {
|
|
37496
|
+
point2 = resolveAbsoluteStickerPoint(issue.position, metrics.viewport);
|
|
37497
|
+
}
|
|
36796
37498
|
if (!point2 || !isFinitePoint(point2)) {
|
|
36797
37499
|
unrendered.push({
|
|
36798
37500
|
issue,
|
|
@@ -36819,7 +37521,7 @@ function resolveStickerPlacements(issues, mode, options = {}) {
|
|
|
36819
37521
|
hash: buildPlacementHash(issue, point2, mode)
|
|
36820
37522
|
});
|
|
36821
37523
|
}
|
|
36822
|
-
return { placements, unrendered };
|
|
37524
|
+
return { placements, unrendered, retryAt: cache == null ? void 0 : cache.retryAt };
|
|
36823
37525
|
}
|
|
36824
37526
|
function getPlacementSortKey(placement) {
|
|
36825
37527
|
return `${placement.issue.created_at || ""}:${placement.issue.id}`;
|
|
@@ -37216,6 +37918,7 @@ function useDocumentLayerScrollVars(ref, enabled) {
|
|
|
37216
37918
|
}
|
|
37217
37919
|
function useDocumentLayoutVersion(enabled, mode) {
|
|
37218
37920
|
const [version, setVersion] = useState(0);
|
|
37921
|
+
const contentVersionRef = useRef(0);
|
|
37219
37922
|
useEffect(() => {
|
|
37220
37923
|
if (!enabled || typeof window === "undefined" || typeof document === "undefined") {
|
|
37221
37924
|
return void 0;
|
|
@@ -37232,8 +37935,18 @@ function useDocumentLayoutVersion(enabled, mode) {
|
|
|
37232
37935
|
const scheduleThrottled = throttle(scheduleFrame, LAYOUT_RECALC_THROTTLE_MS);
|
|
37233
37936
|
window.addEventListener("resize", scheduleThrottled);
|
|
37234
37937
|
window.addEventListener("orientationchange", scheduleThrottled);
|
|
37235
|
-
const observer2 = new MutationObserver(
|
|
37236
|
-
|
|
37938
|
+
const observer2 = new MutationObserver((records) => {
|
|
37939
|
+
if (records.some((record) => record.type === "childList" || record.type === "characterData")) {
|
|
37940
|
+
contentVersionRef.current += 1;
|
|
37941
|
+
}
|
|
37942
|
+
scheduleThrottled();
|
|
37943
|
+
});
|
|
37944
|
+
observer2.observe(document.body, {
|
|
37945
|
+
attributes: true,
|
|
37946
|
+
childList: true,
|
|
37947
|
+
characterData: true,
|
|
37948
|
+
subtree: true
|
|
37949
|
+
});
|
|
37237
37950
|
const resizeObserver = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(scheduleThrottled);
|
|
37238
37951
|
if (resizeObserver) {
|
|
37239
37952
|
resizeObserver.observe(document.documentElement);
|
|
@@ -37253,7 +37966,7 @@ function useDocumentLayoutVersion(enabled, mode) {
|
|
|
37253
37966
|
if (frame2) window.cancelAnimationFrame(frame2);
|
|
37254
37967
|
};
|
|
37255
37968
|
}, [enabled, mode]);
|
|
37256
|
-
return version;
|
|
37969
|
+
return { version, contentVersion: contentVersionRef.current };
|
|
37257
37970
|
}
|
|
37258
37971
|
function getClusterDocumentPoint(cluster) {
|
|
37259
37972
|
return {
|
|
@@ -37289,19 +38002,34 @@ function IssueStickersContainer$1({
|
|
|
37289
38002
|
const closeTimerRef = useRef(null);
|
|
37290
38003
|
const mode = normalizeStickerRenderMode(renderMode);
|
|
37291
38004
|
const markerSize = useStickerSize();
|
|
37292
|
-
const layoutVersion = useDocumentLayoutVersion(visible, mode);
|
|
38005
|
+
const { version: layoutVersion, contentVersion } = useDocumentLayoutVersion(visible, mode);
|
|
38006
|
+
const resolverCacheRef = useRef(createLocatorResolverCache());
|
|
38007
|
+
const [retryVersion, setRetryVersion] = useState(0);
|
|
37293
38008
|
useDocumentLayerScrollVars(layerRef, visible);
|
|
37294
38009
|
const metrics = useMemo(() => getCurrentStickerDocumentMetrics(), [layoutVersion, visible]);
|
|
37295
|
-
const { clusters, unrendered } = useMemo(() => {
|
|
38010
|
+
const { clusters, unrendered, retryAt } = useMemo(() => {
|
|
37296
38011
|
if (!visible) {
|
|
37297
|
-
return { clusters: [], unrendered: [] };
|
|
38012
|
+
return { clusters: [], unrendered: [], retryAt: void 0 };
|
|
37298
38013
|
}
|
|
37299
|
-
const resolved = resolveStickerPlacements(issues, mode, {
|
|
38014
|
+
const resolved = resolveStickerPlacements(issues, mode, {
|
|
38015
|
+
metrics,
|
|
38016
|
+
resolverCache: resolverCacheRef.current,
|
|
38017
|
+
childListVersion: contentVersion
|
|
38018
|
+
});
|
|
37300
38019
|
return {
|
|
37301
38020
|
clusters: clusterStickerPlacements(resolved.placements, markerSize),
|
|
37302
|
-
unrendered: resolved.unrendered
|
|
38021
|
+
unrendered: resolved.unrendered,
|
|
38022
|
+
retryAt: resolved.retryAt
|
|
37303
38023
|
};
|
|
37304
|
-
}, [issues, layoutVersion, markerSize, metrics, mode, visible]);
|
|
38024
|
+
}, [contentVersion, issues, layoutVersion, markerSize, metrics, mode, retryVersion, visible]);
|
|
38025
|
+
useEffect(() => {
|
|
38026
|
+
if (retryAt === void 0) return void 0;
|
|
38027
|
+
const timer = window.setTimeout(
|
|
38028
|
+
() => setRetryVersion((current) => current + 1),
|
|
38029
|
+
Math.max(0, retryAt - Date.now()) + 1
|
|
38030
|
+
);
|
|
38031
|
+
return () => window.clearTimeout(timer);
|
|
38032
|
+
}, [retryAt, retryVersion]);
|
|
37305
38033
|
const unrenderedSignature = useMemo(
|
|
37306
38034
|
() => unrendered.map((item) => `${item.issue.id}:${item.status}:${item.reason}`).sort().join("|"),
|
|
37307
38035
|
[unrendered]
|
|
@@ -37362,6 +38090,7 @@ function IssueStickersContainer$1({
|
|
|
37362
38090
|
[clearClosingCluster, collapseExpandedCluster, expandedClusterId]
|
|
37363
38091
|
);
|
|
37364
38092
|
useEffect(() => {
|
|
38093
|
+
var _a;
|
|
37365
38094
|
if (!expandedClusterId) return void 0;
|
|
37366
38095
|
const handlePointerDown = (event) => {
|
|
37367
38096
|
const root2 = rootRef.current;
|
|
@@ -37370,8 +38099,9 @@ function IssueStickersContainer$1({
|
|
|
37370
38099
|
collapseExpandedCluster();
|
|
37371
38100
|
}
|
|
37372
38101
|
};
|
|
37373
|
-
document.
|
|
37374
|
-
|
|
38102
|
+
const scopes = /* @__PURE__ */ new Set([document, ((_a = rootRef.current) == null ? void 0 : _a.getRootNode()) ?? document]);
|
|
38103
|
+
scopes.forEach((scope) => scope.addEventListener("pointerdown", handlePointerDown, true));
|
|
38104
|
+
return () => scopes.forEach((scope) => scope.removeEventListener("pointerdown", handlePointerDown, true));
|
|
37375
38105
|
}, [collapseExpandedCluster, expandedClusterId]);
|
|
37376
38106
|
if (!visible) {
|
|
37377
38107
|
return null;
|
|
@@ -37635,6 +38365,9 @@ function useKeyboardShortcuts(isCreatingIssueRef, onToggleIssueMode, onToggleRec
|
|
|
37635
38365
|
return;
|
|
37636
38366
|
}
|
|
37637
38367
|
if (isRecordingShortcut(eventLike)) {
|
|
38368
|
+
if (isIssueFormOpen) {
|
|
38369
|
+
return;
|
|
38370
|
+
}
|
|
37638
38371
|
e.preventDefault();
|
|
37639
38372
|
e.stopPropagation();
|
|
37640
38373
|
if (isProcessingRecordingRef.current) {
|
|
@@ -37962,15 +38695,16 @@ function useContentAppState() {
|
|
|
37962
38695
|
}, [isCreatingIssue]);
|
|
37963
38696
|
const handleIssueCreated = useCallback(
|
|
37964
38697
|
async (hasIntegrationFailure = false) => {
|
|
38698
|
+
const hadVideo = Boolean(clickPosition == null ? void 0 : clickPosition.videoFile);
|
|
37965
38699
|
clearClickPosition();
|
|
37966
38700
|
screenRecording.clearRecordedFile();
|
|
37967
38701
|
await loadIssues();
|
|
37968
38702
|
await toggleIssueMode(false);
|
|
37969
38703
|
if (!hasIntegrationFailure) {
|
|
37970
|
-
showToast("이슈가 생성되었습니다.");
|
|
38704
|
+
showToast(hadVideo ? VIDEO_UPLOAD_COPY.created : "이슈가 생성되었습니다.");
|
|
37971
38705
|
}
|
|
37972
38706
|
},
|
|
37973
|
-
[clearClickPosition, loadIssues, screenRecording, toggleIssueMode, showToast]
|
|
38707
|
+
[clickPosition, clearClickPosition, loadIssues, screenRecording, toggleIssueMode, showToast]
|
|
37974
38708
|
);
|
|
37975
38709
|
return {
|
|
37976
38710
|
// 상태
|
|
@@ -37999,6 +38733,31 @@ function useContentAppState() {
|
|
|
37999
38733
|
setFieldFilters
|
|
38000
38734
|
};
|
|
38001
38735
|
}
|
|
38736
|
+
function useWidgetVideoUploads(projectId) {
|
|
38737
|
+
const [states, setStates] = useState([]);
|
|
38738
|
+
useEffect(() => getWidgetVideoUploadQueue().subscribe(setStates), []);
|
|
38739
|
+
useEffect(() => {
|
|
38740
|
+
if (!projectId) return;
|
|
38741
|
+
void resumeWidgetIssueUploads(projectId).then((count) => {
|
|
38742
|
+
if (count > 0) toast.info(VIDEO_UPLOAD_COPY.resumed(count));
|
|
38743
|
+
void getWidgetVideoUploadQueue().pruneExpired();
|
|
38744
|
+
void getWidgetAttachmentUploadQueue().pruneExpired();
|
|
38745
|
+
});
|
|
38746
|
+
}, [projectId]);
|
|
38747
|
+
useEffect(() => {
|
|
38748
|
+
const handleBeforeUnload = (event) => {
|
|
38749
|
+
if (getWidgetVideoUploadQueue().hasActiveUploads() || getWidgetAttachmentUploadQueue().hasActiveUploads()) {
|
|
38750
|
+
event.preventDefault();
|
|
38751
|
+
}
|
|
38752
|
+
};
|
|
38753
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
38754
|
+
return () => window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
38755
|
+
}, []);
|
|
38756
|
+
const retryFailedUploads = () => {
|
|
38757
|
+
states.filter((state) => state.status === "failed").forEach((state) => retryWidgetIssueUploads(state.issueId));
|
|
38758
|
+
};
|
|
38759
|
+
return { uploadProgress: summarizeVideoUploads(states), retryFailedUploads };
|
|
38760
|
+
}
|
|
38002
38761
|
const STORAGE_KEY = "issue-sticker-sticker-render-mode";
|
|
38003
38762
|
const QUERY_ROOT = ["stickerRenderMode"];
|
|
38004
38763
|
function readLocalRenderMode() {
|
|
@@ -38364,7 +39123,7 @@ function MoreMenu({
|
|
|
38364
39123
|
},
|
|
38365
39124
|
{
|
|
38366
39125
|
id: "unrendered",
|
|
38367
|
-
label: unrenderedStickerCount > 0 ?
|
|
39126
|
+
label: unrenderedStickerCount > 0 ? `표시하지 못한 스티커 ${unrenderedStickerCount}개` : "표시하지 못한 스티커 없음",
|
|
38368
39127
|
icon: /* @__PURE__ */ jsx(CircleAlert, { size: ICON_SIZE }),
|
|
38369
39128
|
onClick: onOpenUnrenderedStickers,
|
|
38370
39129
|
// 0개일 때도 노출한다. 숨기면 목록을 열어 확인할 진입점 자체가 없어진다.
|
|
@@ -38609,6 +39368,35 @@ const TOOLBAR_STYLES = `
|
|
|
38609
39368
|
border: 2px solid hsl(var(--surface-1));
|
|
38610
39369
|
pointer-events: none;
|
|
38611
39370
|
}
|
|
39371
|
+
.toolbar-upload {
|
|
39372
|
+
width: 34px;
|
|
39373
|
+
height: 34px;
|
|
39374
|
+
display: flex;
|
|
39375
|
+
align-items: center;
|
|
39376
|
+
justify-content: center;
|
|
39377
|
+
flex: none;
|
|
39378
|
+
border-radius: 50%;
|
|
39379
|
+
border: none;
|
|
39380
|
+
background: transparent;
|
|
39381
|
+
padding: 0;
|
|
39382
|
+
outline: none;
|
|
39383
|
+
}
|
|
39384
|
+
button.toolbar-upload { cursor: pointer; }
|
|
39385
|
+
.toolbar-upload:focus-visible { box-shadow: var(--shadow-focus); }
|
|
39386
|
+
.toolbar-upload svg { transform: rotate(-90deg); }
|
|
39387
|
+
.toolbar-upload-track {
|
|
39388
|
+
fill: none;
|
|
39389
|
+
stroke: hsl(var(--border-subtle));
|
|
39390
|
+
stroke-width: 2.5;
|
|
39391
|
+
}
|
|
39392
|
+
.toolbar-upload-bar {
|
|
39393
|
+
fill: none;
|
|
39394
|
+
stroke: var(--brand);
|
|
39395
|
+
stroke-width: 2.5;
|
|
39396
|
+
stroke-linecap: round;
|
|
39397
|
+
transition: stroke-dashoffset 0.3s ease;
|
|
39398
|
+
}
|
|
39399
|
+
.toolbar-upload[data-failed="true"] .toolbar-upload-bar { stroke: hsl(var(--danger)); }
|
|
38612
39400
|
|
|
38613
39401
|
.toolbar-menu {
|
|
38614
39402
|
position: absolute;
|
|
@@ -38886,6 +39674,13 @@ function formatElapsed(startedAt, now2) {
|
|
|
38886
39674
|
const ss = String(seconds % 60).padStart(2, "0");
|
|
38887
39675
|
return `${mm}:${ss}`;
|
|
38888
39676
|
}
|
|
39677
|
+
const UPLOAD_RING_RADIUS = 7;
|
|
39678
|
+
const UPLOAD_RING_LENGTH = 2 * Math.PI * UPLOAD_RING_RADIUS;
|
|
39679
|
+
function formatUploadLabel({ percent: percent2, count, failed }) {
|
|
39680
|
+
if (failed) return "영상 업로드 실패";
|
|
39681
|
+
const waiting = count > 1 ? ` · 대기 ${count - 1}개` : "";
|
|
39682
|
+
return `영상 업로드 중 ${percent2}%${waiting}`;
|
|
39683
|
+
}
|
|
38889
39684
|
function Toolbar({
|
|
38890
39685
|
onCreateIssue,
|
|
38891
39686
|
onToggleStickers,
|
|
@@ -38907,7 +39702,9 @@ function Toolbar({
|
|
|
38907
39702
|
targets,
|
|
38908
39703
|
account,
|
|
38909
39704
|
blocked,
|
|
38910
|
-
filter: filter2
|
|
39705
|
+
filter: filter2,
|
|
39706
|
+
uploadProgress,
|
|
39707
|
+
onUploadRetry
|
|
38911
39708
|
}) {
|
|
38912
39709
|
const rootRef = useRef(null);
|
|
38913
39710
|
const [internalOpen, setInternalOpen] = useState(false);
|
|
@@ -39011,6 +39808,20 @@ function Toolbar({
|
|
|
39011
39808
|
}, []);
|
|
39012
39809
|
const anchor = toCssAnchor(placement);
|
|
39013
39810
|
const menuPlacement = getMenuPlacement(placement);
|
|
39811
|
+
const uploadRing = uploadProgress ? /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 18 18", "aria-hidden": "true", children: [
|
|
39812
|
+
/* @__PURE__ */ jsx("circle", { className: "toolbar-upload-track", cx: "9", cy: "9", r: UPLOAD_RING_RADIUS }),
|
|
39813
|
+
/* @__PURE__ */ jsx(
|
|
39814
|
+
"circle",
|
|
39815
|
+
{
|
|
39816
|
+
className: "toolbar-upload-bar",
|
|
39817
|
+
cx: "9",
|
|
39818
|
+
cy: "9",
|
|
39819
|
+
r: UPLOAD_RING_RADIUS,
|
|
39820
|
+
strokeDasharray: UPLOAD_RING_LENGTH,
|
|
39821
|
+
strokeDashoffset: uploadProgress.failed ? 0 : UPLOAD_RING_LENGTH * (1 - uploadProgress.percent / 100)
|
|
39822
|
+
}
|
|
39823
|
+
)
|
|
39824
|
+
] }) : null;
|
|
39014
39825
|
const idleBody = /* @__PURE__ */ jsxs(TooltipProvider, { delayDuration: 240, skipDelayDuration: 120, disableHoverableContent: true, children: [
|
|
39015
39826
|
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
39016
39827
|
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
@@ -39049,6 +39860,30 @@ function Toolbar({
|
|
|
39049
39860
|
/* @__PURE__ */ jsx("span", { className: "block font-normal opacity-70", children: "공유할 화면을 선택하는 창이 열립니다" })
|
|
39050
39861
|
] })
|
|
39051
39862
|
] }),
|
|
39863
|
+
uploadProgress ? /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
39864
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: uploadProgress.failed && onUploadRetry ? /* @__PURE__ */ jsx(
|
|
39865
|
+
"button",
|
|
39866
|
+
{
|
|
39867
|
+
type: "button",
|
|
39868
|
+
className: "toolbar-upload",
|
|
39869
|
+
"aria-label": ISSUE_UPLOAD_COPY.retry,
|
|
39870
|
+
"data-failed": true,
|
|
39871
|
+
onClick: onUploadRetry,
|
|
39872
|
+
children: uploadRing
|
|
39873
|
+
}
|
|
39874
|
+
) : /* @__PURE__ */ jsx(
|
|
39875
|
+
"span",
|
|
39876
|
+
{
|
|
39877
|
+
className: "toolbar-upload",
|
|
39878
|
+
role: "status",
|
|
39879
|
+
"aria-label": formatUploadLabel(uploadProgress),
|
|
39880
|
+
"data-failed": uploadProgress.failed,
|
|
39881
|
+
tabIndex: 0,
|
|
39882
|
+
children: uploadRing
|
|
39883
|
+
}
|
|
39884
|
+
) }),
|
|
39885
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "top", sideOffset: 10, children: formatUploadLabel(uploadProgress) })
|
|
39886
|
+
] }) : null,
|
|
39052
39887
|
/* @__PURE__ */ jsx("div", { className: "toolbar-divider" }),
|
|
39053
39888
|
/* @__PURE__ */ jsxs(
|
|
39054
39889
|
"button",
|
|
@@ -39100,7 +39935,7 @@ function Toolbar({
|
|
|
39100
39935
|
children: /* @__PURE__ */ jsx(X$1, { size: 15 })
|
|
39101
39936
|
}
|
|
39102
39937
|
)
|
|
39103
|
-
] }) : state === "
|
|
39938
|
+
] }) : state === "recording" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
39104
39939
|
/* @__PURE__ */ jsx("span", { className: "toolbar-pulse", "aria-hidden": "true" }),
|
|
39105
39940
|
/* @__PURE__ */ jsx("span", { className: "toolbar-elapsed", "aria-hidden": "true", children: formatElapsed(recordingStartedAt, now2) }),
|
|
39106
39941
|
/* @__PURE__ */ jsx("div", { className: "toolbar-divider" }),
|
|
@@ -39115,6 +39950,7 @@ function Toolbar({
|
|
|
39115
39950
|
}
|
|
39116
39951
|
)
|
|
39117
39952
|
] }) : idleBody;
|
|
39953
|
+
if (state === "writing") return null;
|
|
39118
39954
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
39119
39955
|
/* @__PURE__ */ jsx("style", { children: TOOLBAR_STYLES }),
|
|
39120
39956
|
/* @__PURE__ */ jsxs(Popover, { open: filterOpen, onOpenChange: setFilterOpen, children: [
|
|
@@ -39270,6 +40106,7 @@ function ContentAppWrapper({
|
|
|
39270
40106
|
const { renderMode, saveRenderMode } = useStickerRenderMode();
|
|
39271
40107
|
const { isMember } = useAuthContext();
|
|
39272
40108
|
const { selectedProjectId } = useProject();
|
|
40109
|
+
const { uploadProgress, retryFailedUploads } = useWidgetVideoUploads(selectedProjectId);
|
|
39273
40110
|
const filterProjectId = isMember ? selectedProjectId : null;
|
|
39274
40111
|
const { data: filterGroups = [] } = useQuery({
|
|
39275
40112
|
queryKey: ["issueFilterGroups", filterProjectId],
|
|
@@ -39312,6 +40149,8 @@ function ContentAppWrapper({
|
|
|
39312
40149
|
recordingStartedAt: screenRecording.startedAt,
|
|
39313
40150
|
showStickers,
|
|
39314
40151
|
toolbar,
|
|
40152
|
+
uploadProgress,
|
|
40153
|
+
onUploadRetry: retryFailedUploads,
|
|
39315
40154
|
stickerRenderMode: renderMode,
|
|
39316
40155
|
onToggleStickerRenderMode: () => void saveRenderMode(nextRenderMode),
|
|
39317
40156
|
unrenderedStickerCount: unrenderedCount,
|
|
@@ -39407,12 +40246,13 @@ function AuthGate() {
|
|
|
39407
40246
|
) })
|
|
39408
40247
|
] });
|
|
39409
40248
|
}
|
|
39410
|
-
const globalsCSS = `:root,:host{color-scheme:light;--brand-h: 150;--brand-s: 100%;--brand-l: 34%;--brand-hsl: var(--brand-h) var(--brand-s) var(--brand-l);--brand: hsl(var(--brand-hsl));--brand-hover: hsl(var(--brand-h) calc(var(--brand-s) + 3%) calc(var(--brand-l) - 4%));--brand-active: hsl(var(--brand-h) calc(var(--brand-s) + 2%) calc(var(--brand-l) - 8%));--brand-subtle: hsl(var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .14);--brand-border: hsl(var(--brand-h) calc(var(--brand-s) + 8%) calc(var(--brand-l) + 8%) / .34);--brand-fg: hsl(0 0% 100%);--brand-ring: hsl(var(--brand-h) calc(var(--brand-s) + 12%) calc(var(--brand-l) + 10%) / .22);--font-sans: "Pretendard", -apple-system, BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif;--font-display: var(--font-sans);--font-mono: var(--font-sans);--surface-0: 0 0% 99%;--surface-1: 0 0% 100%;--surface-2: 144 8% 96%;--surface-3: 144 6% 93%;--surface-inset: 144 6% 95%;--overlay-backdrop: hsl(0 0% 0% / .24);--overlay-backdrop-strong: hsl(0 0% 0% / .48);--text-primary: 150 10% 12%;--text-secondary: 148 6% 36%;--text-tertiary: 145 5% 42%;--text-disabled: 145 4% 70%;--text-inverse: 0 0% 100%;--border-subtle: 144 8% 91%;--border-default: 144 6% 85%;--border-strong: 144 5% 72%;--border-focus: var(--brand-h) var(--brand-s) var(--brand-l);--success: var(--brand-h) var(--brand-s) var(--brand-l);--success-subtle: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .14;--success-surface: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .24;--warning: 34 82% 52%;--warning-subtle: 34 82% 52% / .14;--danger: 3 68% 56%;--danger-subtle: 3 68% 56% / .12;--danger-deep: 3 62% 40%;--info: 202 52% 44%;--info-subtle: 202 52% 44% / .12;--accent-terra: 13 52% 47%;--accent-terra-subtle: 13 52% 47% / .1;--accent-terra-deep: 13 48% 44%;--accent-blue: 213 86% 56%;--accent-blue-subtle: 213 92% 56% / .12;--accent-blue-deep: 216 78% 40%;--accent-blue-border: 213 86% 62% / .36;--accent-cream: 38 52% 88%;--accent-cream-warm: 40 56% 95%;--accent-cream-deep: 38 48% 82%;--accent-highlight: 55 96% 70%;--accent-ink: 32 13% 11%;--radius-xs: 4px;--radius-sm: 8px;--radius-md: 12px;--radius-lg: 16px;--radius-xl: 20px;--radius-2xl: 24px;--radius-pill: 9999px;--shadow-xs: 0 1px 2px 0 hsl(0 0% 0% / .04);--shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / .06), 0 1px 2px -1px hsl(0 0% 0% / .06);--shadow-md: 0 4px 6px -1px hsl(0 0% 0% / .06), 0 2px 4px -2px hsl(0 0% 0% / .04);--shadow-lg: 0 10px 15px -3px hsl(0 0% 0% / .06), 0 4px 6px -4px hsl(0 0% 0% / .04);--shadow-xl: 0 20px 25px -5px hsl(0 0% 0% / .06), 0 8px 10px -6px hsl(0 0% 0% / .04);--shadow-focus: 0 0 0 3px var(--brand-ring);--shadow-inset: inset 0 1px 2px 0 hsl(0 0% 0% / .04);--shadow-brand: 0 2px 8px hsl(var(--brand-h) calc(var(--brand-s) - 60%) calc(var(--brand-l) - 14%) / .24), 0 6px 20px hsl(var(--brand-h) calc(var(--brand-s) - 70%) calc(var(--brand-l) - 19%) / .16);--duration-instant: 90ms;--duration-fast: .16s;--duration-base: .26s;--duration-out: .18s;--duration-slow: .36s;--ease-out: cubic-bezier(.23, 1, .32, 1);--ease-in-out: cubic-bezier(.45, 0, .15, 1);--ease-spring: cubic-bezier(.18, .94, .32, 1.08);--grid-line: 0 0% 92%}.dark,:host(.dark){color-scheme:dark;--brand-l: 34%;--brand-fg: hsl(0 0% 100%);--surface-0: 150 8% 7%;--surface-1: 150 6% 10%;--surface-2: 150 5% 14%;--surface-3: 150 4% 18%;--surface-inset: 150 6% 6%;--overlay-backdrop: hsl(0 0% 0% / .56);--overlay-backdrop-strong: hsl(0 0% 0% / .8);--text-primary: 40 16% 93%;--text-secondary: 36 8% 68%;--text-tertiary: 34 6% 50%;--text-disabled: 30 5% 34%;--text-inverse: 150 8% 10%;--border-subtle: 150 6% 16%;--border-default: 150 5% 22%;--border-strong: 150 4% 34%;--success: var(--brand-h) var(--brand-s) var(--brand-l);--success-subtle: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .14;--success-surface: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .24;--warning: 34 82% 62%;--danger: 2 70% 64%;--danger-deep: 2 80% 76%;--info: 202 54% 62%;--accent-terra: 13 56% 62%;--accent-terra-subtle: 13 56% 62% / .14;--accent-terra-deep: 13 52% 56%;--accent-blue: 210 92% 68%;--accent-blue-subtle: 210 92% 68% / .18;--accent-blue-deep: 209 100% 78%;--accent-blue-border: 210 92% 72% / .44;--accent-cream: 38 30% 26%;--accent-cream-warm: 38 22% 18%;--accent-cream-deep: 38 26% 32%;--accent-highlight: 55 96% 70%;--accent-ink: 32 13% 8%;--shadow-xs: 0 1px 2px 0 hsl(0 0% 0% / .2);--shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / .3), 0 1px 2px -1px hsl(0 0% 0% / .24);--shadow-md: 0 4px 6px -1px hsl(0 0% 0% / .34), 0 2px 4px -2px hsl(0 0% 0% / .2);--shadow-lg: 0 10px 15px -3px hsl(0 0% 0% / .38), 0 4px 6px -4px hsl(0 0% 0% / .24);--shadow-xl: 0 20px 25px -5px hsl(0 0% 0% / .44), 0 8px 10px -6px hsl(0 0% 0% / .28);--shadow-inset: inset 0 1px 2px 0 hsl(0 0% 0% / .16);--shadow-brand: 0 2px 8px hsl(var(--brand-h) calc(var(--brand-s) - 60%) calc(var(--brand-l) - 24%) / .44), 0 6px 20px hsl(0 0% 0% / .32);--grid-line: 150 6% 14%}*{border-color:hsl(var(--border-default))}html{font-family:var(--font-sans),ui-sans-serif,system-ui,-apple-system,sans-serif;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;font-feature-settings:"cv11","ss01","ss03";background-color:hsl(var(--surface-0))}body{margin:0;min-height:100vh;color:hsl(var(--text-primary));background-color:hsl(var(--surface-0))}::-moz-selection{background-color:hsl(var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 16%) / .22);color:hsl(var(--text-primary))}::selection{background-color:hsl(var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 16%) / .22);color:hsl(var(--text-primary))}code,kbd,samp,pre{font-family:var(--font-mono),ui-monospace,SFMono-Regular,Menlo,monospace;font-feature-settings:"ss01","ss02","cv11"}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:hsl(var(--border-strong) / .52);border-radius:var(--radius-pill);border:2px solid transparent;background-clip:padding-box}::-webkit-scrollbar-thumb:hover{background:hsl(var(--border-strong) / .9);background-clip:padding-box;border:2px solid transparent}.app-canvas,.grid-canvas{background-color:hsl(var(--surface-0))}.onboarding-canvas{word-break:keep-all;overflow-wrap:break-word;--surface-0: var(--brand-h) var(--brand-s) var(--brand-l);--surface-1: 0 0% 100%;--surface-2: var(--brand-h) 62% 28%;--surface-3: var(--brand-h) 60% 25%;--surface-inset: var(--brand-h) 62% 28%;--text-primary: 0 0% 100%;--text-secondary: var(--brand-h) 46% 90%;--text-tertiary: var(--brand-h) 34% 84%;--text-disabled: var(--brand-h) 20% 72%;--border-subtle: var(--brand-h) 44% 52%;--border-default: var(--brand-h) 40% 58%;--border-strong: var(--brand-h) 36% 70%;--border-focus: 0 0% 100%;--brand-hsl: 0 0% 100%;--brand: hsl(var(--brand-hsl));--brand-hover: hsl(var(--brand-h) 24% 94%);--brand-active: hsl(var(--brand-h) 20% 88%);--brand-fg: hsl(var(--brand-h) var(--brand-s) 24%);--brand-ring: hsl(0 0% 100% / .4);--danger: 6 96% 86%;--danger-subtle: 6 96% 86% / .18;--danger-deep: 6 96% 92%;--warning: 42 96% 82%;--warning-subtle: 42 96% 82% / .18}.onboarding-canvas .bg-surface-1,.onboarding-canvas input,.onboarding-canvas textarea,.onboarding-canvas [role=combobox]{--text-primary: 150 10% 12%;--text-secondary: 148 6% 36%;--text-tertiary: 145 5% 42%;--surface-2: 144 8% 96%;--surface-inset: 144 6% 95%;--border-subtle: 144 8% 91%;--border-default: 144 6% 85%;--brand-hsl: var(--brand-h) var(--brand-s) var(--brand-l);--brand: hsl(var(--brand-hsl));--brand-hover: hsl(var(--brand-h) calc(var(--brand-s) + 3%) calc(var(--brand-l) - 4%));--brand-active: hsl(var(--brand-h) calc(var(--brand-s) + 2%) calc(var(--brand-l) - 8%));--brand-fg: hsl(0 0% 100%);--brand-ring: hsl(var(--brand-h) calc(var(--brand-s) + 12%) calc(var(--brand-l) + 10%) / .22);--warning: 34 82% 52%;--warning-subtle: 34 82% 52% / .14;--danger: 3 68% 56%;--danger-subtle: 3 68% 56% / .12;--danger-deep: 3 62% 40%}@keyframes onboarding-step-in{0%{opacity:0;transform:translate(96px)}to{opacity:1;transform:translate(0)}}.onboarding-step-in{animation:onboarding-step-in var(--duration-slow) var(--ease-out) forwards}@keyframes onboarding-enter-up{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.onboarding-enter{position:fixed;top:0;right:0;bottom:0;left:0;overflow-x:hidden;overflow-y:auto;animation:onboarding-enter-up var(--duration-base) var(--ease-out) forwards}body:has(.onboarding-canvas){background-color:hsl(var(--brand-h) var(--brand-s) var(--brand-l))}.panel-shell,.surface-shell{border:1px solid hsl(var(--border-subtle));border-radius:var(--radius-xl);background:hsl(var(--surface-1));box-shadow:var(--shadow-sm)}.panel-shell-muted,.surface-shell-muted{background:hsl(var(--surface-2));box-shadow:var(--shadow-xs)}.panel-shell-subtle{border:1px solid hsl(var(--border-subtle));border-radius:var(--radius-lg);background:hsl(var(--surface-1));box-shadow:var(--shadow-xs)}.panel-shell-elevated{border:1px solid hsl(var(--border-default));border-radius:var(--radius-xl);background:hsl(var(--surface-1));box-shadow:var(--shadow-md)}.utility-chip,.floating-chip{display:inline-flex;align-items:center;gap:8px;border:1px solid hsl(var(--border-default));border-radius:var(--radius-pill);background:hsl(var(--surface-1));box-shadow:var(--shadow-xs)}.hairline{border-bottom:1px solid hsl(var(--border-subtle))}.mono-label{font-family:var(--font-mono),ui-monospace,monospace;font-size:11px;text-transform:uppercase;color:hsl(var(--text-tertiary))}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-sans),ui-sans-serif,system-ui,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--font-mono),ui-monospace,SFMono-Regular,Menlo,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root,:host{--font-sans: "Pretendard", -apple-system, BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif;--font-mono: var(--font-sans);--issue-marker-expand-duration: .52s;--surface-0: 0 0% 99%;--surface-1: 0 0% 100%;--surface-2: 144 8% 96%;--surface-3: 144 6% 93%;--surface-inset: 144 6% 95%;--overlay-backdrop: hsl(0 0% 0% / .24);--overlay-backdrop-strong: hsl(0 0% 0% / .48);--text-primary: 150 10% 12%;--text-secondary: 148 6% 36%;--text-tertiary: 145 5% 52%;--text-disabled: 145 4% 70%;--border-subtle: 144 8% 91%;--border-default: 144 6% 85%;--border-strong: 144 5% 72%;--grid-line: 0 0% 92%;--shadow-xs: 0 1px 2px 0 hsl(0 0% 0% / .04);--shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / .06), 0 1px 2px -1px hsl(0 0% 0% / .06);--shadow-md: 0 4px 6px -1px hsl(0 0% 0% / .06), 0 2px 4px -2px hsl(0 0% 0% / .04);--shadow-lg: 0 10px 15px -3px hsl(0 0% 0% / .06), 0 4px 6px -4px hsl(0 0% 0% / .04);--shadow-xl: 0 20px 25px -5px hsl(0 0% 0% / .06), 0 8px 10px -6px hsl(0 0% 0% / .04);--shadow-focus: 0 0 0 4px var(--brand-ring);--shadow-inset: inset 0 1px 2px 0 hsl(0 0% 0% / .04)}*{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}#issue-sticker-shadow-root{all:initial;display:block;background-color:transparent;--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1));font-family:var(--font-sans);line-height:1.5}.\\!container{width:100%!important}.container{width:100%}@media(min-width:640px){.\\!container{max-width:640px!important}.container{max-width:640px}}@media(min-width:768px){.\\!container{max-width:768px!important}.container{max-width:768px}}@media(min-width:1024px){.\\!container{max-width:1024px!important}.container{max-width:1024px}}@media(min-width:1280px){.\\!container{max-width:1280px!important}.container{max-width:1280px}}@media(min-width:1536px){.\\!container{max-width:1536px!important}.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.\\!visible{visibility:visible!important}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.inset-x-1{left:4px;right:4px}.inset-y-0{top:0;bottom:0}.-bottom-px{bottom:-1px}.bottom-0{bottom:0}.bottom-4{bottom:16px}.bottom-6{bottom:24px}.left-0{left:0}.left-1\\/2{left:50%}.left-2{left:8px}.left-4{left:16px}.left-\\[50\\%\\]{left:50%}.right-0{right:0}.right-2{right:8px}.right-3{right:12px}.right-4{right:16px}.right-5{right:20px}.right-6{right:24px}.top-0{top:0}.top-1{top:4px}.top-1\\/2{top:50%}.top-2{top:8px}.top-3{top:12px}.top-4{top:16px}.top-5{top:20px}.top-\\[50\\%\\]{top:50%}.top-\\[calc\\(\\(3\\.5rem-1\\.75rem\\)\\/2\\)\\]{top:14px}.top-full{top:100%}.isolate{isolation:isolate}.z-10{z-index:10}.z-20{z-index:20}.z-\\[1000000\\]{z-index:1000000}.z-\\[1000003\\]{z-index:1000003}.z-\\[1000004\\]{z-index:1000004}.z-\\[1000005\\]{z-index:1000005}.z-\\[1000006\\]{z-index:1000006}.z-\\[1000007\\]{z-index:1000007}.z-\\[1000008\\]{z-index:1000008}.z-\\[1000010\\]{z-index:1000010}.z-\\[1000011\\]{z-index:1000011}.z-\\[10001\\]{z-index:10001}.z-\\[999999\\]{z-index:999999}.z-\\[9999\\]{z-index:9999}.m-0{margin:0}.-mx-2{margin-left:-8px;margin-right:-8px}.mx-3{margin-left:12px;margin-right:12px}.my-1{margin-top:4px;margin-bottom:4px}.my-2{margin-top:8px;margin-bottom:8px}.my-2\\.5{margin-top:10px;margin-bottom:10px}.my-3{margin-top:12px;margin-bottom:12px}.my-4{margin-top:16px;margin-bottom:16px}.mb-0\\.5{margin-bottom:2px}.mb-1{margin-bottom:4px}.mb-2{margin-bottom:8px}.mb-3{margin-bottom:12px}.mb-5{margin-bottom:20px}.ml-0\\.5{margin-left:2px}.ml-4{margin-left:16px}.mr-1\\.5{margin-right:6px}.mt-0{margin-top:0}.mt-0\\.5{margin-top:2px}.mt-1{margin-top:4px}.mt-2{margin-top:8px}.mt-3{margin-top:12px}.mt-4{margin-top:16px}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-square{aspect-ratio:1 / 1}.\\!h-auto{height:auto!important}.h-1\\.5{height:6px}.h-10{height:40px}.h-11{height:44px}.h-2{height:8px}.h-24{height:96px}.h-3{height:12px}.h-3\\.5{height:14px}.h-4{height:16px}.h-5{height:20px}.h-6{height:24px}.h-7{height:28px}.h-8{height:32px}.h-9{height:36px}.h-\\[1\\.4rem\\]{height:22.4px}.h-\\[100vh\\]{height:100vh}.h-\\[18px\\]{height:18px}.h-\\[220px\\]{height:220px}.h-\\[22px\\]{height:22px}.h-\\[30px\\]{height:30px}.h-\\[min\\(36vh\\,320px\\)\\]{height:min(36vh,320px)}.h-\\[min\\(480px\\,calc\\(100vh-64px\\)\\)\\]{height:min(480px,calc(100vh - 64px))}.h-\\[min\\(54vh\\,520px\\)\\]{height:min(54vh,520px)}.h-\\[min\\(560px\\,calc\\(100vh-32px\\)\\)\\]{height:min(560px,calc(100vh - 32px))}.h-\\[var\\(--issue-sticker-size\\)\\]{height:var(--issue-sticker-size)}.h-\\[var\\(--radix-select-trigger-height\\)\\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.max-h-24{max-height:96px}.max-h-40{max-height:160px}.max-h-56{max-height:224px}.max-h-80{max-height:320px}.max-h-96{max-height:384px}.max-h-\\[150px\\]{max-height:150px}.max-h-\\[220px\\]{max-height:220px}.max-h-\\[320px\\]{max-height:320px}.max-h-\\[420px\\]{max-height:420px}.max-h-\\[90vh\\]{max-height:90vh}.max-h-\\[calc\\(100vh-3rem\\)\\]{max-height:calc(100vh - 48px)}.max-h-\\[min\\(80vh\\,640px\\)\\]{max-height:min(80vh,640px)}.min-h-0{min-height:0px}.min-h-11{min-height:44px}.min-h-\\[108px\\]{min-height:108px}.min-h-\\[120px\\]{min-height:120px}.min-h-\\[140px\\]{min-height:140px}.min-h-\\[220px\\]{min-height:220px}.w-0\\.5{width:2px}.w-1{width:4px}.w-1\\.5{width:6px}.w-1\\/2{width:50%}.w-10{width:40px}.w-12{width:48px}.w-14{width:56px}.w-16{width:64px}.w-2{width:8px}.w-3{width:12px}.w-3\\.5{width:14px}.w-4{width:16px}.w-5{width:20px}.w-6{width:24px}.w-7{width:28px}.w-72{width:288px}.w-8{width:32px}.w-80{width:320px}.w-9{width:36px}.w-\\[1\\.4rem\\]{width:22.4px}.w-\\[18px\\]{width:18px}.w-\\[22\\.5rem\\]{width:360px}.w-\\[22rem\\]{width:352px}.w-\\[30px\\]{width:30px}.w-\\[38px\\]{width:38px}.w-\\[calc\\(100vw-32px\\)\\]{width:calc(100vw - 32px)}.w-\\[min\\(20rem\\,calc\\(100vw_-_1\\.5rem\\)\\)\\]{width:min(320px,calc(100vw - 24px))}.w-\\[min\\(560px\\,calc\\(100vw-32px\\)\\)\\]{width:min(560px,calc(100vw - 32px))}.w-\\[var\\(--radix-popover-trigger-width\\)\\]{width:var(--radix-popover-trigger-width)}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.min-w-0{min-width:0px}.min-w-9{min-width:36px}.min-w-\\[12rem\\]{min-width:192px}.min-w-\\[200px\\]{min-width:200px}.min-w-\\[var\\(--radix-popover-trigger-width\\)\\]{min-width:var(--radix-popover-trigger-width)}.min-w-\\[var\\(--radix-select-trigger-width\\)\\]{min-width:var(--radix-select-trigger-width)}.max-w-\\[10rem\\]{max-width:160px}.max-w-\\[120px\\]{max-width:120px}.max-w-\\[160px\\]{max-width:160px}.max-w-\\[280px\\]{max-width:280px}.max-w-\\[28rem\\]{max-width:448px}.max-w-\\[300px\\]{max-width:300px}.max-w-\\[360px\\]{max-width:360px}.max-w-\\[36rem\\]{max-width:576px}.max-w-\\[400px\\]{max-width:400px}.max-w-\\[420px\\]{max-width:420px}.max-w-\\[428px\\]{max-width:428px}.max-w-\\[480px\\]{max-width:480px}.max-w-\\[62ch\\]{max-width:62ch}.max-w-\\[640px\\]{max-width:640px}.max-w-\\[70ch\\]{max-width:70ch}.max-w-\\[90vw\\]{max-width:90vw}.max-w-\\[960px\\]{max-width:960px}.max-w-\\[calc\\(100vw-24px\\)\\]{max-width:calc(100vw - 24px)}.max-w-\\[var\\(--issue-sticker-size\\)\\]{max-width:var(--issue-sticker-size)}.max-w-full{max-width:100%}.max-w-md{max-width:448px}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.grow{flex-grow:1}.caption-bottom{caption-side:bottom}.border-collapse{border-collapse:collapse}.-translate-x-1{--tw-translate-x: -4px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-1\\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-1{--tw-translate-x: 4px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\\[-50\\%\\]{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\\[18px\\]{--tw-translate-x: 18px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\\[2px\\]{--tw-translate-x: 2px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\\[-50\\%\\]{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes pulse-ring{0%,to{box-shadow:0 0 0 0 var(--brand-ring)}50%{box-shadow:0 0 0 8px transparent}}.animate-pulse-ring{animation:pulse-ring 1.8s var(--ease-in-out) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-col-resize{cursor:col-resize}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.resize-y{resize:vertical}.resize{resize:both}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.content-center{align-content:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0{gap:0px}.gap-0\\.5{gap:2px}.gap-1{gap:4px}.gap-1\\.5{gap:6px}.gap-2{gap:8px}.gap-2\\.5{gap:10px}.gap-3{gap:12px}.gap-3\\.5{gap:14px}.gap-4{gap:16px}.gap-5{gap:20px}.gap-6{gap:24px}.gap-x-5{-moz-column-gap:20px;column-gap:20px}.gap-y-2{row-gap:8px}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(4px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(4px * var(--tw-space-y-reverse))}.space-y-1\\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(6px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(6px * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(8px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(8px * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(12px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(12px * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(16px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(16px * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(20px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(20px * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(24px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(24px * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-line-subtle>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-divide-opacity, 1))}.self-start{align-self:flex-start}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.whitespace-pre-wrap{white-space:pre-wrap}.break-all{word-break:break-all}.break-keep{word-break:keep-all}.\\!rounded-none{border-radius:0!important}.rounded{border-radius:4px}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\\[18px\\]{border-radius:18px}.rounded-\\[20px\\]{border-radius:20px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-none{border-radius:0}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-xs{border-radius:var(--radius-xs)}.rounded-b-md{border-bottom-right-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md)}.rounded-t-md{border-top-left-radius:var(--radius-md);border-top-right-radius:var(--radius-md)}.border{border-width:1px}.border-0{border-width:0px}.border-2{border-width:2px}.border-y{border-top-width:1px;border-bottom-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-t-0{border-top-width:0px}.border-dashed{border-style:dashed}.border-\\[hsl\\(144_8\\%_91\\%\\)\\]{--tw-border-opacity: 1;border-color:hsl(144 8% 91% / var(--tw-border-opacity, 1))}.border-brand{--tw-border-opacity: 1;border-color:hsl(var(--brand-hsl) / var(--tw-border-opacity, 1))}.border-brand-border{border-color:var(--brand-border)}.border-brand\\/30{border-color:hsl(var(--brand-hsl) / .3)}.border-brand\\/40{border-color:hsl(var(--brand-hsl) / .4)}.border-danger\\/20{border-color:hsl(var(--danger) / .2)}.border-danger\\/30{border-color:hsl(var(--danger) / .3)}.border-info\\/20{border-color:hsl(var(--info) / .2)}.border-info\\/30{border-color:hsl(var(--info) / .3)}.border-line{--tw-border-opacity: 1;border-color:hsl(var(--border-default) / var(--tw-border-opacity, 1))}.border-line-focus{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.border-line-strong{--tw-border-opacity: 1;border-color:hsl(var(--border-strong) / var(--tw-border-opacity, 1))}.border-line-subtle{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}.border-line-subtle\\/50{border-color:hsl(var(--border-subtle) / .5)}.border-line-subtle\\/90{border-color:hsl(var(--border-subtle) / .9)}.border-slate-100{--tw-border-opacity: 1;border-color:rgb(241 245 249 / var(--tw-border-opacity, 1))}.border-slate-200{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity, 1))}.border-success{--tw-border-opacity: 1;border-color:hsl(var(--success) / var(--tw-border-opacity, 1))}.border-success\\/20{border-color:hsl(var(--success) / .2)}.border-success\\/30{border-color:hsl(var(--success) / .3)}.border-text-primary{--tw-border-opacity: 1;border-color:hsl(var(--text-primary) / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.border-warning\\/20{border-color:hsl(var(--warning) / .2)}.border-warning\\/30{border-color:hsl(var(--warning) / .3)}.border-white\\/70{border-color:#ffffffb3}.border-white\\/80{border-color:#fffc}.border-white\\/90{border-color:#ffffffe6}.border-l-brand{--tw-border-opacity: 1;border-left-color:hsl(var(--brand-hsl) / var(--tw-border-opacity, 1))}.\\!bg-success-surface{background-color:hsl(var(--success-surface))!important}.bg-\\[var\\(--overlay-backdrop-strong\\)\\]{background-color:var(--overlay-backdrop-strong)}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-black\\/10{background-color:#0000001a}.bg-black\\/40{background-color:#0006}.bg-black\\/80{background-color:#000c}.bg-brand{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.bg-brand-subtle{background-color:var(--brand-subtle)}.bg-brand\\/10{background-color:hsl(var(--brand-hsl) / .1)}.bg-brand\\/5{background-color:hsl(var(--brand-hsl) / .05)}.bg-danger{--tw-bg-opacity: 1;background-color:hsl(var(--danger) / var(--tw-bg-opacity, 1))}.bg-danger-subtle{background-color:hsl(var(--danger-subtle))}.bg-danger\\/10{background-color:hsl(var(--danger) / .1)}.bg-info-subtle{background-color:hsl(var(--info-subtle))}.bg-line{--tw-bg-opacity: 1;background-color:hsl(var(--border-default) / var(--tw-bg-opacity, 1))}.bg-line-subtle{--tw-bg-opacity: 1;background-color:hsl(var(--border-subtle) / var(--tw-bg-opacity, 1))}.bg-success{--tw-bg-opacity: 1;background-color:hsl(var(--success) / var(--tw-bg-opacity, 1))}.bg-success-subtle{background-color:hsl(var(--success-subtle))}.bg-success-surface{background-color:hsl(var(--success-surface))}.bg-surface-0{--tw-bg-opacity: 1;background-color:hsl(var(--surface-0) / var(--tw-bg-opacity, 1))}.bg-surface-1{--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.bg-surface-2{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.bg-surface-3{--tw-bg-opacity: 1;background-color:hsl(var(--surface-3) / var(--tw-bg-opacity, 1))}.bg-surface-inset{--tw-bg-opacity: 1;background-color:hsl(var(--surface-inset) / var(--tw-bg-opacity, 1))}.bg-text-primary{--tw-bg-opacity: 1;background-color:hsl(var(--text-primary) / var(--tw-bg-opacity, 1))}.bg-text-tertiary{--tw-bg-opacity: 1;background-color:hsl(var(--text-tertiary) / var(--tw-bg-opacity, 1))}.bg-transparent{background-color:transparent}.bg-warning{--tw-bg-opacity: 1;background-color:hsl(var(--warning) / var(--tw-bg-opacity, 1))}.bg-warning-subtle{background-color:hsl(var(--warning-subtle))}.bg-warning\\/10{background-color:hsl(var(--warning) / .1)}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\\/10{background-color:#ffffff1a}.bg-white\\/25{background-color:#ffffff40}.fill-text-primary{fill:hsl(var(--text-primary) / 1)}.stroke-\\[3\\]{stroke-width:3}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.\\!p-0{padding:0!important}.p-0{padding:0}.p-0\\.5{padding:2px}.p-1{padding:4px}.p-1\\.5{padding:6px}.p-2{padding:8px}.p-3{padding:12px}.p-3\\.5{padding:14px}.p-4{padding:16px}.p-5{padding:20px}.p-6{padding:24px}.p-7{padding:28px}.p-8{padding:32px}.p-\\[18px\\]{padding:18px}.px-0{padding-left:0;padding-right:0}.px-1\\.5{padding-left:6px;padding-right:6px}.px-2{padding-left:8px;padding-right:8px}.px-2\\.5{padding-left:10px;padding-right:10px}.px-3{padding-left:12px;padding-right:12px}.px-4{padding-left:16px;padding-right:16px}.px-5{padding-left:20px;padding-right:20px}.px-6{padding-left:24px;padding-right:24px}.px-9{padding-left:36px;padding-right:36px}.py-0\\.5{padding-top:2px;padding-bottom:2px}.py-1{padding-top:4px;padding-bottom:4px}.py-1\\.5{padding-top:6px;padding-bottom:6px}.py-10{padding-top:40px;padding-bottom:40px}.py-16{padding-top:64px;padding-bottom:64px}.py-2{padding-top:8px;padding-bottom:8px}.py-2\\.5{padding-top:10px;padding-bottom:10px}.py-3{padding-top:12px;padding-bottom:12px}.py-3\\.5{padding-top:14px;padding-bottom:14px}.py-4{padding-top:16px;padding-bottom:16px}.py-5{padding-top:20px;padding-bottom:20px}.py-6{padding-top:24px;padding-bottom:24px}.py-8{padding-top:32px;padding-bottom:32px}.pb-1{padding-bottom:4px}.pb-3{padding-bottom:12px}.pb-4{padding-bottom:16px}.pl-3{padding-left:12px}.pl-4{padding-left:16px}.pl-5{padding-left:20px}.pl-8{padding-left:32px}.pr-10{padding-right:40px}.pr-3{padding-right:12px}.pr-4{padding-right:16px}.pt-0{padding-top:0}.pt-1{padding-top:4px}.pt-2\\.5{padding-top:10px}.pt-4{padding-top:16px}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-top{vertical-align:top}.align-middle{vertical-align:middle}.font-display{font-family:var(--font-display),ui-sans-serif,system-ui,sans-serif}.font-mono{font-family:var(--font-mono),ui-monospace,SFMono-Regular,Menlo,monospace}.font-sans{font-family:var(--font-sans),ui-sans-serif,system-ui,sans-serif}.text-2xs{font-size:clamp(10.5px,10.19px + .082vw,11.5px);line-height:1.333}.text-\\[0\\.72rem\\]{font-size:11.52px}.text-\\[1\\.45rem\\]{font-size:23.2px}.text-\\[1\\.75rem\\]{font-size:28px}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.text-base{font-size:clamp(14px,13.39px + .163vw,16px);line-height:1.5}.text-lg{font-size:clamp(17px,16.39px + .163vw,19px);line-height:1.471}.text-sm{font-size:clamp(13px,12.54px + .122vw,14.5px);line-height:1.462}.text-xl{font-size:clamp(20px,19.08px + .245vw,23px);line-height:1.45}.text-xs{font-size:clamp(12px,11.69px + .082vw,13px);line-height:1.333}.font-black{font-weight:900}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-4{line-height:16px}.leading-5{line-height:20px}.leading-8{line-height:32px}.leading-\\[1\\.55\\]{line-height:1.55}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.text-\\[hsl\\(145_5\\%_42\\%\\)\\]{--tw-text-opacity: 1;color:hsl(145 5% 42% / var(--tw-text-opacity, 1))}.text-brand{--tw-text-opacity: 1;color:hsl(var(--brand-hsl) / var(--tw-text-opacity, 1))}.text-brand-fg{color:var(--brand-fg)}.text-brand\\/50{color:hsl(var(--brand-hsl) / .5)}.text-danger{--tw-text-opacity: 1;color:hsl(var(--danger) / var(--tw-text-opacity, 1))}.text-danger\\/80{color:hsl(var(--danger) / .8)}.text-info{--tw-text-opacity: 1;color:hsl(var(--info) / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.text-slate-700{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity, 1))}.text-slate-900{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.text-success{--tw-text-opacity: 1;color:hsl(var(--success) / var(--tw-text-opacity, 1))}.text-text-disabled{--tw-text-opacity: 1;color:hsl(var(--text-disabled) / var(--tw-text-opacity, 1))}.text-text-inverse{--tw-text-opacity: 1;color:hsl(var(--text-inverse) / var(--tw-text-opacity, 1))}.text-text-primary{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.text-text-secondary{--tw-text-opacity: 1;color:hsl(var(--text-secondary) / var(--tw-text-opacity, 1))}.text-text-tertiary{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.text-transparent{color:transparent}.text-warning{--tw-text-opacity: 1;color:hsl(var(--warning) / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-white\\/90{color:#ffffffe6}.underline{text-decoration-line:underline}.line-through{text-decoration-line:line-through}.underline-offset-2{text-underline-offset:2px}.underline-offset-4{text-underline-offset:4px}.accent-brand{accent-color:hsl(var(--brand-hsl) / 1)}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.\\!shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1) !important;--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color) !important;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)!important}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\\[0_24px_70px_-28px_rgba\\(16\\,24\\,18\\,0\\.45\\)\\]{--tw-shadow: 0 24px 70px -28px rgba(16,24,18,.45);--tw-shadow-colored: 0 24px 70px -28px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: var(--shadow-lg);--tw-shadow-colored: var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: var(--shadow-md);--tw-shadow-colored: var(--shadow-md);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: var(--shadow-sm);--tw-shadow-colored: var(--shadow-sm);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: var(--shadow-xl);--tw-shadow-colored: var(--shadow-xl);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xs{--tw-shadow: var(--shadow-xs);--tw-shadow-colored: var(--shadow-xs);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.outline-2{outline-width:2px}.outline-offset-0{outline-offset:0px}.outline-transparent{outline-color:transparent}.ring{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-4{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-brand{--tw-ring-opacity: 1;--tw-ring-color: hsl(var(--brand-hsl) / var(--tw-ring-opacity, 1))}.ring-brand-subtle{--tw-ring-color: var(--brand-subtle)}.blur{--tw-blur: blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-\\[2px\\]{--tw-backdrop-blur: blur(2px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,border-color\\,color\\,box-shadow\\]{transition-property:background-color,border-color,color,box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\,border-color\\,box-shadow\\,opacity\\]{transition-property:background-color,color,border-color,box-shadow,opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\,border-color\\,box-shadow\\]{transition-property:background-color,color,border-color,box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\,box-shadow\\]{transition-property:background-color,color,box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\]{transition-property:background-color,color;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[border-color\\,box-shadow\\,background-color\\]{transition-property:border-color,box-shadow,background-color;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[border-color\\,outline-color\\,box-shadow\\,background-color\\,transform\\]{transition-property:border-color,outline-color,box-shadow,background-color,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[max-width\\,height\\,box-shadow\\,background-color\\,border-color\\,transform\\,opacity\\]{transition-property:max-width,height,box-shadow,background-color,border-color,transform,opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[max-width\\]{transition-property:max-width;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[opacity\\,transform\\]{transition-property:opacity,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.duration-\\[var\\(--issue-marker-expand-duration\\)\\]{transition-duration:var(--issue-marker-expand-duration)}.duration-fast{transition-duration:var(--duration-fast)}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:var(--ease-in-out)}.ease-out{transition-timing-function:var(--ease-out)}@keyframes float-gentle{0%,to{transform:translateY(0)}50%{transform:translateY(-2px)}}@keyframes toast-slide{0%{opacity:0;transform:translateY(-14px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes gradient-shift{0%,to{background-position:0% 50%}50%{background-position:100% 50%}}.issue-overlay-shell{border:1px solid hsl(var(--border-subtle));border-radius:var(--radius-lg);background:hsl(var(--surface-1));box-shadow:var(--shadow-md)}.issue-marker-breathe{animation:float-gentle 4.8s var(--ease-in-out) infinite}.issue-marker-breathe:hover,.issue-marker-breathe:focus-visible,.issue-marker-breathe:focus-within{animation-play-state:paused}.\\[overflow-wrap\\:anywhere\\]{overflow-wrap:anywhere}.marker\\:text-text-tertiary *::marker{color:hsl(var(--text-tertiary))}.marker\\:text-text-tertiary::marker{color:hsl(var(--text-tertiary))}.file\\:border-0::file-selector-button{border-width:0px}.file\\:bg-transparent::file-selector-button{background-color:transparent}.file\\:text-sm::file-selector-button{font-size:clamp(13px,12.54px + .122vw,14.5px);line-height:1.462}.file\\:font-medium::file-selector-button{font-weight:500}.file\\:text-text-primary::file-selector-button{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.placeholder\\:text-text-tertiary::-moz-placeholder{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.placeholder\\:text-text-tertiary::placeholder{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.first\\:mt-0:first-child{margin-top:0}.first\\:border-t-0:first-child{border-top-width:0px}.first\\:pt-0:first-child{padding-top:0}.focus-within\\:z-\\[10001\\]:focus-within{z-index:10001}.focus-within\\:border-line-focus:focus-within{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.hover\\:z-\\[10001\\]:hover{z-index:10001}.hover\\:border-line:hover{--tw-border-opacity: 1;border-color:hsl(var(--border-default) / var(--tw-border-opacity, 1))}.hover\\:border-line-strong:hover{--tw-border-opacity: 1;border-color:hsl(var(--border-strong) / var(--tw-border-opacity, 1))}.hover\\:border-line-subtle:hover{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}.hover\\:bg-\\[hsl\\(144_8\\%_96\\%\\)\\]:hover{--tw-bg-opacity: 1;background-color:hsl(144 8% 96% / var(--tw-bg-opacity, 1))}.hover\\:bg-blue-500:hover{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.hover\\:bg-brand-hover:hover{background-color:var(--brand-hover)}.hover\\:bg-brand\\/40:hover{background-color:hsl(var(--brand-hsl) / .4)}.hover\\:bg-danger-subtle:hover{background-color:hsl(var(--danger-subtle))}.hover\\:bg-danger\\/10:hover{background-color:hsl(var(--danger) / .1)}.hover\\:bg-slate-100:hover{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}.hover\\:bg-slate-50:hover{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.hover\\:bg-surface-2:hover{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.hover\\:bg-surface-2\\/60:hover{background-color:hsl(var(--surface-2) / .6)}.hover\\:bg-surface-3:hover{--tw-bg-opacity: 1;background-color:hsl(var(--surface-3) / var(--tw-bg-opacity, 1))}.hover\\:bg-surface-inset:hover{--tw-bg-opacity: 1;background-color:hsl(var(--surface-inset) / var(--tw-bg-opacity, 1))}.hover\\:bg-transparent:hover{background-color:transparent}.hover\\:bg-white\\/20:hover{background-color:#fff3}.hover\\:text-\\[hsl\\(150_10\\%_12\\%\\)\\]:hover{--tw-text-opacity: 1;color:hsl(150 10% 12% / var(--tw-text-opacity, 1))}.hover\\:text-brand-hover:hover{color:var(--brand-hover)}.hover\\:text-danger:hover{--tw-text-opacity: 1;color:hsl(var(--danger) / var(--tw-text-opacity, 1))}.hover\\:text-slate-900:hover{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.hover\\:text-text-primary:hover{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.hover\\:text-text-secondary:hover{--tw-text-opacity: 1;color:hsl(var(--text-secondary) / var(--tw-text-opacity, 1))}.hover\\:underline:hover{text-decoration-line:underline}.hover\\:opacity-90:hover{opacity:.9}.focus\\:border-brand:focus{--tw-border-opacity: 1;border-color:hsl(var(--brand-hsl) / var(--tw-border-opacity, 1))}.focus\\:bg-surface-2:focus{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.focus\\:text-text-primary:focus{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.focus\\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus-visible\\:h-\\[var\\(--issue-sticker-hover-size\\)\\]:focus-visible{height:var(--issue-sticker-hover-size)}.focus-visible\\:max-w-\\[160px\\]:focus-visible{max-width:160px}.focus-visible\\:max-w-\\[480px\\]:focus-visible{max-width:480px}.focus-visible\\:border-line-focus:focus-visible{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.focus-visible\\:border-transparent:focus-visible{border-color:transparent}.focus-visible\\:bg-slate-50:focus-visible{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.focus-visible\\:bg-surface-1:focus-visible{--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.focus-visible\\:bg-surface-2:focus-visible{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.focus-visible\\:text-text-primary:focus-visible{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.focus-visible\\:shadow-focus:focus-visible{--tw-shadow: var(--shadow-focus);--tw-shadow-colored: var(--shadow-focus);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus-visible\\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\\:outline-line-focus:focus-visible{outline-color:hsl(var(--border-focus) / 1)}.focus-visible\\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\\:ring-4:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\\:ring-brand-subtle:focus-visible{--tw-ring-color: var(--brand-subtle)}.focus-visible\\:ring-white\\/40:focus-visible{--tw-ring-color: rgb(255 255 255 / .4)}.active\\:bg-brand-active:active{background-color:var(--brand-active)}.active\\:bg-brand\\/60:active{background-color:hsl(var(--brand-hsl) / .6)}.disabled\\:pointer-events-none:disabled{pointer-events:none}.disabled\\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\\:opacity-40:disabled{opacity:.4}.disabled\\:opacity-50:disabled{opacity:.5}.group\\/marker:hover .group-hover\\/marker\\:h-\\[var\\(--issue-sticker-hover-size\\)\\]{height:var(--issue-sticker-hover-size)}.group\\/marker:hover .group-hover\\/marker\\:max-w-\\[160px\\]{max-width:160px}.group\\/marker:hover .group-hover\\/marker\\:max-w-\\[480px\\]{max-width:480px}.group\\/marker:hover .group-hover\\/marker\\:translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\\/marker:hover .group-hover\\/marker\\:bg-brand-hover{background-color:var(--brand-hover)}.group\\/marker:hover .group-hover\\/marker\\:opacity-100{opacity:1}.group\\/marker:hover .group-hover\\/marker\\:shadow-xl{--tw-shadow: var(--shadow-xl);--tw-shadow-colored: var(--shadow-xl);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.group:focus-visible .group-focus-visible\\:translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:focus-visible .group-focus-visible\\:opacity-100{opacity:1}.group.toaster .group-\\[\\.toaster\\]\\:isolate{isolation:isolate}.group.toast .group-\\[\\.toast\\]\\:rounded-md,.group.toaster .group-\\[\\.toaster\\]\\:rounded-md{border-radius:var(--radius-md)}.group.toaster .group-\\[\\.toaster\\]\\:border{border-width:1px}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-danger\\/30{border-color:hsl(var(--danger) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-info\\/30{border-color:hsl(var(--info) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-success\\/30{border-color:hsl(var(--success) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-warning\\/30{border-color:hsl(var(--warning) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:border-line-subtle{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}.group.toast .group-\\[\\.toast\\]\\:bg-brand{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.group.toast .group-\\[\\.toast\\]\\:bg-surface-2{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-danger-subtle{background-color:hsl(var(--danger-subtle))!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-info-subtle{background-color:hsl(var(--info-subtle))!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-success-surface{background-color:hsl(var(--success-surface))!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-warning-subtle{background-color:hsl(var(--warning-subtle))!important}.group.toaster .group-\\[\\.toaster\\]\\:bg-surface-1{--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:px-4{padding-left:16px;padding-right:16px}.group.toaster .group-\\[\\.toaster\\]\\:py-3{padding-top:12px;padding-bottom:12px}.group.toast .group-\\[\\.toast\\]\\:text-sm{font-size:clamp(13px,12.54px + .122vw,14.5px);line-height:1.462}.group.toast .group-\\[\\.toast\\]\\:text-xs{font-size:clamp(12px,11.69px + .082vw,13px);line-height:1.333}.group.toast .group-\\[\\.toast\\]\\:font-medium{font-weight:500}.group.toast .group-\\[\\.toast\\]\\:text-brand-fg{color:var(--brand-fg)}.group.toast .group-\\[\\.toast\\]\\:text-text-primary{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.group.toast .group-\\[\\.toast\\]\\:text-text-secondary{--tw-text-opacity: 1;color:hsl(var(--text-secondary) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:text-text-primary{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:shadow-sm{--tw-shadow: var(--shadow-sm);--tw-shadow-colored: var(--shadow-sm);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.group.toaster .group-\\[\\.toaster\\]\\:before\\:absolute:before{content:var(--tw-content);position:absolute}.group.toaster .group-\\[\\.toaster\\]\\:before\\:inset-0:before{content:var(--tw-content);top:0;right:0;bottom:0;left:0}.group.toaster .group-\\[\\.toaster\\]\\:before\\:-z-10:before{content:var(--tw-content);z-index:-10}.group.toaster .group-\\[\\.toaster\\]\\:before\\:rounded-md:before{content:var(--tw-content);border-radius:var(--radius-md)}.group.toaster .group-\\[\\.toaster\\]\\:before\\:bg-surface-1:before{content:var(--tw-content);--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:before\\:content-\\[\\'\\'\\]:before{--tw-content: "";content:var(--tw-content)}.peer:focus-visible~.peer-focus-visible\\:shadow-focus{--tw-shadow: var(--shadow-focus);--tw-shadow-colored: var(--shadow-focus);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.peer:disabled~.peer-disabled\\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\\:opacity-60{opacity:.6}.aria-disabled\\:pointer-events-none[aria-disabled=true]{pointer-events:none}.aria-disabled\\:opacity-40[aria-disabled=true]{opacity:.4}.data-\\[disabled\\]\\:pointer-events-none[data-disabled]{pointer-events:none}@keyframes dialog-out{0%{opacity:1;transform:translate(-50%,-50%) scale(1)}to{opacity:0;transform:translate(-50%,calc(-50% + 8px)) scale(.985)}}.data-\\[state\\=closed\\]\\:animate-dialog-out[data-state=closed]{animation:dialog-out var(--duration-out) var(--ease-out) forwards}@keyframes fade-out{0%{opacity:1}to{opacity:0}}.data-\\[state\\=closed\\]\\:animate-fade-out[data-state=closed]{animation:fade-out var(--duration-out) var(--ease-out) forwards}@keyframes scale-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.97)}}.data-\\[state\\=closed\\]\\:animate-scale-out[data-state=closed]{animation:scale-out var(--duration-out) var(--ease-out) forwards}@keyframes sheet-out-left{0%{transform:translate(0)}to{transform:translate(-100%);opacity:.6}}.data-\\[state\\=closed\\]\\:animate-sheet-out-left[data-state=closed]{animation:sheet-out-left var(--duration-base) var(--ease-out) forwards}@keyframes sheet-out-right{0%{transform:translate(0);opacity:1}to{transform:translate(100%);opacity:.6}}.data-\\[state\\=closed\\]\\:animate-sheet-out-right[data-state=closed]{animation:sheet-out-right var(--duration-base) var(--ease-out) forwards}.data-\\[state\\=delayed-open\\]\\:animate-scale-in[data-state=delayed-open]{animation:scale-in var(--duration-base) var(--ease-out) forwards}@keyframes dialog-in{0%{opacity:0;transform:translate(-50%,calc(-50% + 12px)) scale(.985)}to{opacity:1;transform:translate(-50%,-50%) scale(1)}}.data-\\[state\\=open\\]\\:animate-dialog-in[data-state=open]{animation:dialog-in var(--duration-base) var(--ease-out) forwards}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.data-\\[state\\=open\\]\\:animate-fade-in[data-state=open]{animation:fade-in var(--duration-base) var(--ease-out) forwards}@keyframes scale-in{0%{opacity:0;transform:scale(.98)}to{opacity:1;transform:scale(1)}}.data-\\[state\\=open\\]\\:animate-scale-in[data-state=open]{animation:scale-in var(--duration-base) var(--ease-out) forwards}@keyframes sheet-in-left{0%{transform:translate(-100%);opacity:.6}to{transform:translate(0);opacity:1}}.data-\\[state\\=open\\]\\:animate-sheet-in-left[data-state=open]{animation:sheet-in-left var(--duration-base) var(--ease-out) forwards}@keyframes sheet-in-right{0%{transform:translate(100%);opacity:.6}to{transform:translate(0)}}.data-\\[state\\=open\\]\\:animate-sheet-in-right[data-state=open]{animation:sheet-in-right var(--duration-base) var(--ease-out) forwards}.data-\\[range-end\\=true\\]\\:rounded-md[data-range-end=true]{border-radius:var(--radius-md)}.data-\\[range-middle\\=true\\]\\:rounded-none[data-range-middle=true]{border-radius:0}.data-\\[range-start\\=true\\]\\:rounded-md[data-range-start=true]{border-radius:var(--radius-md)}.data-\\[state\\=open\\]\\:border-line-focus[data-state=open]{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.data-\\[highlighted\\]\\:bg-surface-2[data-highlighted]{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.data-\\[range-end\\=true\\]\\:bg-brand[data-range-end=true]{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.data-\\[range-middle\\=true\\]\\:bg-transparent[data-range-middle=true]{background-color:transparent}.data-\\[range-start\\=true\\]\\:bg-brand[data-range-start=true],.data-\\[selected-single\\=true\\]\\:bg-brand[data-selected-single=true]{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.data-\\[state\\=selected\\]\\:bg-brand-subtle[data-state=selected]{background-color:var(--brand-subtle)}.data-\\[today\\=true\\]\\:font-semibold[data-today=true]{font-weight:600}.data-\\[highlighted\\]\\:text-text-primary[data-highlighted]{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.data-\\[placeholder\\]\\:text-text-tertiary[data-placeholder]{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.data-\\[range-end\\=true\\]\\:text-brand-fg[data-range-end=true]{color:var(--brand-fg)}.data-\\[range-middle\\=true\\]\\:text-text-primary[data-range-middle=true]{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.data-\\[range-start\\=true\\]\\:text-brand-fg[data-range-start=true],.data-\\[selected-single\\=true\\]\\:text-brand-fg[data-selected-single=true]{color:var(--brand-fg)}.data-\\[state\\=active\\]\\:text-text-primary[data-state=active],.data-\\[state\\=checked\\]\\:text-text-primary[data-state=checked]{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.data-\\[today\\=true\\]\\:text-brand[data-today=true]{--tw-text-opacity: 1;color:hsl(var(--brand-hsl) / var(--tw-text-opacity, 1))}.data-\\[disabled\\]\\:opacity-50[data-disabled]{opacity:.5}.data-\\[state\\=open\\]\\:shadow-focus[data-state=open]{--tw-shadow: var(--shadow-focus);--tw-shadow-colored: var(--shadow-focus);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.data-\\[range-end\\=true\\]\\:hover\\:bg-brand-hover:hover[data-range-end=true],.data-\\[range-start\\=true\\]\\:hover\\:bg-brand-hover:hover[data-range-start=true],.data-\\[selected-single\\=true\\]\\:hover\\:bg-brand-hover:hover[data-selected-single=true]{background-color:var(--brand-hover)}.data-\\[range-end\\=true\\]\\:hover\\:text-brand-fg:hover[data-range-end=true],.data-\\[range-start\\=true\\]\\:hover\\:text-brand-fg:hover[data-range-start=true],.data-\\[selected-single\\=true\\]\\:hover\\:text-brand-fg:hover[data-selected-single=true]{color:var(--brand-fg)}@media(prefers-reduced-motion:reduce){.motion-reduce\\:animate-none{animation:none}}@media(min-width:640px){.sm\\:flex-row{flex-direction:row}.sm\\:items-start{align-items:flex-start}.sm\\:items-center{align-items:center}.sm\\:justify-end{justify-content:flex-end}.sm\\:justify-between{justify-content:space-between}.sm\\:gap-2\\.5{gap:10px}.sm\\:p-5{padding:20px}.sm\\:p-6{padding:24px}.sm\\:text-2xl{font-size:clamp(26px,24.78px + .327vw,30px);line-height:1.308}.sm\\:text-\\[2\\.1rem\\]{font-size:33.6px}}@media(min-width:768px){.md\\:flex{display:flex}.md\\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\\:grid-cols-\\[minmax\\(0\\,1fr\\)_minmax\\(260px\\,340px\\)\\]{grid-template-columns:minmax(0,1fr) minmax(260px,340px)}}.\\[\\&\\>\\*\\:not\\(summary\\)\\]\\:pl-4>*:not(summary){padding-left:16px}.\\[\\&\\>span\\]\\:line-clamp-1>span{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.\\[\\&\\>tr\\]\\:last\\:border-b-0:last-child>tr{border-bottom-width:0px}.\\[\\&\\[data-state\\=open\\]\\>span\\[data-chevron\\]\\]\\:rotate-180[data-state=open]>span[data-chevron]{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-danger [data-icon]{--tw-text-opacity: 1;color:hsl(var(--danger) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-info [data-icon]{--tw-text-opacity: 1;color:hsl(var(--info) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-success [data-icon]{--tw-text-opacity: 1;color:hsl(var(--success) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-warning [data-icon]{--tw-text-opacity: 1;color:hsl(var(--warning) / var(--tw-text-opacity, 1))}.\\[\\&_tr\\]\\:border-b tr{border-bottom-width:1px}.\\[\\&_tr\\]\\:border-line-subtle tr{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}`;
|
|
40249
|
+
const globalsCSS = `:root,:host{color-scheme:light;--brand-h: 150;--brand-s: 100%;--brand-l: 34%;--brand-hsl: var(--brand-h) var(--brand-s) var(--brand-l);--brand: hsl(var(--brand-hsl));--brand-hover: hsl(var(--brand-h) calc(var(--brand-s) + 3%) calc(var(--brand-l) - 4%));--brand-active: hsl(var(--brand-h) calc(var(--brand-s) + 2%) calc(var(--brand-l) - 8%));--brand-subtle: hsl(var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .14);--brand-border: hsl(var(--brand-h) calc(var(--brand-s) + 8%) calc(var(--brand-l) + 8%) / .34);--brand-fg: hsl(0 0% 100%);--brand-ring: hsl(var(--brand-h) calc(var(--brand-s) + 12%) calc(var(--brand-l) + 10%) / .22);--font-sans: "Pretendard", -apple-system, BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif;--font-display: var(--font-sans);--font-mono: var(--font-sans);--surface-0: 0 0% 99%;--surface-1: 0 0% 100%;--surface-2: 144 8% 96%;--surface-3: 144 6% 93%;--surface-inset: 144 6% 95%;--overlay-backdrop: hsl(0 0% 0% / .24);--overlay-backdrop-strong: hsl(0 0% 0% / .48);--text-primary: 150 10% 12%;--text-secondary: 148 6% 36%;--text-tertiary: 145 5% 42%;--text-disabled: 145 4% 70%;--text-inverse: 0 0% 100%;--border-subtle: 144 8% 91%;--border-default: 144 6% 85%;--border-strong: 144 5% 72%;--border-focus: var(--brand-h) var(--brand-s) var(--brand-l);--success: var(--brand-h) var(--brand-s) var(--brand-l);--success-subtle: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .14;--success-surface: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .24;--warning: 34 82% 52%;--warning-subtle: 34 82% 52% / .14;--danger: 3 68% 56%;--danger-subtle: 3 68% 56% / .12;--danger-deep: 3 62% 40%;--info: 202 52% 44%;--info-subtle: 202 52% 44% / .12;--accent-terra: 13 52% 47%;--accent-terra-subtle: 13 52% 47% / .1;--accent-terra-deep: 13 48% 44%;--accent-blue: 213 86% 56%;--accent-blue-subtle: 213 92% 56% / .12;--accent-blue-deep: 216 78% 40%;--accent-blue-border: 213 86% 62% / .36;--accent-cream: 38 52% 88%;--accent-cream-warm: 40 56% 95%;--accent-cream-deep: 38 48% 82%;--accent-highlight: 55 96% 70%;--accent-ink: 32 13% 11%;--radius-xs: 4px;--radius-sm: 8px;--radius-md: 12px;--radius-lg: 16px;--radius-xl: 20px;--radius-2xl: 24px;--radius-pill: 9999px;--shadow-xs: 0 1px 2px 0 hsl(0 0% 0% / .04);--shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / .06), 0 1px 2px -1px hsl(0 0% 0% / .06);--shadow-md: 0 4px 6px -1px hsl(0 0% 0% / .06), 0 2px 4px -2px hsl(0 0% 0% / .04);--shadow-lg: 0 10px 15px -3px hsl(0 0% 0% / .06), 0 4px 6px -4px hsl(0 0% 0% / .04);--shadow-xl: 0 20px 25px -5px hsl(0 0% 0% / .06), 0 8px 10px -6px hsl(0 0% 0% / .04);--shadow-focus: 0 0 0 3px var(--brand-ring);--shadow-inset: inset 0 1px 2px 0 hsl(0 0% 0% / .04);--shadow-brand: 0 2px 8px hsl(var(--brand-h) calc(var(--brand-s) - 60%) calc(var(--brand-l) - 14%) / .24), 0 6px 20px hsl(var(--brand-h) calc(var(--brand-s) - 70%) calc(var(--brand-l) - 19%) / .16);--duration-instant: 90ms;--duration-fast: .16s;--duration-base: .26s;--duration-out: .18s;--duration-slow: .36s;--ease-out: cubic-bezier(.23, 1, .32, 1);--ease-in-out: cubic-bezier(.45, 0, .15, 1);--ease-spring: cubic-bezier(.18, .94, .32, 1.08);--grid-line: 0 0% 92%}.dark,:host(.dark){color-scheme:dark;--brand-l: 34%;--brand-fg: hsl(0 0% 100%);--surface-0: 150 8% 7%;--surface-1: 150 6% 10%;--surface-2: 150 5% 14%;--surface-3: 150 4% 18%;--surface-inset: 150 6% 6%;--overlay-backdrop: hsl(0 0% 0% / .56);--overlay-backdrop-strong: hsl(0 0% 0% / .8);--text-primary: 40 16% 93%;--text-secondary: 36 8% 68%;--text-tertiary: 34 6% 50%;--text-disabled: 30 5% 34%;--text-inverse: 150 8% 10%;--border-subtle: 150 6% 16%;--border-default: 150 5% 22%;--border-strong: 150 4% 34%;--success: var(--brand-h) var(--brand-s) var(--brand-l);--success-subtle: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .14;--success-surface: var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 14%) / .24;--warning: 34 82% 62%;--danger: 2 70% 64%;--danger-deep: 2 80% 76%;--info: 202 54% 62%;--accent-terra: 13 56% 62%;--accent-terra-subtle: 13 56% 62% / .14;--accent-terra-deep: 13 52% 56%;--accent-blue: 210 92% 68%;--accent-blue-subtle: 210 92% 68% / .18;--accent-blue-deep: 209 100% 78%;--accent-blue-border: 210 92% 72% / .44;--accent-cream: 38 30% 26%;--accent-cream-warm: 38 22% 18%;--accent-cream-deep: 38 26% 32%;--accent-highlight: 55 96% 70%;--accent-ink: 32 13% 8%;--shadow-xs: 0 1px 2px 0 hsl(0 0% 0% / .2);--shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / .3), 0 1px 2px -1px hsl(0 0% 0% / .24);--shadow-md: 0 4px 6px -1px hsl(0 0% 0% / .34), 0 2px 4px -2px hsl(0 0% 0% / .2);--shadow-lg: 0 10px 15px -3px hsl(0 0% 0% / .38), 0 4px 6px -4px hsl(0 0% 0% / .24);--shadow-xl: 0 20px 25px -5px hsl(0 0% 0% / .44), 0 8px 10px -6px hsl(0 0% 0% / .28);--shadow-inset: inset 0 1px 2px 0 hsl(0 0% 0% / .16);--shadow-brand: 0 2px 8px hsl(var(--brand-h) calc(var(--brand-s) - 60%) calc(var(--brand-l) - 24%) / .44), 0 6px 20px hsl(0 0% 0% / .32);--grid-line: 150 6% 14%}*{border-color:hsl(var(--border-default))}html{font-family:var(--font-sans),ui-sans-serif,system-ui,-apple-system,sans-serif;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;font-feature-settings:"cv11","ss01","ss03";background-color:hsl(var(--surface-0))}body{margin:0;min-height:100vh;color:hsl(var(--text-primary));background-color:hsl(var(--surface-0))}::-moz-selection{background-color:hsl(var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 16%) / .22);color:hsl(var(--text-primary))}::selection{background-color:hsl(var(--brand-h) calc(var(--brand-s) + 6%) calc(var(--brand-l) + 16%) / .22);color:hsl(var(--text-primary))}code,kbd,samp,pre{font-family:var(--font-mono),ui-monospace,SFMono-Regular,Menlo,monospace;font-feature-settings:"ss01","ss02","cv11"}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:hsl(var(--border-strong) / .52);border-radius:var(--radius-pill);border:2px solid transparent;background-clip:padding-box}::-webkit-scrollbar-thumb:hover{background:hsl(var(--border-strong) / .9);background-clip:padding-box;border:2px solid transparent}.app-canvas,.grid-canvas{background-color:hsl(var(--surface-0))}.onboarding-canvas{word-break:keep-all;overflow-wrap:break-word;--surface-0: var(--brand-h) var(--brand-s) var(--brand-l);--surface-1: 0 0% 100%;--surface-2: var(--brand-h) 62% 28%;--surface-3: var(--brand-h) 60% 25%;--surface-inset: var(--brand-h) 62% 28%;--text-primary: 0 0% 100%;--text-secondary: var(--brand-h) 46% 90%;--text-tertiary: var(--brand-h) 34% 84%;--text-disabled: var(--brand-h) 20% 72%;--border-subtle: var(--brand-h) 44% 52%;--border-default: var(--brand-h) 40% 58%;--border-strong: var(--brand-h) 36% 70%;--border-focus: 0 0% 100%;--brand-hsl: 0 0% 100%;--brand: hsl(var(--brand-hsl));--brand-hover: hsl(var(--brand-h) 24% 94%);--brand-active: hsl(var(--brand-h) 20% 88%);--brand-fg: hsl(var(--brand-h) var(--brand-s) 24%);--brand-ring: hsl(0 0% 100% / .4);--danger: 6 96% 86%;--danger-subtle: 6 96% 86% / .18;--danger-deep: 6 96% 92%;--warning: 42 96% 82%;--warning-subtle: 42 96% 82% / .18}.onboarding-canvas .bg-surface-1,.onboarding-canvas input,.onboarding-canvas textarea,.onboarding-canvas [role=combobox]{--text-primary: 150 10% 12%;--text-secondary: 148 6% 36%;--text-tertiary: 145 5% 42%;--surface-2: 144 8% 96%;--surface-inset: 144 6% 95%;--border-subtle: 144 8% 91%;--border-default: 144 6% 85%;--brand-hsl: var(--brand-h) var(--brand-s) var(--brand-l);--brand: hsl(var(--brand-hsl));--brand-hover: hsl(var(--brand-h) calc(var(--brand-s) + 3%) calc(var(--brand-l) - 4%));--brand-active: hsl(var(--brand-h) calc(var(--brand-s) + 2%) calc(var(--brand-l) - 8%));--brand-fg: hsl(0 0% 100%);--brand-ring: hsl(var(--brand-h) calc(var(--brand-s) + 12%) calc(var(--brand-l) + 10%) / .22);--warning: 34 82% 52%;--warning-subtle: 34 82% 52% / .14;--danger: 3 68% 56%;--danger-subtle: 3 68% 56% / .12;--danger-deep: 3 62% 40%}@keyframes onboarding-step-in{0%{opacity:0;transform:translate(96px)}to{opacity:1;transform:translate(0)}}.onboarding-step-in{animation:onboarding-step-in var(--duration-slow) var(--ease-out) forwards}@keyframes onboarding-enter-up{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.onboarding-enter{position:fixed;top:0;right:0;bottom:0;left:0;overflow-x:hidden;overflow-y:auto;animation:onboarding-enter-up var(--duration-base) var(--ease-out) forwards}body:has(.onboarding-canvas){background-color:hsl(var(--brand-h) var(--brand-s) var(--brand-l))}.panel-shell,.surface-shell{border:1px solid hsl(var(--border-subtle));border-radius:var(--radius-xl);background:hsl(var(--surface-1));box-shadow:var(--shadow-sm)}.panel-shell-muted,.surface-shell-muted{background:hsl(var(--surface-2));box-shadow:var(--shadow-xs)}.panel-shell-subtle{border:1px solid hsl(var(--border-subtle));border-radius:var(--radius-lg);background:hsl(var(--surface-1));box-shadow:var(--shadow-xs)}.panel-shell-elevated{border:1px solid hsl(var(--border-default));border-radius:var(--radius-xl);background:hsl(var(--surface-1));box-shadow:var(--shadow-md)}.utility-chip,.floating-chip{display:inline-flex;align-items:center;gap:8px;border:1px solid hsl(var(--border-default));border-radius:var(--radius-pill);background:hsl(var(--surface-1));box-shadow:var(--shadow-xs)}.hairline{border-bottom:1px solid hsl(var(--border-subtle))}.mono-label{font-family:var(--font-mono),ui-monospace,monospace;font-size:11px;text-transform:uppercase;color:hsl(var(--text-tertiary))}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-sans),ui-sans-serif,system-ui,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--font-mono),ui-monospace,SFMono-Regular,Menlo,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root,:host{--font-sans: "Pretendard", -apple-system, BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif;--font-mono: var(--font-sans);--issue-marker-expand-duration: .52s;--surface-0: 0 0% 99%;--surface-1: 0 0% 100%;--surface-2: 144 8% 96%;--surface-3: 144 6% 93%;--surface-inset: 144 6% 95%;--overlay-backdrop: hsl(0 0% 0% / .24);--overlay-backdrop-strong: hsl(0 0% 0% / .48);--text-primary: 150 10% 12%;--text-secondary: 148 6% 36%;--text-tertiary: 145 5% 52%;--text-disabled: 145 4% 70%;--border-subtle: 144 8% 91%;--border-default: 144 6% 85%;--border-strong: 144 5% 72%;--grid-line: 0 0% 92%;--shadow-xs: 0 1px 2px 0 hsl(0 0% 0% / .04);--shadow-sm: 0 1px 3px 0 hsl(0 0% 0% / .06), 0 1px 2px -1px hsl(0 0% 0% / .06);--shadow-md: 0 4px 6px -1px hsl(0 0% 0% / .06), 0 2px 4px -2px hsl(0 0% 0% / .04);--shadow-lg: 0 10px 15px -3px hsl(0 0% 0% / .06), 0 4px 6px -4px hsl(0 0% 0% / .04);--shadow-xl: 0 20px 25px -5px hsl(0 0% 0% / .06), 0 8px 10px -6px hsl(0 0% 0% / .04);--shadow-focus: 0 0 0 4px var(--brand-ring);--shadow-inset: inset 0 1px 2px 0 hsl(0 0% 0% / .04)}*{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}#issue-sticker-shadow-root{all:initial;display:block;background-color:transparent;--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1));font-family:var(--font-sans);line-height:1.5}.\\!container{width:100%!important}.container{width:100%}@media(min-width:640px){.\\!container{max-width:640px!important}.container{max-width:640px}}@media(min-width:768px){.\\!container{max-width:768px!important}.container{max-width:768px}}@media(min-width:1024px){.\\!container{max-width:1024px!important}.container{max-width:1024px}}@media(min-width:1280px){.\\!container{max-width:1280px!important}.container{max-width:1280px}}@media(min-width:1536px){.\\!container{max-width:1536px!important}.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.\\!visible{visibility:visible!important}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.inset-x-1{left:4px;right:4px}.inset-y-0{top:0;bottom:0}.-bottom-px{bottom:-1px}.bottom-0{bottom:0}.bottom-4{bottom:16px}.bottom-6{bottom:24px}.left-0{left:0}.left-1\\/2{left:50%}.left-2{left:8px}.left-4{left:16px}.left-\\[50\\%\\]{left:50%}.right-0{right:0}.right-2{right:8px}.right-3{right:12px}.right-4{right:16px}.right-5{right:20px}.right-6{right:24px}.top-0{top:0}.top-1{top:4px}.top-1\\/2{top:50%}.top-2{top:8px}.top-3{top:12px}.top-4{top:16px}.top-5{top:20px}.top-\\[50\\%\\]{top:50%}.top-\\[calc\\(\\(3\\.5rem-1\\.75rem\\)\\/2\\)\\]{top:14px}.top-full{top:100%}.isolate{isolation:isolate}.z-10{z-index:10}.z-20{z-index:20}.z-\\[1000000\\]{z-index:1000000}.z-\\[1000003\\]{z-index:1000003}.z-\\[1000004\\]{z-index:1000004}.z-\\[1000005\\]{z-index:1000005}.z-\\[1000006\\]{z-index:1000006}.z-\\[1000007\\]{z-index:1000007}.z-\\[1000008\\]{z-index:1000008}.z-\\[1000010\\]{z-index:1000010}.z-\\[1000011\\]{z-index:1000011}.z-\\[10001\\]{z-index:10001}.z-\\[999999\\]{z-index:999999}.z-\\[9999\\]{z-index:9999}.m-0{margin:0}.-mx-2{margin-left:-8px;margin-right:-8px}.mx-3{margin-left:12px;margin-right:12px}.my-1{margin-top:4px;margin-bottom:4px}.my-2{margin-top:8px;margin-bottom:8px}.my-2\\.5{margin-top:10px;margin-bottom:10px}.my-3{margin-top:12px;margin-bottom:12px}.my-4{margin-top:16px;margin-bottom:16px}.mb-0\\.5{margin-bottom:2px}.mb-1{margin-bottom:4px}.mb-2{margin-bottom:8px}.mb-3{margin-bottom:12px}.mb-5{margin-bottom:20px}.ml-0\\.5{margin-left:2px}.ml-4{margin-left:16px}.mr-1\\.5{margin-right:6px}.mr-2{margin-right:8px}.mt-0{margin-top:0}.mt-0\\.5{margin-top:2px}.mt-1{margin-top:4px}.mt-2{margin-top:8px}.mt-3{margin-top:12px}.mt-4{margin-top:16px}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-square{aspect-ratio:1 / 1}.\\!h-auto{height:auto!important}.h-1\\.5{height:6px}.h-10{height:40px}.h-11{height:44px}.h-2{height:8px}.h-24{height:96px}.h-3{height:12px}.h-3\\.5{height:14px}.h-4{height:16px}.h-5{height:20px}.h-6{height:24px}.h-7{height:28px}.h-8{height:32px}.h-9{height:36px}.h-\\[1\\.4rem\\]{height:22.4px}.h-\\[100vh\\]{height:100vh}.h-\\[18px\\]{height:18px}.h-\\[220px\\]{height:220px}.h-\\[22px\\]{height:22px}.h-\\[30px\\]{height:30px}.h-\\[min\\(36vh\\,320px\\)\\]{height:min(36vh,320px)}.h-\\[min\\(480px\\,calc\\(100vh-64px\\)\\)\\]{height:min(480px,calc(100vh - 64px))}.h-\\[min\\(54vh\\,520px\\)\\]{height:min(54vh,520px)}.h-\\[min\\(560px\\,calc\\(100vh-32px\\)\\)\\]{height:min(560px,calc(100vh - 32px))}.h-\\[var\\(--issue-sticker-size\\)\\]{height:var(--issue-sticker-size)}.h-\\[var\\(--radix-select-trigger-height\\)\\]{height:var(--radix-select-trigger-height)}.h-auto{height:auto}.h-full{height:100%}.h-px{height:1px}.max-h-24{max-height:96px}.max-h-40{max-height:160px}.max-h-56{max-height:224px}.max-h-80{max-height:320px}.max-h-96{max-height:384px}.max-h-\\[150px\\]{max-height:150px}.max-h-\\[220px\\]{max-height:220px}.max-h-\\[320px\\]{max-height:320px}.max-h-\\[420px\\]{max-height:420px}.max-h-\\[90vh\\]{max-height:90vh}.max-h-\\[calc\\(100vh-3rem\\)\\]{max-height:calc(100vh - 48px)}.max-h-\\[min\\(80vh\\,640px\\)\\]{max-height:min(80vh,640px)}.min-h-0{min-height:0px}.min-h-11{min-height:44px}.min-h-\\[108px\\]{min-height:108px}.min-h-\\[120px\\]{min-height:120px}.min-h-\\[140px\\]{min-height:140px}.min-h-\\[220px\\]{min-height:220px}.w-0\\.5{width:2px}.w-1{width:4px}.w-1\\.5{width:6px}.w-1\\/2{width:50%}.w-10{width:40px}.w-12{width:48px}.w-14{width:56px}.w-16{width:64px}.w-2{width:8px}.w-3{width:12px}.w-3\\.5{width:14px}.w-4{width:16px}.w-5{width:20px}.w-6{width:24px}.w-7{width:28px}.w-72{width:288px}.w-8{width:32px}.w-80{width:320px}.w-9{width:36px}.w-\\[1\\.4rem\\]{width:22.4px}.w-\\[18px\\]{width:18px}.w-\\[22\\.5rem\\]{width:360px}.w-\\[22rem\\]{width:352px}.w-\\[30px\\]{width:30px}.w-\\[38px\\]{width:38px}.w-\\[calc\\(100vw-32px\\)\\]{width:calc(100vw - 32px)}.w-\\[min\\(24rem\\,calc\\(100vw_-_1\\.5rem\\)\\)\\]{width:min(384px,calc(100vw - 24px))}.w-\\[min\\(30rem\\,calc\\(100vw_-_1\\.5rem\\)\\)\\]{width:min(480px,calc(100vw - 24px))}.w-\\[min\\(560px\\,calc\\(100vw-32px\\)\\)\\]{width:min(560px,calc(100vw - 32px))}.w-\\[var\\(--radix-popover-trigger-width\\)\\]{width:var(--radix-popover-trigger-width)}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.min-w-0{min-width:0px}.min-w-9{min-width:36px}.min-w-\\[12rem\\]{min-width:192px}.min-w-\\[200px\\]{min-width:200px}.min-w-\\[var\\(--radix-popover-trigger-width\\)\\]{min-width:var(--radix-popover-trigger-width)}.min-w-\\[var\\(--radix-select-trigger-width\\)\\]{min-width:var(--radix-select-trigger-width)}.max-w-\\[10rem\\]{max-width:160px}.max-w-\\[120px\\]{max-width:120px}.max-w-\\[160px\\]{max-width:160px}.max-w-\\[16rem\\]{max-width:256px}.max-w-\\[260px\\]{max-width:260px}.max-w-\\[280px\\]{max-width:280px}.max-w-\\[28rem\\]{max-width:448px}.max-w-\\[300px\\]{max-width:300px}.max-w-\\[360px\\]{max-width:360px}.max-w-\\[36rem\\]{max-width:576px}.max-w-\\[400px\\]{max-width:400px}.max-w-\\[420px\\]{max-width:420px}.max-w-\\[428px\\]{max-width:428px}.max-w-\\[480px\\]{max-width:480px}.max-w-\\[62ch\\]{max-width:62ch}.max-w-\\[640px\\]{max-width:640px}.max-w-\\[70ch\\]{max-width:70ch}.max-w-\\[90vw\\]{max-width:90vw}.max-w-\\[960px\\]{max-width:960px}.max-w-\\[calc\\(100vw-24px\\)\\]{max-width:calc(100vw - 24px)}.max-w-\\[var\\(--issue-sticker-size\\)\\]{max-width:var(--issue-sticker-size)}.max-w-full{max-width:100%}.max-w-md{max-width:448px}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.grow{flex-grow:1}.caption-bottom{caption-side:bottom}.border-collapse{border-collapse:collapse}.-translate-x-1{--tw-translate-x: -4px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-1\\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-1{--tw-translate-x: 4px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\\[-50\\%\\]{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\\[18px\\]{--tw-translate-x: 18px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\\[2px\\]{--tw-translate-x: 2px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\\[-50\\%\\]{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes pulse-ring{0%,to{box-shadow:0 0 0 0 var(--brand-ring)}50%{box-shadow:0 0 0 8px transparent}}.animate-pulse-ring{animation:pulse-ring 1.8s var(--ease-in-out) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-col-resize{cursor:col-resize}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.resize-y{resize:vertical}.resize{resize:both}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.content-center{align-content:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0{gap:0px}.gap-0\\.5{gap:2px}.gap-1{gap:4px}.gap-1\\.5{gap:6px}.gap-2{gap:8px}.gap-2\\.5{gap:10px}.gap-3{gap:12px}.gap-3\\.5{gap:14px}.gap-4{gap:16px}.gap-5{gap:20px}.gap-6{gap:24px}.gap-x-5{-moz-column-gap:20px;column-gap:20px}.gap-y-2{row-gap:8px}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(4px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(4px * var(--tw-space-y-reverse))}.space-y-1\\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(6px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(6px * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(8px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(8px * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(12px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(12px * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(16px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(16px * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(20px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(20px * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(24px * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(24px * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-line-subtle>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-divide-opacity, 1))}.self-start{align-self:flex-start}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.break-keep{word-break:keep-all}.\\!rounded-none{border-radius:0!important}.rounded{border-radius:4px}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\\[18px\\]{border-radius:18px}.rounded-\\[20px\\]{border-radius:20px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-none{border-radius:0}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-xs{border-radius:var(--radius-xs)}.rounded-b-md{border-bottom-right-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md)}.rounded-t-md{border-top-left-radius:var(--radius-md);border-top-right-radius:var(--radius-md)}.border{border-width:1px}.border-0{border-width:0px}.border-2{border-width:2px}.border-y{border-top-width:1px;border-bottom-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-t-0{border-top-width:0px}.border-dashed{border-style:dashed}.border-\\[hsl\\(144_8\\%_91\\%\\)\\]{--tw-border-opacity: 1;border-color:hsl(144 8% 91% / var(--tw-border-opacity, 1))}.border-brand{--tw-border-opacity: 1;border-color:hsl(var(--brand-hsl) / var(--tw-border-opacity, 1))}.border-brand-border{border-color:var(--brand-border)}.border-brand\\/30{border-color:hsl(var(--brand-hsl) / .3)}.border-brand\\/40{border-color:hsl(var(--brand-hsl) / .4)}.border-danger\\/20{border-color:hsl(var(--danger) / .2)}.border-danger\\/30{border-color:hsl(var(--danger) / .3)}.border-info\\/20{border-color:hsl(var(--info) / .2)}.border-info\\/30{border-color:hsl(var(--info) / .3)}.border-line{--tw-border-opacity: 1;border-color:hsl(var(--border-default) / var(--tw-border-opacity, 1))}.border-line-focus{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.border-line-strong{--tw-border-opacity: 1;border-color:hsl(var(--border-strong) / var(--tw-border-opacity, 1))}.border-line-subtle{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}.border-line-subtle\\/50{border-color:hsl(var(--border-subtle) / .5)}.border-line-subtle\\/90{border-color:hsl(var(--border-subtle) / .9)}.border-slate-100{--tw-border-opacity: 1;border-color:rgb(241 245 249 / var(--tw-border-opacity, 1))}.border-slate-200{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity, 1))}.border-success{--tw-border-opacity: 1;border-color:hsl(var(--success) / var(--tw-border-opacity, 1))}.border-success\\/20{border-color:hsl(var(--success) / .2)}.border-success\\/30{border-color:hsl(var(--success) / .3)}.border-text-primary{--tw-border-opacity: 1;border-color:hsl(var(--text-primary) / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.border-warning\\/20{border-color:hsl(var(--warning) / .2)}.border-warning\\/30{border-color:hsl(var(--warning) / .3)}.border-white\\/70{border-color:#ffffffb3}.border-white\\/80{border-color:#fffc}.border-white\\/90{border-color:#ffffffe6}.border-l-brand{--tw-border-opacity: 1;border-left-color:hsl(var(--brand-hsl) / var(--tw-border-opacity, 1))}.\\!bg-success-surface{background-color:hsl(var(--success-surface))!important}.bg-\\[var\\(--overlay-backdrop-strong\\)\\]{background-color:var(--overlay-backdrop-strong)}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-black\\/10{background-color:#0000001a}.bg-black\\/40{background-color:#0006}.bg-black\\/80{background-color:#000c}.bg-brand{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.bg-brand-subtle{background-color:var(--brand-subtle)}.bg-brand\\/10{background-color:hsl(var(--brand-hsl) / .1)}.bg-brand\\/5{background-color:hsl(var(--brand-hsl) / .05)}.bg-danger{--tw-bg-opacity: 1;background-color:hsl(var(--danger) / var(--tw-bg-opacity, 1))}.bg-danger-subtle{background-color:hsl(var(--danger-subtle))}.bg-danger\\/10{background-color:hsl(var(--danger) / .1)}.bg-info-subtle{background-color:hsl(var(--info-subtle))}.bg-line{--tw-bg-opacity: 1;background-color:hsl(var(--border-default) / var(--tw-bg-opacity, 1))}.bg-line-subtle{--tw-bg-opacity: 1;background-color:hsl(var(--border-subtle) / var(--tw-bg-opacity, 1))}.bg-success{--tw-bg-opacity: 1;background-color:hsl(var(--success) / var(--tw-bg-opacity, 1))}.bg-success-subtle{background-color:hsl(var(--success-subtle))}.bg-success-surface{background-color:hsl(var(--success-surface))}.bg-surface-0{--tw-bg-opacity: 1;background-color:hsl(var(--surface-0) / var(--tw-bg-opacity, 1))}.bg-surface-1{--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.bg-surface-2{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.bg-surface-3{--tw-bg-opacity: 1;background-color:hsl(var(--surface-3) / var(--tw-bg-opacity, 1))}.bg-surface-inset{--tw-bg-opacity: 1;background-color:hsl(var(--surface-inset) / var(--tw-bg-opacity, 1))}.bg-text-primary{--tw-bg-opacity: 1;background-color:hsl(var(--text-primary) / var(--tw-bg-opacity, 1))}.bg-text-tertiary{--tw-bg-opacity: 1;background-color:hsl(var(--text-tertiary) / var(--tw-bg-opacity, 1))}.bg-transparent{background-color:transparent}.bg-warning{--tw-bg-opacity: 1;background-color:hsl(var(--warning) / var(--tw-bg-opacity, 1))}.bg-warning-subtle{background-color:hsl(var(--warning-subtle))}.bg-warning\\/10{background-color:hsl(var(--warning) / .1)}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-white\\/10{background-color:#ffffff1a}.bg-white\\/25{background-color:#ffffff40}.fill-text-primary{fill:hsl(var(--text-primary) / 1)}.stroke-\\[3\\]{stroke-width:3}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.\\!p-0{padding:0!important}.p-0{padding:0}.p-0\\.5{padding:2px}.p-1{padding:4px}.p-1\\.5{padding:6px}.p-2{padding:8px}.p-3{padding:12px}.p-3\\.5{padding:14px}.p-4{padding:16px}.p-5{padding:20px}.p-6{padding:24px}.p-7{padding:28px}.p-8{padding:32px}.p-\\[18px\\]{padding:18px}.px-0{padding-left:0;padding-right:0}.px-1\\.5{padding-left:6px;padding-right:6px}.px-2{padding-left:8px;padding-right:8px}.px-2\\.5{padding-left:10px;padding-right:10px}.px-3{padding-left:12px;padding-right:12px}.px-4{padding-left:16px;padding-right:16px}.px-5{padding-left:20px;padding-right:20px}.px-6{padding-left:24px;padding-right:24px}.px-9{padding-left:36px;padding-right:36px}.py-0\\.5{padding-top:2px;padding-bottom:2px}.py-1{padding-top:4px;padding-bottom:4px}.py-1\\.5{padding-top:6px;padding-bottom:6px}.py-10{padding-top:40px;padding-bottom:40px}.py-16{padding-top:64px;padding-bottom:64px}.py-2{padding-top:8px;padding-bottom:8px}.py-2\\.5{padding-top:10px;padding-bottom:10px}.py-3{padding-top:12px;padding-bottom:12px}.py-3\\.5{padding-top:14px;padding-bottom:14px}.py-4{padding-top:16px;padding-bottom:16px}.py-5{padding-top:20px;padding-bottom:20px}.py-6{padding-top:24px;padding-bottom:24px}.py-8{padding-top:32px;padding-bottom:32px}.pb-1{padding-bottom:4px}.pb-3{padding-bottom:12px}.pb-4{padding-bottom:16px}.pl-0{padding-left:0}.pl-3{padding-left:12px}.pl-4{padding-left:16px}.pl-5{padding-left:20px}.pl-8{padding-left:32px}.pr-10{padding-right:40px}.pr-3{padding-right:12px}.pr-4{padding-right:16px}.pt-0{padding-top:0}.pt-1{padding-top:4px}.pt-2\\.5{padding-top:10px}.pt-4{padding-top:16px}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-top{vertical-align:top}.align-middle{vertical-align:middle}.font-display{font-family:var(--font-display),ui-sans-serif,system-ui,sans-serif}.font-mono{font-family:var(--font-mono),ui-monospace,SFMono-Regular,Menlo,monospace}.font-sans{font-family:var(--font-sans),ui-sans-serif,system-ui,sans-serif}.text-2xs{font-size:clamp(10.5px,10.19px + .082vw,11.5px);line-height:1.333}.text-\\[0\\.72rem\\]{font-size:11.52px}.text-\\[1\\.45rem\\]{font-size:23.2px}.text-\\[1\\.75rem\\]{font-size:28px}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.text-base{font-size:clamp(14px,13.39px + .163vw,16px);line-height:1.5}.text-lg{font-size:clamp(17px,16.39px + .163vw,19px);line-height:1.471}.text-sm{font-size:clamp(13px,12.54px + .122vw,14.5px);line-height:1.462}.text-xl{font-size:clamp(20px,19.08px + .245vw,23px);line-height:1.45}.text-xs{font-size:clamp(12px,11.69px + .082vw,13px);line-height:1.333}.font-black{font-weight:900}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-4{line-height:16px}.leading-5{line-height:20px}.leading-8{line-height:32px}.leading-\\[1\\.55\\]{line-height:1.55}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.text-\\[hsl\\(145_5\\%_42\\%\\)\\]{--tw-text-opacity: 1;color:hsl(145 5% 42% / var(--tw-text-opacity, 1))}.text-brand{--tw-text-opacity: 1;color:hsl(var(--brand-hsl) / var(--tw-text-opacity, 1))}.text-brand-fg{color:var(--brand-fg)}.text-brand\\/50{color:hsl(var(--brand-hsl) / .5)}.text-danger{--tw-text-opacity: 1;color:hsl(var(--danger) / var(--tw-text-opacity, 1))}.text-danger\\/80{color:hsl(var(--danger) / .8)}.text-info{--tw-text-opacity: 1;color:hsl(var(--info) / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.text-slate-700{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity, 1))}.text-slate-900{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.text-success{--tw-text-opacity: 1;color:hsl(var(--success) / var(--tw-text-opacity, 1))}.text-text-disabled{--tw-text-opacity: 1;color:hsl(var(--text-disabled) / var(--tw-text-opacity, 1))}.text-text-inverse{--tw-text-opacity: 1;color:hsl(var(--text-inverse) / var(--tw-text-opacity, 1))}.text-text-primary{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.text-text-secondary{--tw-text-opacity: 1;color:hsl(var(--text-secondary) / var(--tw-text-opacity, 1))}.text-text-tertiary{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.text-transparent{color:transparent}.text-warning{--tw-text-opacity: 1;color:hsl(var(--warning) / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-white\\/90{color:#ffffffe6}.underline{text-decoration-line:underline}.line-through{text-decoration-line:line-through}.underline-offset-2{text-underline-offset:2px}.underline-offset-4{text-underline-offset:4px}.accent-brand{accent-color:hsl(var(--brand-hsl) / 1)}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.\\!shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1) !important;--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color) !important;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)!important}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / .25);--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\\[0_24px_70px_-28px_rgba\\(16\\,24\\,18\\,0\\.45\\)\\]{--tw-shadow: 0 24px 70px -28px rgba(16,24,18,.45);--tw-shadow-colored: 0 24px 70px -28px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: var(--shadow-lg);--tw-shadow-colored: var(--shadow-lg);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: var(--shadow-md);--tw-shadow-colored: var(--shadow-md);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: var(--shadow-sm);--tw-shadow-colored: var(--shadow-sm);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: var(--shadow-xl);--tw-shadow-colored: var(--shadow-xl);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xs{--tw-shadow: var(--shadow-xs);--tw-shadow-colored: var(--shadow-xs);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.outline-2{outline-width:2px}.outline-offset-0{outline-offset:0px}.outline-transparent{outline-color:transparent}.ring{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-4{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-brand{--tw-ring-opacity: 1;--tw-ring-color: hsl(var(--brand-hsl) / var(--tw-ring-opacity, 1))}.ring-brand-subtle{--tw-ring-color: var(--brand-subtle)}.blur{--tw-blur: blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-\\[2px\\]{--tw-backdrop-blur: blur(2px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,border-color\\,color\\,box-shadow\\]{transition-property:background-color,border-color,color,box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\,border-color\\,box-shadow\\,opacity\\]{transition-property:background-color,color,border-color,box-shadow,opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\,border-color\\,box-shadow\\]{transition-property:background-color,color,border-color,box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\,box-shadow\\]{transition-property:background-color,color,box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[background-color\\,color\\]{transition-property:background-color,color;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[border-color\\,box-shadow\\,background-color\\]{transition-property:border-color,box-shadow,background-color;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[border-color\\,outline-color\\,box-shadow\\,background-color\\,transform\\]{transition-property:border-color,outline-color,box-shadow,background-color,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[max-width\\,height\\,box-shadow\\,background-color\\,border-color\\,transform\\,opacity\\]{transition-property:max-width,height,box-shadow,background-color,border-color,transform,opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[max-width\\]{transition-property:max-width;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\\[opacity\\,transform\\]{transition-property:opacity,transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}.duration-\\[var\\(--issue-marker-expand-duration\\)\\]{transition-duration:var(--issue-marker-expand-duration)}.duration-fast{transition-duration:var(--duration-fast)}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:var(--ease-in-out)}.ease-out{transition-timing-function:var(--ease-out)}@keyframes float-gentle{0%,to{transform:translateY(0)}50%{transform:translateY(-2px)}}@keyframes toast-slide{0%{opacity:0;transform:translateY(-14px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes gradient-shift{0%,to{background-position:0% 50%}50%{background-position:100% 50%}}.issue-overlay-shell{border:1px solid hsl(var(--border-subtle));border-radius:var(--radius-lg);background:hsl(var(--surface-1));box-shadow:var(--shadow-md)}.issue-marker-breathe{animation:float-gentle 4.8s var(--ease-in-out) infinite}.issue-marker-breathe:hover,.issue-marker-breathe:focus-visible,.issue-marker-breathe:focus-within{animation-play-state:paused}.\\[overflow-wrap\\:anywhere\\]{overflow-wrap:anywhere}.marker\\:text-text-tertiary *::marker{color:hsl(var(--text-tertiary))}.marker\\:text-text-tertiary::marker{color:hsl(var(--text-tertiary))}.file\\:border-0::file-selector-button{border-width:0px}.file\\:bg-transparent::file-selector-button{background-color:transparent}.file\\:text-sm::file-selector-button{font-size:clamp(13px,12.54px + .122vw,14.5px);line-height:1.462}.file\\:font-medium::file-selector-button{font-weight:500}.file\\:text-text-primary::file-selector-button{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.placeholder\\:text-text-tertiary::-moz-placeholder{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.placeholder\\:text-text-tertiary::placeholder{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.first\\:mt-0:first-child{margin-top:0}.first\\:border-t-0:first-child{border-top-width:0px}.first\\:pt-0:first-child{padding-top:0}.focus-within\\:z-\\[10001\\]:focus-within{z-index:10001}.focus-within\\:border-line-focus:focus-within{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.hover\\:z-\\[10001\\]:hover{z-index:10001}.hover\\:border-line:hover{--tw-border-opacity: 1;border-color:hsl(var(--border-default) / var(--tw-border-opacity, 1))}.hover\\:border-line-strong:hover{--tw-border-opacity: 1;border-color:hsl(var(--border-strong) / var(--tw-border-opacity, 1))}.hover\\:border-line-subtle:hover{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}.hover\\:bg-\\[hsl\\(144_8\\%_96\\%\\)\\]:hover{--tw-bg-opacity: 1;background-color:hsl(144 8% 96% / var(--tw-bg-opacity, 1))}.hover\\:bg-blue-500:hover{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.hover\\:bg-brand-hover:hover{background-color:var(--brand-hover)}.hover\\:bg-brand\\/40:hover{background-color:hsl(var(--brand-hsl) / .4)}.hover\\:bg-danger-subtle:hover{background-color:hsl(var(--danger-subtle))}.hover\\:bg-danger\\/10:hover{background-color:hsl(var(--danger) / .1)}.hover\\:bg-slate-100:hover{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}.hover\\:bg-slate-50:hover{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.hover\\:bg-surface-2:hover{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.hover\\:bg-surface-2\\/60:hover{background-color:hsl(var(--surface-2) / .6)}.hover\\:bg-surface-3:hover{--tw-bg-opacity: 1;background-color:hsl(var(--surface-3) / var(--tw-bg-opacity, 1))}.hover\\:bg-surface-inset:hover{--tw-bg-opacity: 1;background-color:hsl(var(--surface-inset) / var(--tw-bg-opacity, 1))}.hover\\:bg-transparent:hover{background-color:transparent}.hover\\:bg-white\\/20:hover{background-color:#fff3}.hover\\:text-\\[hsl\\(150_10\\%_12\\%\\)\\]:hover{--tw-text-opacity: 1;color:hsl(150 10% 12% / var(--tw-text-opacity, 1))}.hover\\:text-brand-hover:hover{color:var(--brand-hover)}.hover\\:text-danger:hover{--tw-text-opacity: 1;color:hsl(var(--danger) / var(--tw-text-opacity, 1))}.hover\\:text-slate-900:hover{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.hover\\:text-text-primary:hover{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.hover\\:text-text-secondary:hover{--tw-text-opacity: 1;color:hsl(var(--text-secondary) / var(--tw-text-opacity, 1))}.hover\\:underline:hover{text-decoration-line:underline}.hover\\:opacity-90:hover{opacity:.9}.focus\\:border-brand:focus{--tw-border-opacity: 1;border-color:hsl(var(--brand-hsl) / var(--tw-border-opacity, 1))}.focus\\:bg-surface-2:focus{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.focus\\:text-text-primary:focus{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.focus\\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus-visible\\:h-\\[var\\(--issue-sticker-hover-size\\)\\]:focus-visible{height:var(--issue-sticker-hover-size)}.focus-visible\\:max-w-\\[160px\\]:focus-visible{max-width:160px}.focus-visible\\:max-w-\\[480px\\]:focus-visible{max-width:480px}.focus-visible\\:border-line-focus:focus-visible{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.focus-visible\\:border-transparent:focus-visible{border-color:transparent}.focus-visible\\:bg-slate-50:focus-visible{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.focus-visible\\:bg-surface-1:focus-visible{--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.focus-visible\\:bg-surface-2:focus-visible{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.focus-visible\\:text-text-primary:focus-visible{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.focus-visible\\:shadow-focus:focus-visible{--tw-shadow: var(--shadow-focus);--tw-shadow-colored: var(--shadow-focus);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus-visible\\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\\:outline-line-focus:focus-visible{outline-color:hsl(var(--border-focus) / 1)}.focus-visible\\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\\:ring-4:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\\:ring-brand-subtle:focus-visible{--tw-ring-color: var(--brand-subtle)}.focus-visible\\:ring-white\\/40:focus-visible{--tw-ring-color: rgb(255 255 255 / .4)}.active\\:bg-brand-active:active{background-color:var(--brand-active)}.active\\:bg-brand\\/60:active{background-color:hsl(var(--brand-hsl) / .6)}.disabled\\:pointer-events-none:disabled{pointer-events:none}.disabled\\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\\:opacity-40:disabled{opacity:.4}.disabled\\:opacity-50:disabled{opacity:.5}.group\\/marker:hover .group-hover\\/marker\\:h-\\[var\\(--issue-sticker-hover-size\\)\\]{height:var(--issue-sticker-hover-size)}.group\\/marker:hover .group-hover\\/marker\\:max-w-\\[160px\\]{max-width:160px}.group\\/marker:hover .group-hover\\/marker\\:max-w-\\[480px\\]{max-width:480px}.group\\/marker:hover .group-hover\\/marker\\:translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group\\/marker:hover .group-hover\\/marker\\:bg-brand-hover{background-color:var(--brand-hover)}.group\\/marker:hover .group-hover\\/marker\\:opacity-100{opacity:1}.group\\/marker:hover .group-hover\\/marker\\:shadow-xl{--tw-shadow: var(--shadow-xl);--tw-shadow-colored: var(--shadow-xl);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.group:focus-visible .group-focus-visible\\:translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:focus-visible .group-focus-visible\\:opacity-100{opacity:1}.group.toaster .group-\\[\\.toaster\\]\\:isolate{isolation:isolate}.group.toast .group-\\[\\.toast\\]\\:rounded-md,.group.toaster .group-\\[\\.toaster\\]\\:rounded-md{border-radius:var(--radius-md)}.group.toaster .group-\\[\\.toaster\\]\\:border{border-width:1px}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-danger\\/30{border-color:hsl(var(--danger) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-info\\/30{border-color:hsl(var(--info) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-success\\/30{border-color:hsl(var(--success) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!border-warning\\/30{border-color:hsl(var(--warning) / .3)!important}.group.toaster .group-\\[\\.toaster\\]\\:border-line-subtle{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}.group.toast .group-\\[\\.toast\\]\\:bg-brand{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.group.toast .group-\\[\\.toast\\]\\:bg-surface-2{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-danger-subtle{background-color:hsl(var(--danger-subtle))!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-info-subtle{background-color:hsl(var(--info-subtle))!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-success-surface{background-color:hsl(var(--success-surface))!important}.group.toaster .group-\\[\\.toaster\\]\\:\\!bg-warning-subtle{background-color:hsl(var(--warning-subtle))!important}.group.toaster .group-\\[\\.toaster\\]\\:bg-surface-1{--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:px-4{padding-left:16px;padding-right:16px}.group.toaster .group-\\[\\.toaster\\]\\:py-3{padding-top:12px;padding-bottom:12px}.group.toast .group-\\[\\.toast\\]\\:text-sm{font-size:clamp(13px,12.54px + .122vw,14.5px);line-height:1.462}.group.toast .group-\\[\\.toast\\]\\:text-xs{font-size:clamp(12px,11.69px + .082vw,13px);line-height:1.333}.group.toast .group-\\[\\.toast\\]\\:font-medium{font-weight:500}.group.toast .group-\\[\\.toast\\]\\:text-brand-fg{color:var(--brand-fg)}.group.toast .group-\\[\\.toast\\]\\:text-text-primary{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.group.toast .group-\\[\\.toast\\]\\:text-text-secondary{--tw-text-opacity: 1;color:hsl(var(--text-secondary) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:text-text-primary{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:shadow-sm{--tw-shadow: var(--shadow-sm);--tw-shadow-colored: var(--shadow-sm);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.group.toaster .group-\\[\\.toaster\\]\\:before\\:absolute:before{content:var(--tw-content);position:absolute}.group.toaster .group-\\[\\.toaster\\]\\:before\\:inset-0:before{content:var(--tw-content);top:0;right:0;bottom:0;left:0}.group.toaster .group-\\[\\.toaster\\]\\:before\\:-z-10:before{content:var(--tw-content);z-index:-10}.group.toaster .group-\\[\\.toaster\\]\\:before\\:rounded-md:before{content:var(--tw-content);border-radius:var(--radius-md)}.group.toaster .group-\\[\\.toaster\\]\\:before\\:bg-surface-1:before{content:var(--tw-content);--tw-bg-opacity: 1;background-color:hsl(var(--surface-1) / var(--tw-bg-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:before\\:content-\\[\\'\\'\\]:before{--tw-content: "";content:var(--tw-content)}.peer:focus-visible~.peer-focus-visible\\:shadow-focus{--tw-shadow: var(--shadow-focus);--tw-shadow-colored: var(--shadow-focus);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.peer:disabled~.peer-disabled\\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\\:opacity-60{opacity:.6}.aria-disabled\\:pointer-events-none[aria-disabled=true]{pointer-events:none}.aria-disabled\\:opacity-40[aria-disabled=true]{opacity:.4}.data-\\[disabled\\]\\:pointer-events-none[data-disabled]{pointer-events:none}@keyframes dialog-out{0%{opacity:1;transform:translate(-50%,-50%) scale(1)}to{opacity:0;transform:translate(-50%,calc(-50% + 8px)) scale(.985)}}.data-\\[state\\=closed\\]\\:animate-dialog-out[data-state=closed]{animation:dialog-out var(--duration-out) var(--ease-out) forwards}@keyframes fade-out{0%{opacity:1}to{opacity:0}}.data-\\[state\\=closed\\]\\:animate-fade-out[data-state=closed]{animation:fade-out var(--duration-out) var(--ease-out) forwards}@keyframes scale-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.97)}}.data-\\[state\\=closed\\]\\:animate-scale-out[data-state=closed]{animation:scale-out var(--duration-out) var(--ease-out) forwards}@keyframes sheet-out-left{0%{transform:translate(0)}to{transform:translate(-100%);opacity:.6}}.data-\\[state\\=closed\\]\\:animate-sheet-out-left[data-state=closed]{animation:sheet-out-left var(--duration-base) var(--ease-out) forwards}@keyframes sheet-out-right{0%{transform:translate(0);opacity:1}to{transform:translate(100%);opacity:.6}}.data-\\[state\\=closed\\]\\:animate-sheet-out-right[data-state=closed]{animation:sheet-out-right var(--duration-base) var(--ease-out) forwards}.data-\\[state\\=delayed-open\\]\\:animate-scale-in[data-state=delayed-open]{animation:scale-in var(--duration-base) var(--ease-out) forwards}@keyframes dialog-in{0%{opacity:0;transform:translate(-50%,calc(-50% + 12px)) scale(.985)}to{opacity:1;transform:translate(-50%,-50%) scale(1)}}.data-\\[state\\=open\\]\\:animate-dialog-in[data-state=open]{animation:dialog-in var(--duration-base) var(--ease-out) forwards}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.data-\\[state\\=open\\]\\:animate-fade-in[data-state=open]{animation:fade-in var(--duration-base) var(--ease-out) forwards}@keyframes scale-in{0%{opacity:0;transform:scale(.98)}to{opacity:1;transform:scale(1)}}.data-\\[state\\=open\\]\\:animate-scale-in[data-state=open]{animation:scale-in var(--duration-base) var(--ease-out) forwards}@keyframes sheet-in-left{0%{transform:translate(-100%);opacity:.6}to{transform:translate(0);opacity:1}}.data-\\[state\\=open\\]\\:animate-sheet-in-left[data-state=open]{animation:sheet-in-left var(--duration-base) var(--ease-out) forwards}@keyframes sheet-in-right{0%{transform:translate(100%);opacity:.6}to{transform:translate(0)}}.data-\\[state\\=open\\]\\:animate-sheet-in-right[data-state=open]{animation:sheet-in-right var(--duration-base) var(--ease-out) forwards}.data-\\[range-end\\=true\\]\\:rounded-md[data-range-end=true]{border-radius:var(--radius-md)}.data-\\[range-middle\\=true\\]\\:rounded-none[data-range-middle=true]{border-radius:0}.data-\\[range-start\\=true\\]\\:rounded-md[data-range-start=true]{border-radius:var(--radius-md)}.data-\\[state\\=open\\]\\:border-line-focus[data-state=open]{--tw-border-opacity: 1;border-color:hsl(var(--border-focus) / var(--tw-border-opacity, 1))}.data-\\[highlighted\\]\\:bg-surface-2[data-highlighted]{--tw-bg-opacity: 1;background-color:hsl(var(--surface-2) / var(--tw-bg-opacity, 1))}.data-\\[range-end\\=true\\]\\:bg-brand[data-range-end=true]{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.data-\\[range-middle\\=true\\]\\:bg-transparent[data-range-middle=true]{background-color:transparent}.data-\\[range-start\\=true\\]\\:bg-brand[data-range-start=true],.data-\\[selected-single\\=true\\]\\:bg-brand[data-selected-single=true]{--tw-bg-opacity: 1;background-color:hsl(var(--brand-hsl) / var(--tw-bg-opacity, 1))}.data-\\[state\\=selected\\]\\:bg-brand-subtle[data-state=selected]{background-color:var(--brand-subtle)}.data-\\[today\\=true\\]\\:font-semibold[data-today=true]{font-weight:600}.data-\\[highlighted\\]\\:text-text-primary[data-highlighted]{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.data-\\[placeholder\\]\\:text-text-tertiary[data-placeholder]{--tw-text-opacity: 1;color:hsl(var(--text-tertiary) / var(--tw-text-opacity, 1))}.data-\\[range-end\\=true\\]\\:text-brand-fg[data-range-end=true]{color:var(--brand-fg)}.data-\\[range-middle\\=true\\]\\:text-text-primary[data-range-middle=true]{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.data-\\[range-start\\=true\\]\\:text-brand-fg[data-range-start=true],.data-\\[selected-single\\=true\\]\\:text-brand-fg[data-selected-single=true]{color:var(--brand-fg)}.data-\\[state\\=active\\]\\:text-text-primary[data-state=active],.data-\\[state\\=checked\\]\\:text-text-primary[data-state=checked]{--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.data-\\[today\\=true\\]\\:text-brand[data-today=true]{--tw-text-opacity: 1;color:hsl(var(--brand-hsl) / var(--tw-text-opacity, 1))}.data-\\[disabled\\]\\:opacity-50[data-disabled]{opacity:.5}.data-\\[state\\=open\\]\\:shadow-focus[data-state=open]{--tw-shadow: var(--shadow-focus);--tw-shadow-colored: var(--shadow-focus);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.data-\\[range-end\\=true\\]\\:hover\\:bg-brand-hover:hover[data-range-end=true],.data-\\[range-start\\=true\\]\\:hover\\:bg-brand-hover:hover[data-range-start=true],.data-\\[selected-single\\=true\\]\\:hover\\:bg-brand-hover:hover[data-selected-single=true]{background-color:var(--brand-hover)}.data-\\[range-end\\=true\\]\\:hover\\:text-brand-fg:hover[data-range-end=true],.data-\\[range-start\\=true\\]\\:hover\\:text-brand-fg:hover[data-range-start=true],.data-\\[selected-single\\=true\\]\\:hover\\:text-brand-fg:hover[data-selected-single=true]{color:var(--brand-fg)}@media(prefers-reduced-motion:reduce){.motion-reduce\\:animate-none{animation:none}}@media(min-width:640px){.sm\\:flex-row{flex-direction:row}.sm\\:items-start{align-items:flex-start}.sm\\:items-center{align-items:center}.sm\\:justify-end{justify-content:flex-end}.sm\\:justify-between{justify-content:space-between}.sm\\:gap-2\\.5{gap:10px}.sm\\:p-5{padding:20px}.sm\\:p-6{padding:24px}.sm\\:text-2xl{font-size:clamp(26px,24.78px + .327vw,30px);line-height:1.308}.sm\\:text-\\[2\\.1rem\\]{font-size:33.6px}}@media(min-width:768px){.md\\:flex{display:flex}.md\\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\\:grid-cols-\\[minmax\\(0\\,1fr\\)_minmax\\(260px\\,340px\\)\\]{grid-template-columns:minmax(0,1fr) minmax(260px,340px)}}.\\[\\&\\>\\*\\:not\\(summary\\)\\]\\:pl-4>*:not(summary){padding-left:16px}.\\[\\&\\>span\\]\\:line-clamp-1>span{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.\\[\\&\\>tr\\]\\:last\\:border-b-0:last-child>tr{border-bottom-width:0px}.\\[\\&\\[data-state\\=open\\]\\>span\\[data-chevron\\]\\]\\:rotate-180[data-state=open]>span[data-chevron]{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-danger [data-icon]{--tw-text-opacity: 1;color:hsl(var(--danger) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-info [data-icon]{--tw-text-opacity: 1;color:hsl(var(--info) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-success [data-icon]{--tw-text-opacity: 1;color:hsl(var(--success) / var(--tw-text-opacity, 1))}.group.toaster .group-\\[\\.toaster\\]\\:\\[\\&_\\[data-icon\\]\\]\\:text-warning [data-icon]{--tw-text-opacity: 1;color:hsl(var(--warning) / var(--tw-text-opacity, 1))}.\\[\\&_tr\\]\\:border-b tr{border-bottom-width:1px}.\\[\\&_tr\\]\\:border-line-subtle tr{--tw-border-opacity: 1;border-color:hsl(var(--border-subtle) / var(--tw-border-opacity, 1))}li .\\[li_\\&\\]\\:pl-5{padding-left:20px}`;
|
|
39411
40250
|
const sdkShadowCSS = ".issue-sticker-marker{pointer-events:auto}@keyframes highlight-pulse{0%,to{transform:scale(1);box-shadow:var(--shadow-md)}50%{transform:scale(1.3);box-shadow:var(--shadow-focus),var(--shadow-lg)}}.issue-sticker-marker.highlight-animation{animation:highlight-pulse .7s ease-in-out 3}";
|
|
39412
40251
|
const contentPageCSS = `@layer issue-sticker-mode{html:has(>body.issue-sticker-mode-active),body.issue-sticker-mode-active,body.issue-sticker-mode-active *{cursor:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cpath d='M3 29 C3 22 3 20 3 15 a12 12 0 0 1 12-12 h2 a12 12 0 0 1 12 12 a12 12 0 0 1-12 12 H12 Z' fill='%231C221F' fill-opacity='0.35' stroke='%23ffffff' stroke-width='2' stroke-linejoin='round'/%3E%3C/svg%3E") 3 29,crosshair!important}body.issue-sticker-mode-active iframe{pointer-events:none!important}body.issue-sticker-mode-active .issue-sticker-mode-highlight{outline:2px solid hsl(160 64% 34%)!important;outline-offset:2px!important;background-color:#1f8e691f!important;transition:outline .1s ease,background-color .1s ease!important}}`;
|
|
39413
40252
|
const PAGE_STYLE_ID = "issue-sticker-page-style";
|
|
39414
40253
|
let root = null;
|
|
39415
40254
|
let mountContainer = null;
|
|
40255
|
+
let detachHostGuards = null;
|
|
39416
40256
|
function renderWidget(config) {
|
|
39417
40257
|
if (!root || !mountContainer) {
|
|
39418
40258
|
return;
|
|
@@ -39443,8 +40283,13 @@ function mountWidget(config) {
|
|
|
39443
40283
|
}
|
|
39444
40284
|
document.body.appendChild(hostElement);
|
|
39445
40285
|
const shadow = createShadowRoot(hostElement, [globalsCSS, sdkShadowCSS]);
|
|
39446
|
-
|
|
39447
|
-
|
|
40286
|
+
detachHostGuards == null ? void 0 : detachHostGuards();
|
|
40287
|
+
const guards = [
|
|
40288
|
+
attachHostTypingGuard(shadow.shadowRoot),
|
|
40289
|
+
attachHostPointerGuard(shadow.shadowRoot),
|
|
40290
|
+
attachHostInteractionGuard(shadow.shadowRoot)
|
|
40291
|
+
];
|
|
40292
|
+
detachHostGuards = () => guards.forEach((detach) => detach());
|
|
39448
40293
|
mountContainer = shadow.mountContainer;
|
|
39449
40294
|
mountContainer.style.width = "0";
|
|
39450
40295
|
mountContainer.style.height = "0";
|
|
@@ -39460,6 +40305,8 @@ function unmountWidget() {
|
|
|
39460
40305
|
root = null;
|
|
39461
40306
|
}
|
|
39462
40307
|
mountContainer = null;
|
|
40308
|
+
detachHostGuards == null ? void 0 : detachHostGuards();
|
|
40309
|
+
detachHostGuards = null;
|
|
39463
40310
|
const host = document.getElementById(ISSUE_STICKER_HOST_ELEMENT_ID);
|
|
39464
40311
|
if (host && getIssueStickerRuntimeOwner(host) !== ISSUE_STICKER_RUNTIME_OWNER.extension) {
|
|
39465
40312
|
host.remove();
|