issue-sticker 0.4.0 → 0.5.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 +29 -0
- package/dist/{createLucideIcon-C0Ldhp96.js → createLucideIcon-BzRMP02h.js} +12 -12
- package/dist/{externalUserPayload-BVNl_35M.js → externalUserPayload-zu5IXBjn.js} +437 -182
- package/dist/issue-sticker-headless-react.js +2 -2
- package/dist/issue-sticker-headless.js +124 -15
- package/dist/issue-sticker-react.js +1690 -892
- package/dist/{tooltip-CEc_RGfI.js → tooltip-B6UPO-U8.js} +1 -1
- package/headless.d.ts +27 -2
- package/package.json +3 -2
|
@@ -15,14 +15,14 @@ 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 { A as trimLogBackupPayload, B as createSafeLogBackup, D as getLogBackupSessionId, N as NetworkCollector, C as ConsoleCollector, E as createIndexedDbLogBackup, e as buildJiraCreateFields, P as Popover, F as PopoverTrigger,
|
|
18
|
+
import { A as trimLogBackupPayload, B as createSafeLogBackup, D as getLogBackupSessionId, N as NetworkCollector, C as ConsoleCollector, E as createIndexedDbLogBackup, e as buildJiraCreateFields, P as Popover, F as PopoverTrigger, G as Input, H as PopoverContent, L as Label, J as Select, K as SelectTrigger, M as SelectValue, O as SelectContent, Q as SelectItem, R as getJiraMetadata, T as buildJiraIssueFields, U as Button, V as Root2$2, W as Trigger$2, X as ChevronDown, Y as Portal, Z as Content2$1, _ as Check, $ as X$1, a0 as DatePicker, f as buildNotionCreateFields, a1 as getNotionPageMetadata, a2 as buildNotionFieldViews, c as captureWithModernScreenshot, p as pristineFetch, d as directUploadToSignedUrl, a3 as DirectUploadError, a4 as toast, a5 as getSnapshot, a6 as subscribe, a as uploadSignedMediaWithApi, v as sanitizeString, a7 as getRegisteredUploadId, h as buildGuestOwnerQuery, j as devLogger, w as getCurrentPageUrl, a8 as ChevronRight, a9 as ReactRemoveScroll, aa as hideOthers, ab as useFocusGuards, ac as FocusScope, ad as getExposedJiraFields, ae as isJiraFieldExposed, n as normalizeExposedJiraFields, y as preloadModernScreenshot, u as uploadScreenshotDataUrls, k as filterExcludeServiceRelatedLogs, l as filterExcludeServiceRelatedConsoleLogs, s as selectNetworkLogsForSubmit, m as compactNetworkLogsForTransport, o as CONSOLE_LOG_SUBMIT_LIMIT, g as getElementPosition, q as buildExternalUserPayload, r as shrinkNetworkLogsToFit, t as measureJsonBytes, i as buildIssueIntegrationsPayload, x as SessionReplayCaptureController, af as getPrimaryJiraMapping, ag as createCollection, ah as useDirection, ai as getPrimaryNotionMapping, aj as buttonVariants, ak as findElementBySelector, al as findElementByXPath, S as ScreenRecordingController, am as OnboardingProvider, an as useOnboardingTarget, ao as useOnboardingFlow, ap as ISSUE_STICKER_HOST_ELEMENT_ID, aq as getIssueStickerRuntimeOwner, ar as ISSUE_STICKER_RUNTIME_OWNER, z as isIssueStickerExtensionRuntimeActive, as as createRuntimeHostElement, at as attachHostTypingGuard, au as ISSUE_STICKER_EXTENSION_RUNTIME_READY_EVENT } from "./externalUserPayload-zu5IXBjn.js";
|
|
19
19
|
import * as React from "react";
|
|
20
20
|
import { useRef, useState, useEffect, useId, useLayoutEffect, useCallback, forwardRef, createElement, createContext, useContext, Component, Suspense, useInsertionEffect, useMemo, Children, isValidElement, Fragment as Fragment$1, useSyncExternalStore } from "react";
|
|
21
21
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
22
|
-
import { c as createLucideIcon$1, b as usePortalContainer, a as cn$1, O as OVERLAY_LAYER, s as stopEscapePropagation, d as OVERLAY_LAYER_INDEX,
|
|
22
|
+
import { c as createLucideIcon$1, b as usePortalContainer, a as cn$1, O as OVERLAY_LAYER, s as stopEscapePropagation, d as OVERLAY_LAYER_INDEX, P as Presence, e as Primitive, f as composeEventHandlers, g as useDismissableLayerSurface, h as useComposedRefs$1, i as Portal$1, j as createContextScope, k as createSlot, D as DismissableLayer, l as useControllableState, m as useId$1, n as useLayoutEffect2, o as useCallbackRef, p as PortalContainerProvider } from "./createLucideIcon-BzRMP02h.js";
|
|
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-
|
|
25
|
+
import { T as TooltipProvider, a as Tooltip, b as TooltipTrigger, c as TooltipContent } from "./tooltip-B6UPO-U8.js";
|
|
26
26
|
function readReleaseMarker() {
|
|
27
27
|
try {
|
|
28
28
|
const g = globalThis.__ISSUE_STICKER_RELEASE__;
|
|
@@ -211,6 +211,15 @@ function buildAdminIssueUrl(issueId, options) {
|
|
|
211
211
|
if (!adminBaseUrl) return null;
|
|
212
212
|
return new URL(`/issues/${encodeURIComponent(normalizedIssueId)}`, adminBaseUrl).toString();
|
|
213
213
|
}
|
|
214
|
+
function buildAdminProjectTitlePatternUrl(projectId, options) {
|
|
215
|
+
const normalizedProjectId = projectId.trim();
|
|
216
|
+
if (!normalizedProjectId) return null;
|
|
217
|
+
const adminBaseUrl = resolveAdminBaseUrl(options);
|
|
218
|
+
if (!adminBaseUrl) return null;
|
|
219
|
+
const url = new URL(`/projects/${encodeURIComponent(normalizedProjectId)}`, adminBaseUrl);
|
|
220
|
+
url.searchParams.set("tab", "title-pattern");
|
|
221
|
+
return url.toString();
|
|
222
|
+
}
|
|
214
223
|
const FAILED_ACTION_TEXT = {
|
|
215
224
|
jira: "Jira 이슈를 생성하지 못했습니다.",
|
|
216
225
|
notion: "Notion 페이지를 생성하지 못했습니다.",
|
|
@@ -491,6 +500,16 @@ function AsyncSearchSelect({
|
|
|
491
500
|
}
|
|
492
501
|
);
|
|
493
502
|
}
|
|
503
|
+
/**
|
|
504
|
+
* @license lucide-react v0.468.0 - ISC
|
|
505
|
+
*
|
|
506
|
+
* This source code is licensed under the ISC license.
|
|
507
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
508
|
+
*/
|
|
509
|
+
const ArrowUp = createLucideIcon$1("ArrowUp", [
|
|
510
|
+
["path", { d: "m5 12 7-7 7 7", key: "hav0vg" }],
|
|
511
|
+
["path", { d: "M12 19V5", key: "x0mq9r" }]
|
|
512
|
+
]);
|
|
494
513
|
/**
|
|
495
514
|
* @license lucide-react v0.468.0 - ISC
|
|
496
515
|
*
|
|
@@ -684,6 +703,22 @@ const MapPin = createLucideIcon$1("MapPin", [
|
|
|
684
703
|
],
|
|
685
704
|
["circle", { cx: "12", cy: "10", r: "3", key: "ilqhr7" }]
|
|
686
705
|
]);
|
|
706
|
+
/**
|
|
707
|
+
* @license lucide-react v0.468.0 - ISC
|
|
708
|
+
*
|
|
709
|
+
* This source code is licensed under the ISC license.
|
|
710
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
711
|
+
*/
|
|
712
|
+
const PenLine = createLucideIcon$1("PenLine", [
|
|
713
|
+
["path", { d: "M12 20h9", key: "t2du7b" }],
|
|
714
|
+
[
|
|
715
|
+
"path",
|
|
716
|
+
{
|
|
717
|
+
d: "M16.376 3.622a1 1 0 0 1 3.002 3.002L7.368 18.635a2 2 0 0 1-.855.506l-2.872.838a.5.5 0 0 1-.62-.62l.838-2.872a2 2 0 0 1 .506-.854z",
|
|
718
|
+
key: "1ykcvy"
|
|
719
|
+
}
|
|
720
|
+
]
|
|
721
|
+
]);
|
|
687
722
|
/**
|
|
688
723
|
* @license lucide-react v0.468.0 - ISC
|
|
689
724
|
*
|
|
@@ -696,6 +731,25 @@ const RefreshCw = createLucideIcon$1("RefreshCw", [
|
|
|
696
731
|
["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
|
|
697
732
|
["path", { d: "M8 16H3v5", key: "1cv678" }]
|
|
698
733
|
]);
|
|
734
|
+
/**
|
|
735
|
+
* @license lucide-react v0.468.0 - ISC
|
|
736
|
+
*
|
|
737
|
+
* This source code is licensed under the ISC license.
|
|
738
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
739
|
+
*/
|
|
740
|
+
const Sparkles = createLucideIcon$1("Sparkles", [
|
|
741
|
+
[
|
|
742
|
+
"path",
|
|
743
|
+
{
|
|
744
|
+
d: "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",
|
|
745
|
+
key: "4pj2yx"
|
|
746
|
+
}
|
|
747
|
+
],
|
|
748
|
+
["path", { d: "M20 3v4", key: "1olli1" }],
|
|
749
|
+
["path", { d: "M22 5h-4", key: "1gvqau" }],
|
|
750
|
+
["path", { d: "M4 17v2", key: "vumght" }],
|
|
751
|
+
["path", { d: "M5 18H3", key: "zchphs" }]
|
|
752
|
+
]);
|
|
699
753
|
/**
|
|
700
754
|
* @license lucide-react v0.468.0 - ISC
|
|
701
755
|
*
|
|
@@ -735,6 +789,28 @@ const TriangleAlert = createLucideIcon$1("TriangleAlert", [
|
|
|
735
789
|
["path", { d: "M12 9v4", key: "juzpu7" }],
|
|
736
790
|
["path", { d: "M12 17h.01", key: "p32p05" }]
|
|
737
791
|
]);
|
|
792
|
+
/**
|
|
793
|
+
* @license lucide-react v0.468.0 - ISC
|
|
794
|
+
*
|
|
795
|
+
* This source code is licensed under the ISC license.
|
|
796
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
797
|
+
*/
|
|
798
|
+
const WandSparkles = createLucideIcon$1("WandSparkles", [
|
|
799
|
+
[
|
|
800
|
+
"path",
|
|
801
|
+
{
|
|
802
|
+
d: "m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72",
|
|
803
|
+
key: "ul74o6"
|
|
804
|
+
}
|
|
805
|
+
],
|
|
806
|
+
["path", { d: "m14 7 3 3", key: "1r5n42" }],
|
|
807
|
+
["path", { d: "M5 6v4", key: "ilb8ba" }],
|
|
808
|
+
["path", { d: "M19 14v4", key: "blhpug" }],
|
|
809
|
+
["path", { d: "M10 2v2", key: "7u0qdc" }],
|
|
810
|
+
["path", { d: "M7 8H3", key: "zfb6yr" }],
|
|
811
|
+
["path", { d: "M21 16h-4", key: "1cnmox" }],
|
|
812
|
+
["path", { d: "M11 3H9", key: "1obp7u" }]
|
|
813
|
+
]);
|
|
738
814
|
const EMPTY_SELECT_VALUE$1 = "__ISSUE_STICKER_EMPTY__";
|
|
739
815
|
const JIRA_REQUIRED_FIELD_IDS = /* @__PURE__ */ new Set(["priority"]);
|
|
740
816
|
function JiraReadonlyField({ field }) {
|
|
@@ -1816,9 +1892,9 @@ var FocusManager = (_b = class extends Subscribable {
|
|
|
1816
1892
|
}
|
|
1817
1893
|
}
|
|
1818
1894
|
onFocus() {
|
|
1819
|
-
const
|
|
1895
|
+
const isFocused2 = this.isFocused();
|
|
1820
1896
|
this.listeners.forEach((listener) => {
|
|
1821
|
-
listener(
|
|
1897
|
+
listener(isFocused2);
|
|
1822
1898
|
});
|
|
1823
1899
|
}
|
|
1824
1900
|
isFocused() {
|
|
@@ -4537,9 +4613,10 @@ function useMutation(options, queryClient2) {
|
|
|
4537
4613
|
return { ...result, mutate, mutateAsync: result.mutate };
|
|
4538
4614
|
}
|
|
4539
4615
|
const BODY_SCROLLBAR_WIDTH_CSS_VAR = "--issue-sticker-scrollbar-width";
|
|
4540
|
-
const useIsomorphicLayoutEffect$
|
|
4541
|
-
function useDisableBodyScroll() {
|
|
4542
|
-
useIsomorphicLayoutEffect$
|
|
4616
|
+
const useIsomorphicLayoutEffect$2 = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
4617
|
+
function useDisableBodyScroll(enabled = true) {
|
|
4618
|
+
useIsomorphicLayoutEffect$2(() => {
|
|
4619
|
+
if (!enabled) return;
|
|
4543
4620
|
const body = document.body;
|
|
4544
4621
|
const root2 = document.documentElement;
|
|
4545
4622
|
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
@@ -4560,7 +4637,7 @@ function useDisableBodyScroll() {
|
|
|
4560
4637
|
root2.style.removeProperty(BODY_SCROLLBAR_WIDTH_CSS_VAR);
|
|
4561
4638
|
}
|
|
4562
4639
|
};
|
|
4563
|
-
}, []);
|
|
4640
|
+
}, [enabled]);
|
|
4564
4641
|
}
|
|
4565
4642
|
function useGuardedCallback(callback) {
|
|
4566
4643
|
const guardRef = useRef(false);
|
|
@@ -5904,355 +5981,22 @@ function DefaultErrorFallback({ resetErrorBoundary }) {
|
|
|
5904
5981
|
/* @__PURE__ */ jsx(Button, { variant: "outline", className: "mt-3", onClick: resetErrorBoundary, children: "다시 시도" })
|
|
5905
5982
|
] });
|
|
5906
5983
|
}
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
{
|
|
5914
|
-
post: (endpoint, body) => apiClient.post(endpoint, body),
|
|
5915
|
-
directUpload
|
|
5916
|
-
},
|
|
5917
|
-
file,
|
|
5918
|
-
mediaType,
|
|
5919
|
-
{ issueId }
|
|
5920
|
-
);
|
|
5921
|
-
}
|
|
5922
|
-
function issuesEndpoint() {
|
|
5923
|
-
return "/api/sdk/issues";
|
|
5924
|
-
}
|
|
5925
|
-
function guestOwnerQuery() {
|
|
5926
|
-
var _a;
|
|
5927
|
-
const mode = getSdkMode();
|
|
5928
|
-
if (mode !== "public") return "";
|
|
5929
|
-
return buildGuestOwnerQuery(mode, (_a = SDKManager.getInstance().config.user) == null ? void 0 : _a.id);
|
|
5930
|
-
}
|
|
5931
|
-
let issuesCache = null;
|
|
5932
|
-
const CACHE_DURATION_MS = 30 * 1e3;
|
|
5933
|
-
const STICKER_PAGE_SIZE = 500;
|
|
5934
|
-
const STICKER_FETCH_LIMIT = 2e3;
|
|
5935
|
-
function getFilterKey(filters) {
|
|
5936
|
-
var _a;
|
|
5937
|
-
const normalizeArray = (value) => {
|
|
5938
|
-
if (value === "all" || !value) return "all";
|
|
5939
|
-
if (Array.isArray(value)) {
|
|
5940
|
-
return value.length === 0 ? "all" : [...value].sort().join(",");
|
|
5941
|
-
}
|
|
5942
|
-
return String(value);
|
|
5943
|
-
};
|
|
5944
|
-
const normalizeRecord = (record) => {
|
|
5945
|
-
if (!record) return "";
|
|
5946
|
-
const entries = Object.entries(record).map(([k, v]) => [k, [...v].sort()]).sort(([a], [b]) => a.localeCompare(b));
|
|
5947
|
-
return JSON.stringify(entries);
|
|
5948
|
-
};
|
|
5949
|
-
return JSON.stringify({
|
|
5950
|
-
sdkMode: getSdkMode() || "public",
|
|
5951
|
-
url: normalizeArray(filters.url),
|
|
5952
|
-
projectId: filters.projectId || "all",
|
|
5953
|
-
externalUserId: filters.externalUserId || "",
|
|
5954
|
-
search: ((_a = filters.search) == null ? void 0 : _a.trim()) || "",
|
|
5955
|
-
dateFrom: filters.dateFrom || "",
|
|
5956
|
-
dateTo: filters.dateTo || "",
|
|
5957
|
-
page: filters.page ?? 0,
|
|
5958
|
-
pageSize: filters.pageSize ?? 0,
|
|
5959
|
-
custom: normalizeRecord(filters.customFieldValues),
|
|
5960
|
-
jira: normalizeRecord(filters.jiraFieldValues),
|
|
5961
|
-
notion: normalizeRecord(filters.notionFieldValues),
|
|
5962
|
-
sortOrder: filters.sortOrder || ""
|
|
5984
|
+
let reloadToastId = null;
|
|
5985
|
+
function showReloadToast() {
|
|
5986
|
+
if (reloadToastId && getSnapshot().some((t) => t.id === reloadToastId)) return;
|
|
5987
|
+
reloadToastId = toast.error("이슈 정보를 불러오지 못했습니다. 페이지를 새로고침해주세요.", {
|
|
5988
|
+
// 사용자가 새로고침하거나 닫을 때까지 남긴다.
|
|
5989
|
+
duration: 0,
|
|
5990
|
+
action: { label: "새로고침", onClick: () => window.location.reload() }
|
|
5963
5991
|
});
|
|
5964
5992
|
}
|
|
5965
|
-
function
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
function recordToFilterArray(record, keyFieldName) {
|
|
5969
|
-
if (!record) return null;
|
|
5970
|
-
const entries = Object.entries(record).filter(([, values]) => values.length > 0);
|
|
5971
|
-
if (entries.length === 0) return null;
|
|
5972
|
-
return entries.map(
|
|
5973
|
-
([key, values]) => ({ [keyFieldName]: key, values })
|
|
5974
|
-
);
|
|
5975
|
-
}
|
|
5976
|
-
function buildFilterQueryParams(filters, options = {}) {
|
|
5977
|
-
var _a;
|
|
5978
|
-
const params = new URLSearchParams();
|
|
5979
|
-
if (filters.projectId) {
|
|
5980
|
-
params.set("project_id", filters.projectId);
|
|
5981
|
-
}
|
|
5982
|
-
if (filters.url && filters.url !== "all") {
|
|
5983
|
-
if (Array.isArray(filters.url) && filters.url.length > 0) {
|
|
5984
|
-
params.set("url", filters.url.join(","));
|
|
5985
|
-
} else if (typeof filters.url === "string") {
|
|
5986
|
-
params.set("url", filters.url);
|
|
5987
|
-
}
|
|
5988
|
-
}
|
|
5989
|
-
if (options.includeListFilters) {
|
|
5990
|
-
const search2 = (_a = filters.search) == null ? void 0 : _a.trim();
|
|
5991
|
-
if (search2) {
|
|
5992
|
-
params.set("search", search2);
|
|
5993
|
-
}
|
|
5994
|
-
if (filters.dateFrom) {
|
|
5995
|
-
params.set("date_from", filters.dateFrom);
|
|
5996
|
-
}
|
|
5997
|
-
if (filters.dateTo) {
|
|
5998
|
-
params.set("date_to", filters.dateTo);
|
|
5999
|
-
}
|
|
6000
|
-
const customArr = recordToFilterArray(filters.customFieldValues, "fieldDefinitionId");
|
|
6001
|
-
if (customArr) {
|
|
6002
|
-
params.set("custom_field_filters", JSON.stringify(customArr));
|
|
6003
|
-
}
|
|
6004
|
-
const jiraArr = recordToFilterArray(filters.jiraFieldValues, "fieldName");
|
|
6005
|
-
if (jiraArr) {
|
|
6006
|
-
params.set("jira_field_filters", JSON.stringify(jiraArr));
|
|
6007
|
-
}
|
|
6008
|
-
const notionArr = recordToFilterArray(filters.notionFieldValues, "fieldName");
|
|
6009
|
-
if (notionArr) {
|
|
6010
|
-
params.set("notion_field_filters", JSON.stringify(notionArr));
|
|
6011
|
-
}
|
|
6012
|
-
}
|
|
6013
|
-
if (filters.externalUserId) {
|
|
6014
|
-
params.set("external_user_id", filters.externalUserId);
|
|
6015
|
-
}
|
|
6016
|
-
if (typeof filters.page === "number" && filters.page > 0) {
|
|
6017
|
-
params.set("page", String(filters.page));
|
|
6018
|
-
}
|
|
6019
|
-
if (typeof filters.pageSize === "number" && filters.pageSize > 0) {
|
|
6020
|
-
params.set("page_size", String(filters.pageSize));
|
|
6021
|
-
}
|
|
6022
|
-
if (filters.sortOrder) {
|
|
6023
|
-
params.set("sort_order", filters.sortOrder);
|
|
6024
|
-
}
|
|
6025
|
-
const queryString = params.toString();
|
|
6026
|
-
return queryString ? `?${queryString}` : "";
|
|
6027
|
-
}
|
|
6028
|
-
const issueService = {
|
|
6029
|
-
async createIssue(issueData) {
|
|
6030
|
-
const sanitizedData = sanitizeString(issueData);
|
|
6031
|
-
const screenshotUploadIds = (sanitizedData.screenshot || []).map((url) => getRegisteredUploadId(url, "screenshot")).filter((id2) => typeof id2 === "string" && id2.length > 0);
|
|
6032
|
-
const videoUploadId = sanitizedData.video ? getRegisteredUploadId(sanitizedData.video, "video") : void 0;
|
|
6033
|
-
if (screenshotUploadIds.length > 0) {
|
|
6034
|
-
sanitizedData.screenshot_upload_ids = screenshotUploadIds;
|
|
6035
|
-
}
|
|
6036
|
-
if (videoUploadId) {
|
|
6037
|
-
sanitizedData.video_upload_id = videoUploadId;
|
|
6038
|
-
}
|
|
6039
|
-
const issue = await apiClient.post(issuesEndpoint(), sanitizedData);
|
|
6040
|
-
invalidateIssuesCache();
|
|
6041
|
-
return issue;
|
|
6042
|
-
},
|
|
6043
|
-
async getIssues(filters = {}, options = {}) {
|
|
6044
|
-
if (!filters.projectId) {
|
|
6045
|
-
throw new Error("프로젝트 ID가 필요합니다. 로그인 상태를 확인해주세요.");
|
|
6046
|
-
}
|
|
6047
|
-
const filterKey = getFilterKey(filters);
|
|
6048
|
-
const now2 = Date.now();
|
|
6049
|
-
if (!options.forceRefresh && issuesCache && issuesCache.filterKey === filterKey && now2 - issuesCache.timestamp < CACHE_DURATION_MS) {
|
|
6050
|
-
return issuesCache.data;
|
|
6051
|
-
}
|
|
6052
|
-
const queryParams = buildFilterQueryParams(filters, { includeListFilters: true });
|
|
6053
|
-
const { items: issues = [] } = await apiClient.get(
|
|
6054
|
-
`${issuesEndpoint()}${queryParams}`
|
|
6055
|
-
);
|
|
6056
|
-
issuesCache = {
|
|
6057
|
-
data: issues,
|
|
6058
|
-
timestamp: now2,
|
|
6059
|
-
filterKey
|
|
6060
|
-
};
|
|
6061
|
-
return issues;
|
|
6062
|
-
},
|
|
6063
|
-
async getIssueStickers(filters = {}, options = {}) {
|
|
6064
|
-
var _a, _b2;
|
|
6065
|
-
if (!filters.projectId) {
|
|
6066
|
-
throw new Error("프로젝트 ID가 필요합니다. 로그인 상태를 확인해주세요.");
|
|
6067
|
-
}
|
|
6068
|
-
const filterKey = `${getFilterKey(filters)}:stickers`;
|
|
6069
|
-
const now2 = Date.now();
|
|
6070
|
-
if (!options.forceRefresh && issuesCache && issuesCache.filterKey === filterKey && now2 - issuesCache.timestamp < CACHE_DURATION_MS) {
|
|
6071
|
-
return issuesCache.data;
|
|
6072
|
-
}
|
|
6073
|
-
const issues = [];
|
|
6074
|
-
let page = 1;
|
|
6075
|
-
let hasMore = true;
|
|
6076
|
-
while (hasMore && issues.length <= STICKER_FETCH_LIMIT) {
|
|
6077
|
-
const response = await apiClient.getResponse(
|
|
6078
|
-
`${issuesEndpoint()}/stickers${buildFilterQueryParams({
|
|
6079
|
-
...filters,
|
|
6080
|
-
page,
|
|
6081
|
-
pageSize: STICKER_PAGE_SIZE
|
|
6082
|
-
})}`
|
|
6083
|
-
);
|
|
6084
|
-
const pageItems = ((_a = response.data) == null ? void 0 : _a.items) ?? [];
|
|
6085
|
-
issues.push(...pageItems);
|
|
6086
|
-
hasMore = Boolean((_b2 = response.data) == null ? void 0 : _b2.hasMore) && pageItems.length > 0;
|
|
6087
|
-
page += 1;
|
|
6088
|
-
}
|
|
6089
|
-
issuesCache = {
|
|
6090
|
-
data: issues,
|
|
6091
|
-
timestamp: now2,
|
|
6092
|
-
filterKey
|
|
6093
|
-
};
|
|
6094
|
-
return issues;
|
|
6095
|
-
},
|
|
6096
|
-
invalidateCache() {
|
|
6097
|
-
invalidateIssuesCache();
|
|
6098
|
-
},
|
|
6099
|
-
// 계약 정본: 백엔드 GET /api/sdk/issues/:id 는 back#101부터 읽기-시점-스크럽된
|
|
6100
|
-
// llm_prompt 를 상세 응답에 포함한다(목록 응답에는 없음) — 스코프를 이 반환 타입에만 한정한다.
|
|
6101
|
-
async getIssue(id2) {
|
|
6102
|
-
return apiClient.get(
|
|
6103
|
-
`${issuesEndpoint()}/${id2}${guestOwnerQuery()}`
|
|
6104
|
-
);
|
|
6105
|
-
},
|
|
6106
|
-
async updateIssue(id2, updates) {
|
|
6107
|
-
const sanitizedUpdates = sanitizeString(updates);
|
|
6108
|
-
const issue = await apiClient.put(
|
|
6109
|
-
`${issuesEndpoint()}/${id2}${guestOwnerQuery()}`,
|
|
6110
|
-
sanitizedUpdates
|
|
6111
|
-
);
|
|
6112
|
-
invalidateIssuesCache();
|
|
6113
|
-
return issue;
|
|
6114
|
-
},
|
|
6115
|
-
async deleteIssue(id2, options) {
|
|
6116
|
-
const endpoint = `${issuesEndpoint()}/${id2}${guestOwnerQuery()}`;
|
|
6117
|
-
if (options == null ? void 0 : options.deleteExternal) {
|
|
6118
|
-
await apiClient.delete(endpoint, {
|
|
6119
|
-
body: { deleteExternal: true }
|
|
6120
|
-
});
|
|
6121
|
-
} else {
|
|
6122
|
-
await apiClient.delete(endpoint);
|
|
6123
|
-
}
|
|
6124
|
-
invalidateIssuesCache();
|
|
6125
|
-
}
|
|
6126
|
-
};
|
|
6127
|
-
async function loadIssuesFromServer(url, projectId, externalUserId, options = {}) {
|
|
6128
|
-
if (!projectId) return [];
|
|
6129
|
-
try {
|
|
6130
|
-
return await issueService.getIssueStickers(
|
|
6131
|
-
{
|
|
6132
|
-
url,
|
|
6133
|
-
projectId,
|
|
6134
|
-
...externalUserId ? { externalUserId } : {}
|
|
6135
|
-
},
|
|
6136
|
-
options
|
|
6137
|
-
) || [];
|
|
6138
|
-
} catch (error) {
|
|
6139
|
-
devLogger.error("useIssues", "Failed to load issues:", error);
|
|
6140
|
-
return [];
|
|
6141
|
-
}
|
|
6142
|
-
}
|
|
6143
|
-
const contentProjectIdQueryKey = ["contentProjectId"];
|
|
6144
|
-
const contentIssuesQueryRoot = ["contentIssues"];
|
|
6145
|
-
function contentIssuesQueryKey({
|
|
6146
|
-
currentUrl,
|
|
6147
|
-
projectId,
|
|
6148
|
-
isMember,
|
|
6149
|
-
externalUserId
|
|
6150
|
-
}) {
|
|
6151
|
-
return [...contentIssuesQueryRoot, currentUrl, projectId, isMember, externalUserId];
|
|
6152
|
-
}
|
|
6153
|
-
function fetchContentIssues({
|
|
6154
|
-
currentUrl,
|
|
6155
|
-
projectId,
|
|
6156
|
-
isMember,
|
|
6157
|
-
externalUserId,
|
|
6158
|
-
forceRefresh
|
|
6159
|
-
}) {
|
|
6160
|
-
return loadIssuesFromServer(currentUrl, projectId, isMember ? void 0 : externalUserId, {
|
|
6161
|
-
forceRefresh
|
|
6162
|
-
});
|
|
6163
|
-
}
|
|
6164
|
-
function useIssues(currentUrl) {
|
|
6165
|
-
var _a;
|
|
6166
|
-
const { isMember } = useAuthContext();
|
|
6167
|
-
const config = SDKManager.getInstance().config;
|
|
6168
|
-
const sessionIssuesRef = useRef([]);
|
|
6169
|
-
const projectIdQuery = useSuspenseQuery({
|
|
6170
|
-
queryKey: contentProjectIdQueryKey,
|
|
6171
|
-
queryFn: async () => {
|
|
6172
|
-
const accountInfo = await getAccountInfo();
|
|
6173
|
-
return (accountInfo == null ? void 0 : accountInfo.projectId) ?? null;
|
|
6174
|
-
}
|
|
6175
|
-
});
|
|
6176
|
-
const projectId = projectIdQuery.data;
|
|
6177
|
-
const { data: serverIssues = [] } = useSuspenseQuery({
|
|
6178
|
-
queryKey: contentIssuesQueryKey({
|
|
6179
|
-
currentUrl,
|
|
6180
|
-
projectId,
|
|
6181
|
-
isMember,
|
|
6182
|
-
externalUserId: (_a = config.user) == null ? void 0 : _a.id
|
|
6183
|
-
}),
|
|
6184
|
-
queryFn: () => {
|
|
6185
|
-
var _a2;
|
|
6186
|
-
return fetchContentIssues({
|
|
6187
|
-
currentUrl,
|
|
6188
|
-
projectId,
|
|
6189
|
-
isMember,
|
|
6190
|
-
externalUserId: (_a2 = config.user) == null ? void 0 : _a2.id
|
|
6191
|
-
});
|
|
6192
|
-
}
|
|
6193
|
-
});
|
|
6194
|
-
const issues = !isMember && !config.user ? [...serverIssues, ...sessionIssuesRef.current] : serverIssues;
|
|
6195
|
-
const addSessionIssue = useCallback((issue) => {
|
|
6196
|
-
sessionIssuesRef.current = [...sessionIssuesRef.current, issue];
|
|
6197
|
-
}, []);
|
|
6198
|
-
const loadIssues = useCallback(async () => {
|
|
6199
|
-
issueService.invalidateCache();
|
|
6200
|
-
await Promise.all([
|
|
6201
|
-
queryClient.invalidateQueries({ queryKey: contentIssuesQueryRoot }),
|
|
6202
|
-
queryClient.invalidateQueries({ queryKey: contentProjectIdQueryKey })
|
|
6203
|
-
]);
|
|
6204
|
-
}, []);
|
|
6205
|
-
return {
|
|
6206
|
-
issues,
|
|
6207
|
-
loadIssues,
|
|
6208
|
-
addSessionIssue
|
|
6209
|
-
};
|
|
6210
|
-
}
|
|
6211
|
-
function readCurrentUrl() {
|
|
6212
|
-
return getCurrentPageUrl();
|
|
5993
|
+
function OverlayErrorFallback() {
|
|
5994
|
+
useEffect(showReloadToast, []);
|
|
5995
|
+
return null;
|
|
6213
5996
|
}
|
|
6214
|
-
function
|
|
6215
|
-
const
|
|
6216
|
-
|
|
6217
|
-
useEffect(() => {
|
|
6218
|
-
var _a;
|
|
6219
|
-
if (isLoading || !isAuthenticated || !projectId) {
|
|
6220
|
-
return;
|
|
6221
|
-
}
|
|
6222
|
-
const currentUrl = readCurrentUrl();
|
|
6223
|
-
const externalUserId = (_a = SDKManager.getInstance().config.user) == null ? void 0 : _a.id;
|
|
6224
|
-
const fetchKey = [
|
|
6225
|
-
currentUrl,
|
|
6226
|
-
projectId,
|
|
6227
|
-
isMember ? "member" : "public",
|
|
6228
|
-
accountId ?? "",
|
|
6229
|
-
token ?? "",
|
|
6230
|
-
externalUserId ?? ""
|
|
6231
|
-
].join("|");
|
|
6232
|
-
if (lastFetchKeyRef.current === fetchKey) {
|
|
6233
|
-
return;
|
|
6234
|
-
}
|
|
6235
|
-
lastFetchKeyRef.current = fetchKey;
|
|
6236
|
-
queryClient.setQueryData(contentProjectIdQueryKey, projectId);
|
|
6237
|
-
void queryClient.fetchQuery({
|
|
6238
|
-
queryKey: contentIssuesQueryKey({
|
|
6239
|
-
currentUrl,
|
|
6240
|
-
projectId,
|
|
6241
|
-
isMember,
|
|
6242
|
-
externalUserId
|
|
6243
|
-
}),
|
|
6244
|
-
queryFn: () => fetchContentIssues({
|
|
6245
|
-
currentUrl,
|
|
6246
|
-
projectId,
|
|
6247
|
-
isMember,
|
|
6248
|
-
externalUserId,
|
|
6249
|
-
forceRefresh: true
|
|
6250
|
-
}),
|
|
6251
|
-
staleTime: 0
|
|
6252
|
-
}).catch((error) => {
|
|
6253
|
-
devLogger.error("usePrefetchContentIssues", "Failed to prefetch issues:", error);
|
|
6254
|
-
});
|
|
6255
|
-
}, [accountId, isAuthenticated, isLoading, isMember, projectId, token]);
|
|
5997
|
+
function QueryBoundary({ children, onError, loadingFallback }) {
|
|
5998
|
+
const ErrorFallback = onError ?? DefaultErrorFallback;
|
|
5999
|
+
return /* @__PURE__ */ jsx(QueryErrorResetBoundary, { children: ({ reset }) => /* @__PURE__ */ jsx(m, { FallbackComponent: ErrorFallback, onReset: reset, children: /* @__PURE__ */ jsx(Suspense, { fallback: loadingFallback ?? /* @__PURE__ */ jsx(Skeleton, {}), children }) }) });
|
|
6256
6000
|
}
|
|
6257
6001
|
const LayoutGroupContext = createContext({});
|
|
6258
6002
|
function useConstant(init) {
|
|
@@ -6263,7 +6007,7 @@ function useConstant(init) {
|
|
|
6263
6007
|
return ref.current;
|
|
6264
6008
|
}
|
|
6265
6009
|
const isBrowser$1 = typeof window !== "undefined";
|
|
6266
|
-
const useIsomorphicLayoutEffect = isBrowser$1 ? useLayoutEffect : useEffect;
|
|
6010
|
+
const useIsomorphicLayoutEffect$1 = isBrowser$1 ? useLayoutEffect : useEffect;
|
|
6267
6011
|
const PresenceContext = /* @__PURE__ */ createContext(null);
|
|
6268
6012
|
function addUniqueItem(arr, item) {
|
|
6269
6013
|
if (arr.indexOf(item) === -1)
|
|
@@ -12530,7 +12274,7 @@ const PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, p
|
|
|
12530
12274
|
const id2 = useId();
|
|
12531
12275
|
const isPresentRef = useRef(isPresent);
|
|
12532
12276
|
const onExitCompleteRef = useRef(onExitComplete);
|
|
12533
|
-
useIsomorphicLayoutEffect(() => {
|
|
12277
|
+
useIsomorphicLayoutEffect$1(() => {
|
|
12534
12278
|
isPresentRef.current = isPresent;
|
|
12535
12279
|
onExitCompleteRef.current = onExitComplete;
|
|
12536
12280
|
});
|
|
@@ -12608,7 +12352,7 @@ const AnimatePresence = ({ children, custom, initial = true, onExitComplete, pre
|
|
|
12608
12352
|
const exitingComponents = useRef(/* @__PURE__ */ new Set());
|
|
12609
12353
|
const [diffedChildren, setDiffedChildren] = useState(presentChildren);
|
|
12610
12354
|
const [renderedChildren, setRenderedChildren] = useState(presentChildren);
|
|
12611
|
-
useIsomorphicLayoutEffect(() => {
|
|
12355
|
+
useIsomorphicLayoutEffect$1(() => {
|
|
12612
12356
|
isInitialRender.current = false;
|
|
12613
12357
|
pendingPresentChildren.current = presentChildren;
|
|
12614
12358
|
for (let i = 0; i < renderedChildren.length; i++) {
|
|
@@ -13061,7 +12805,7 @@ function useVisualElement(Component2, visualState, props, createVisualElement, P
|
|
|
13061
12805
|
});
|
|
13062
12806
|
const optimisedAppearId = props[optimizedAppearDataAttribute];
|
|
13063
12807
|
const wantsHandoff = useRef(Boolean(optimisedAppearId) && typeof window !== "undefined" && !((_a = window.MotionHandoffIsComplete) == null ? void 0 : _a.call(window, optimisedAppearId)) && ((_b2 = window.MotionHasOptimisedAnimation) == null ? void 0 : _b2.call(window, optimisedAppearId)));
|
|
13064
|
-
useIsomorphicLayoutEffect(() => {
|
|
12808
|
+
useIsomorphicLayoutEffect$1(() => {
|
|
13065
12809
|
hasMountedOnce.current = true;
|
|
13066
12810
|
if (!visualElement)
|
|
13067
12811
|
return;
|
|
@@ -14391,6 +14135,11 @@ const featureBundle = {
|
|
|
14391
14135
|
...layout
|
|
14392
14136
|
};
|
|
14393
14137
|
const motion$1 = /* @__PURE__ */ createMotionProxy(featureBundle, createDomVisualElement);
|
|
14138
|
+
function useReducedMotion() {
|
|
14139
|
+
!hasReducedMotionListener.current && initPrefersReducedMotion();
|
|
14140
|
+
const [shouldReduceMotion] = useState(prefersReducedMotion.current);
|
|
14141
|
+
return shouldReduceMotion;
|
|
14142
|
+
}
|
|
14394
14143
|
const motion = motion$1;
|
|
14395
14144
|
const TYPE_STYLES = {
|
|
14396
14145
|
default: {
|
|
@@ -14504,6 +14253,407 @@ function ToastContainer({ position: position2 = "top-right" }) {
|
|
|
14504
14253
|
if (typeof document === "undefined") return null;
|
|
14505
14254
|
return createPortal(content2, portalContainer ?? document.body);
|
|
14506
14255
|
}
|
|
14256
|
+
const POLL_INTERVAL_MS = 3e3;
|
|
14257
|
+
const MAX_POLLS = 10;
|
|
14258
|
+
function isGeneratingStatus(status) {
|
|
14259
|
+
return status === "pending" || status === "processing";
|
|
14260
|
+
}
|
|
14261
|
+
function useGenerationPolling({
|
|
14262
|
+
status,
|
|
14263
|
+
onRefetch,
|
|
14264
|
+
maxPolls = MAX_POLLS,
|
|
14265
|
+
intervalMs = POLL_INTERVAL_MS,
|
|
14266
|
+
restartKey
|
|
14267
|
+
}) {
|
|
14268
|
+
const refetchRef = useRef(onRefetch);
|
|
14269
|
+
refetchRef.current = onRefetch;
|
|
14270
|
+
useEffect(() => {
|
|
14271
|
+
if (status !== "pending" && status !== "processing") return;
|
|
14272
|
+
let polls = 0;
|
|
14273
|
+
const timer = setInterval(() => {
|
|
14274
|
+
polls += 1;
|
|
14275
|
+
refetchRef.current();
|
|
14276
|
+
if (polls >= maxPolls) clearInterval(timer);
|
|
14277
|
+
}, intervalMs);
|
|
14278
|
+
return () => clearInterval(timer);
|
|
14279
|
+
}, [status, restartKey, maxPolls, intervalMs]);
|
|
14280
|
+
}
|
|
14281
|
+
function useIssueListGenerationPolling({
|
|
14282
|
+
issues,
|
|
14283
|
+
onRefetch
|
|
14284
|
+
}) {
|
|
14285
|
+
const generatingIds = issues.filter((issue) => isGeneratingStatus(issue.generation_status)).map((issue) => issue.id).sort().join(",");
|
|
14286
|
+
useGenerationPolling({
|
|
14287
|
+
status: generatingIds ? "pending" : null,
|
|
14288
|
+
restartKey: generatingIds,
|
|
14289
|
+
onRefetch
|
|
14290
|
+
});
|
|
14291
|
+
}
|
|
14292
|
+
async function uploadSignedMedia(file, mediaType, issueId) {
|
|
14293
|
+
return uploadSignedMediaWithApi(
|
|
14294
|
+
{
|
|
14295
|
+
post: (endpoint, body) => apiClient.post(endpoint, body),
|
|
14296
|
+
directUpload
|
|
14297
|
+
},
|
|
14298
|
+
file,
|
|
14299
|
+
mediaType,
|
|
14300
|
+
{ issueId }
|
|
14301
|
+
);
|
|
14302
|
+
}
|
|
14303
|
+
function issuesEndpoint() {
|
|
14304
|
+
return "/api/sdk/issues";
|
|
14305
|
+
}
|
|
14306
|
+
function guestOwnerQuery() {
|
|
14307
|
+
var _a;
|
|
14308
|
+
const mode = getSdkMode();
|
|
14309
|
+
if (mode !== "public") return "";
|
|
14310
|
+
return buildGuestOwnerQuery(mode, (_a = SDKManager.getInstance().config.user) == null ? void 0 : _a.id);
|
|
14311
|
+
}
|
|
14312
|
+
let issuesCache = null;
|
|
14313
|
+
const CACHE_DURATION_MS = 30 * 1e3;
|
|
14314
|
+
const STICKER_PAGE_SIZE = 500;
|
|
14315
|
+
const STICKER_FETCH_LIMIT = 2e3;
|
|
14316
|
+
function getFilterKey(filters) {
|
|
14317
|
+
var _a;
|
|
14318
|
+
const normalizeArray = (value) => {
|
|
14319
|
+
if (value === "all" || !value) return "all";
|
|
14320
|
+
if (Array.isArray(value)) {
|
|
14321
|
+
return value.length === 0 ? "all" : [...value].sort().join(",");
|
|
14322
|
+
}
|
|
14323
|
+
return String(value);
|
|
14324
|
+
};
|
|
14325
|
+
const normalizeRecord = (record) => {
|
|
14326
|
+
if (!record) return "";
|
|
14327
|
+
const entries = Object.entries(record).map(([k, v]) => [k, [...v].sort()]).sort(([a], [b]) => a.localeCompare(b));
|
|
14328
|
+
return JSON.stringify(entries);
|
|
14329
|
+
};
|
|
14330
|
+
return JSON.stringify({
|
|
14331
|
+
sdkMode: getSdkMode() || "public",
|
|
14332
|
+
url: normalizeArray(filters.url),
|
|
14333
|
+
projectId: filters.projectId || "all",
|
|
14334
|
+
externalUserId: filters.externalUserId || "",
|
|
14335
|
+
search: ((_a = filters.search) == null ? void 0 : _a.trim()) || "",
|
|
14336
|
+
dateFrom: filters.dateFrom || "",
|
|
14337
|
+
dateTo: filters.dateTo || "",
|
|
14338
|
+
page: filters.page ?? 0,
|
|
14339
|
+
pageSize: filters.pageSize ?? 0,
|
|
14340
|
+
custom: normalizeRecord(filters.customFieldValues),
|
|
14341
|
+
jira: normalizeRecord(filters.jiraFieldValues),
|
|
14342
|
+
notion: normalizeRecord(filters.notionFieldValues),
|
|
14343
|
+
sortOrder: filters.sortOrder || ""
|
|
14344
|
+
});
|
|
14345
|
+
}
|
|
14346
|
+
function invalidateIssuesCache() {
|
|
14347
|
+
issuesCache = null;
|
|
14348
|
+
}
|
|
14349
|
+
function recordToFilterArray(record, keyFieldName) {
|
|
14350
|
+
if (!record) return null;
|
|
14351
|
+
const entries = Object.entries(record).filter(([, values]) => values.length > 0);
|
|
14352
|
+
if (entries.length === 0) return null;
|
|
14353
|
+
return entries.map(
|
|
14354
|
+
([key, values]) => ({ [keyFieldName]: key, values })
|
|
14355
|
+
);
|
|
14356
|
+
}
|
|
14357
|
+
function buildFilterQueryParams(filters, options = {}) {
|
|
14358
|
+
var _a;
|
|
14359
|
+
const params = new URLSearchParams();
|
|
14360
|
+
if (filters.projectId) {
|
|
14361
|
+
params.set("project_id", filters.projectId);
|
|
14362
|
+
}
|
|
14363
|
+
if (filters.url && filters.url !== "all") {
|
|
14364
|
+
if (Array.isArray(filters.url) && filters.url.length > 0) {
|
|
14365
|
+
params.set("url", filters.url.join(","));
|
|
14366
|
+
} else if (typeof filters.url === "string") {
|
|
14367
|
+
params.set("url", filters.url);
|
|
14368
|
+
}
|
|
14369
|
+
}
|
|
14370
|
+
if (options.includeListFilters) {
|
|
14371
|
+
const search2 = (_a = filters.search) == null ? void 0 : _a.trim();
|
|
14372
|
+
if (search2) {
|
|
14373
|
+
params.set("search", search2);
|
|
14374
|
+
}
|
|
14375
|
+
if (filters.dateFrom) {
|
|
14376
|
+
params.set("date_from", filters.dateFrom);
|
|
14377
|
+
}
|
|
14378
|
+
if (filters.dateTo) {
|
|
14379
|
+
params.set("date_to", filters.dateTo);
|
|
14380
|
+
}
|
|
14381
|
+
const customArr = recordToFilterArray(filters.customFieldValues, "fieldDefinitionId");
|
|
14382
|
+
if (customArr) {
|
|
14383
|
+
params.set("custom_field_filters", JSON.stringify(customArr));
|
|
14384
|
+
}
|
|
14385
|
+
const jiraArr = recordToFilterArray(filters.jiraFieldValues, "fieldName");
|
|
14386
|
+
if (jiraArr) {
|
|
14387
|
+
params.set("jira_field_filters", JSON.stringify(jiraArr));
|
|
14388
|
+
}
|
|
14389
|
+
const notionArr = recordToFilterArray(filters.notionFieldValues, "fieldName");
|
|
14390
|
+
if (notionArr) {
|
|
14391
|
+
params.set("notion_field_filters", JSON.stringify(notionArr));
|
|
14392
|
+
}
|
|
14393
|
+
}
|
|
14394
|
+
if (filters.externalUserId) {
|
|
14395
|
+
params.set("external_user_id", filters.externalUserId);
|
|
14396
|
+
}
|
|
14397
|
+
if (typeof filters.page === "number" && filters.page > 0) {
|
|
14398
|
+
params.set("page", String(filters.page));
|
|
14399
|
+
}
|
|
14400
|
+
if (typeof filters.pageSize === "number" && filters.pageSize > 0) {
|
|
14401
|
+
params.set("page_size", String(filters.pageSize));
|
|
14402
|
+
}
|
|
14403
|
+
if (filters.sortOrder) {
|
|
14404
|
+
params.set("sort_order", filters.sortOrder);
|
|
14405
|
+
}
|
|
14406
|
+
const queryString = params.toString();
|
|
14407
|
+
return queryString ? `?${queryString}` : "";
|
|
14408
|
+
}
|
|
14409
|
+
const issueService = {
|
|
14410
|
+
async createIssue(issueData) {
|
|
14411
|
+
const sanitizedData = sanitizeString(issueData);
|
|
14412
|
+
const screenshotUploadIds = (sanitizedData.screenshot || []).map((url) => getRegisteredUploadId(url, "screenshot")).filter((id2) => typeof id2 === "string" && id2.length > 0);
|
|
14413
|
+
const videoUploadId = sanitizedData.video ? getRegisteredUploadId(sanitizedData.video, "video") : void 0;
|
|
14414
|
+
if (screenshotUploadIds.length > 0) {
|
|
14415
|
+
sanitizedData.screenshot_upload_ids = screenshotUploadIds;
|
|
14416
|
+
}
|
|
14417
|
+
if (videoUploadId) {
|
|
14418
|
+
sanitizedData.video_upload_id = videoUploadId;
|
|
14419
|
+
}
|
|
14420
|
+
const issue = await apiClient.post(issuesEndpoint(), sanitizedData);
|
|
14421
|
+
invalidateIssuesCache();
|
|
14422
|
+
return issue;
|
|
14423
|
+
},
|
|
14424
|
+
async getIssues(filters = {}, options = {}) {
|
|
14425
|
+
if (!filters.projectId) {
|
|
14426
|
+
throw new Error("프로젝트 ID가 필요합니다. 로그인 상태를 확인해주세요.");
|
|
14427
|
+
}
|
|
14428
|
+
const filterKey = getFilterKey(filters);
|
|
14429
|
+
const now2 = Date.now();
|
|
14430
|
+
if (!options.forceRefresh && issuesCache && issuesCache.filterKey === filterKey && now2 - issuesCache.timestamp < CACHE_DURATION_MS) {
|
|
14431
|
+
return issuesCache.data;
|
|
14432
|
+
}
|
|
14433
|
+
const queryParams = buildFilterQueryParams(filters, { includeListFilters: true });
|
|
14434
|
+
const { items: issues = [] } = await apiClient.get(
|
|
14435
|
+
`${issuesEndpoint()}${queryParams}`
|
|
14436
|
+
);
|
|
14437
|
+
issuesCache = {
|
|
14438
|
+
data: issues,
|
|
14439
|
+
timestamp: now2,
|
|
14440
|
+
filterKey
|
|
14441
|
+
};
|
|
14442
|
+
return issues;
|
|
14443
|
+
},
|
|
14444
|
+
async getIssueStickers(filters = {}, options = {}) {
|
|
14445
|
+
var _a, _b2;
|
|
14446
|
+
if (!filters.projectId) {
|
|
14447
|
+
throw new Error("프로젝트 ID가 필요합니다. 로그인 상태를 확인해주세요.");
|
|
14448
|
+
}
|
|
14449
|
+
const filterKey = `${getFilterKey(filters)}:stickers`;
|
|
14450
|
+
const now2 = Date.now();
|
|
14451
|
+
if (!options.forceRefresh && issuesCache && issuesCache.filterKey === filterKey && now2 - issuesCache.timestamp < CACHE_DURATION_MS) {
|
|
14452
|
+
return issuesCache.data;
|
|
14453
|
+
}
|
|
14454
|
+
const issues = [];
|
|
14455
|
+
let page = 1;
|
|
14456
|
+
let hasMore = true;
|
|
14457
|
+
while (hasMore && issues.length <= STICKER_FETCH_LIMIT) {
|
|
14458
|
+
const response = await apiClient.getResponse(
|
|
14459
|
+
`${issuesEndpoint()}/stickers${buildFilterQueryParams({
|
|
14460
|
+
...filters,
|
|
14461
|
+
page,
|
|
14462
|
+
pageSize: STICKER_PAGE_SIZE
|
|
14463
|
+
})}`
|
|
14464
|
+
);
|
|
14465
|
+
const pageItems = ((_a = response.data) == null ? void 0 : _a.items) ?? [];
|
|
14466
|
+
issues.push(...pageItems);
|
|
14467
|
+
hasMore = Boolean((_b2 = response.data) == null ? void 0 : _b2.hasMore) && pageItems.length > 0;
|
|
14468
|
+
page += 1;
|
|
14469
|
+
}
|
|
14470
|
+
issuesCache = {
|
|
14471
|
+
data: issues,
|
|
14472
|
+
timestamp: now2,
|
|
14473
|
+
filterKey
|
|
14474
|
+
};
|
|
14475
|
+
return issues;
|
|
14476
|
+
},
|
|
14477
|
+
invalidateCache() {
|
|
14478
|
+
invalidateIssuesCache();
|
|
14479
|
+
},
|
|
14480
|
+
// 계약 정본: 백엔드 GET /api/sdk/issues/:id 는 back#101부터 읽기-시점-스크럽된
|
|
14481
|
+
// llm_prompt 를 상세 응답에 포함한다(목록 응답에는 없음) — 스코프를 이 반환 타입에만 한정한다.
|
|
14482
|
+
async getIssue(id2) {
|
|
14483
|
+
return apiClient.get(
|
|
14484
|
+
`${issuesEndpoint()}/${id2}${guestOwnerQuery()}`
|
|
14485
|
+
);
|
|
14486
|
+
},
|
|
14487
|
+
async updateIssue(id2, updates) {
|
|
14488
|
+
const sanitizedUpdates = sanitizeString(updates);
|
|
14489
|
+
const issue = await apiClient.put(
|
|
14490
|
+
`${issuesEndpoint()}/${id2}${guestOwnerQuery()}`,
|
|
14491
|
+
sanitizedUpdates
|
|
14492
|
+
);
|
|
14493
|
+
invalidateIssuesCache();
|
|
14494
|
+
return issue;
|
|
14495
|
+
},
|
|
14496
|
+
/**
|
|
14497
|
+
* 재생성. 스펙상 세 표면 모두에서 되고 역할과 무관하다 — 게이트는 역할이 아니라
|
|
14498
|
+
* 그 이슈에 접근할 수 있는지다(백엔드가 판정한다).
|
|
14499
|
+
*
|
|
14500
|
+
* 응답으로 갱신된 이슈가 오지만 상태는 다시 pending 이다. 호출부는 응답을
|
|
14501
|
+
* 그대로 쓰지 말고 폴링으로 이어받아야 한다.
|
|
14502
|
+
*/
|
|
14503
|
+
async regenerateIssue(id2) {
|
|
14504
|
+
const issue = await apiClient.post(
|
|
14505
|
+
`${issuesEndpoint()}/${encodeURIComponent(id2)}/regenerate${guestOwnerQuery()}`,
|
|
14506
|
+
{}
|
|
14507
|
+
);
|
|
14508
|
+
invalidateIssuesCache();
|
|
14509
|
+
return issue;
|
|
14510
|
+
},
|
|
14511
|
+
async deleteIssue(id2, options) {
|
|
14512
|
+
const endpoint = `${issuesEndpoint()}/${id2}${guestOwnerQuery()}`;
|
|
14513
|
+
if (options == null ? void 0 : options.deleteExternal) {
|
|
14514
|
+
await apiClient.delete(endpoint, {
|
|
14515
|
+
body: { deleteExternal: true }
|
|
14516
|
+
});
|
|
14517
|
+
} else {
|
|
14518
|
+
await apiClient.delete(endpoint);
|
|
14519
|
+
}
|
|
14520
|
+
invalidateIssuesCache();
|
|
14521
|
+
}
|
|
14522
|
+
};
|
|
14523
|
+
async function loadIssuesFromServer(url, projectId, externalUserId, options = {}) {
|
|
14524
|
+
if (!projectId) return [];
|
|
14525
|
+
try {
|
|
14526
|
+
return await issueService.getIssueStickers(
|
|
14527
|
+
{
|
|
14528
|
+
url,
|
|
14529
|
+
projectId,
|
|
14530
|
+
...externalUserId ? { externalUserId } : {}
|
|
14531
|
+
},
|
|
14532
|
+
options
|
|
14533
|
+
) || [];
|
|
14534
|
+
} catch (error) {
|
|
14535
|
+
devLogger.error("useIssues", "Failed to load issues:", error);
|
|
14536
|
+
return [];
|
|
14537
|
+
}
|
|
14538
|
+
}
|
|
14539
|
+
const contentProjectIdQueryKey = ["contentProjectId"];
|
|
14540
|
+
const contentIssuesQueryRoot = ["contentIssues"];
|
|
14541
|
+
function contentIssuesQueryKey({
|
|
14542
|
+
currentUrl,
|
|
14543
|
+
projectId,
|
|
14544
|
+
isMember,
|
|
14545
|
+
externalUserId
|
|
14546
|
+
}) {
|
|
14547
|
+
return [...contentIssuesQueryRoot, currentUrl, projectId, isMember, externalUserId];
|
|
14548
|
+
}
|
|
14549
|
+
function fetchContentIssues({
|
|
14550
|
+
currentUrl,
|
|
14551
|
+
projectId,
|
|
14552
|
+
isMember,
|
|
14553
|
+
externalUserId,
|
|
14554
|
+
forceRefresh
|
|
14555
|
+
}) {
|
|
14556
|
+
return loadIssuesFromServer(currentUrl, projectId, isMember ? void 0 : externalUserId, {
|
|
14557
|
+
forceRefresh
|
|
14558
|
+
});
|
|
14559
|
+
}
|
|
14560
|
+
function useIssues(currentUrl) {
|
|
14561
|
+
var _a;
|
|
14562
|
+
const { isMember } = useAuthContext();
|
|
14563
|
+
const config = SDKManager.getInstance().config;
|
|
14564
|
+
const sessionIssuesRef = useRef([]);
|
|
14565
|
+
const projectIdQuery = useSuspenseQuery({
|
|
14566
|
+
queryKey: contentProjectIdQueryKey,
|
|
14567
|
+
queryFn: async () => {
|
|
14568
|
+
const accountInfo = await getAccountInfo();
|
|
14569
|
+
return (accountInfo == null ? void 0 : accountInfo.projectId) ?? null;
|
|
14570
|
+
}
|
|
14571
|
+
});
|
|
14572
|
+
const projectId = projectIdQuery.data;
|
|
14573
|
+
const { data: serverIssues = [] } = useSuspenseQuery({
|
|
14574
|
+
queryKey: contentIssuesQueryKey({
|
|
14575
|
+
currentUrl,
|
|
14576
|
+
projectId,
|
|
14577
|
+
isMember,
|
|
14578
|
+
externalUserId: (_a = config.user) == null ? void 0 : _a.id
|
|
14579
|
+
}),
|
|
14580
|
+
queryFn: () => {
|
|
14581
|
+
var _a2;
|
|
14582
|
+
return fetchContentIssues({
|
|
14583
|
+
currentUrl,
|
|
14584
|
+
projectId,
|
|
14585
|
+
isMember,
|
|
14586
|
+
externalUserId: (_a2 = config.user) == null ? void 0 : _a2.id
|
|
14587
|
+
});
|
|
14588
|
+
}
|
|
14589
|
+
});
|
|
14590
|
+
const issues = !isMember && !config.user ? [...serverIssues, ...sessionIssuesRef.current] : serverIssues;
|
|
14591
|
+
const addSessionIssue = useCallback((issue) => {
|
|
14592
|
+
sessionIssuesRef.current = [...sessionIssuesRef.current, issue];
|
|
14593
|
+
}, []);
|
|
14594
|
+
const loadIssues = useCallback(async () => {
|
|
14595
|
+
issueService.invalidateCache();
|
|
14596
|
+
await Promise.all([
|
|
14597
|
+
queryClient.invalidateQueries({ queryKey: contentIssuesQueryRoot }),
|
|
14598
|
+
queryClient.invalidateQueries({ queryKey: contentProjectIdQueryKey })
|
|
14599
|
+
]);
|
|
14600
|
+
}, []);
|
|
14601
|
+
useIssueListGenerationPolling({
|
|
14602
|
+
issues: serverIssues,
|
|
14603
|
+
onRefetch: () => void loadIssues()
|
|
14604
|
+
});
|
|
14605
|
+
return {
|
|
14606
|
+
issues,
|
|
14607
|
+
loadIssues,
|
|
14608
|
+
addSessionIssue
|
|
14609
|
+
};
|
|
14610
|
+
}
|
|
14611
|
+
function readCurrentUrl() {
|
|
14612
|
+
return getCurrentPageUrl();
|
|
14613
|
+
}
|
|
14614
|
+
function usePrefetchContentIssues() {
|
|
14615
|
+
const { isAuthenticated, isLoading, isMember, projectId, accountId, token } = useAuthContext();
|
|
14616
|
+
const lastFetchKeyRef = useRef(null);
|
|
14617
|
+
useEffect(() => {
|
|
14618
|
+
var _a;
|
|
14619
|
+
if (isLoading || !isAuthenticated || !projectId) {
|
|
14620
|
+
return;
|
|
14621
|
+
}
|
|
14622
|
+
const currentUrl = readCurrentUrl();
|
|
14623
|
+
const externalUserId = (_a = SDKManager.getInstance().config.user) == null ? void 0 : _a.id;
|
|
14624
|
+
const fetchKey = [
|
|
14625
|
+
currentUrl,
|
|
14626
|
+
projectId,
|
|
14627
|
+
isMember ? "member" : "public",
|
|
14628
|
+
accountId ?? "",
|
|
14629
|
+
token ?? "",
|
|
14630
|
+
externalUserId ?? ""
|
|
14631
|
+
].join("|");
|
|
14632
|
+
if (lastFetchKeyRef.current === fetchKey) {
|
|
14633
|
+
return;
|
|
14634
|
+
}
|
|
14635
|
+
lastFetchKeyRef.current = fetchKey;
|
|
14636
|
+
queryClient.setQueryData(contentProjectIdQueryKey, projectId);
|
|
14637
|
+
void queryClient.fetchQuery({
|
|
14638
|
+
queryKey: contentIssuesQueryKey({
|
|
14639
|
+
currentUrl,
|
|
14640
|
+
projectId,
|
|
14641
|
+
isMember,
|
|
14642
|
+
externalUserId
|
|
14643
|
+
}),
|
|
14644
|
+
queryFn: () => fetchContentIssues({
|
|
14645
|
+
currentUrl,
|
|
14646
|
+
projectId,
|
|
14647
|
+
isMember,
|
|
14648
|
+
externalUserId,
|
|
14649
|
+
forceRefresh: true
|
|
14650
|
+
}),
|
|
14651
|
+
staleTime: 0
|
|
14652
|
+
}).catch((error) => {
|
|
14653
|
+
devLogger.error("usePrefetchContentIssues", "Failed to prefetch issues:", error);
|
|
14654
|
+
});
|
|
14655
|
+
}, [accountId, isAuthenticated, isLoading, isMember, projectId, token]);
|
|
14656
|
+
}
|
|
14507
14657
|
function UnrenderedStickerDialog({
|
|
14508
14658
|
open,
|
|
14509
14659
|
items,
|
|
@@ -14587,6 +14737,74 @@ function UnrenderedStickerDialog({
|
|
|
14587
14737
|
}
|
|
14588
14738
|
);
|
|
14589
14739
|
}
|
|
14740
|
+
function getDeepActiveElement(root2 = document) {
|
|
14741
|
+
var _a;
|
|
14742
|
+
let active = root2.activeElement;
|
|
14743
|
+
while ((_a = active == null ? void 0 : active.shadowRoot) == null ? void 0 : _a.activeElement) {
|
|
14744
|
+
active = active.shadowRoot.activeElement;
|
|
14745
|
+
}
|
|
14746
|
+
return active;
|
|
14747
|
+
}
|
|
14748
|
+
const FOCUSABLE_SELECTOR = [
|
|
14749
|
+
"a[href]",
|
|
14750
|
+
"button:not([disabled])",
|
|
14751
|
+
"input:not([disabled])",
|
|
14752
|
+
"select:not([disabled])",
|
|
14753
|
+
"textarea:not([disabled])",
|
|
14754
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
14755
|
+
].join(",");
|
|
14756
|
+
function isVisible(element2, container) {
|
|
14757
|
+
if (getComputedStyle(element2).visibility === "hidden") return false;
|
|
14758
|
+
let node2 = element2;
|
|
14759
|
+
while (node2) {
|
|
14760
|
+
if (getComputedStyle(node2).display === "none") return false;
|
|
14761
|
+
if (node2 === container) break;
|
|
14762
|
+
node2 = node2.parentElement;
|
|
14763
|
+
}
|
|
14764
|
+
return true;
|
|
14765
|
+
}
|
|
14766
|
+
function tabbables(container) {
|
|
14767
|
+
return Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)).filter(
|
|
14768
|
+
(element2) => element2.tabIndex >= 0 && isVisible(element2, container)
|
|
14769
|
+
);
|
|
14770
|
+
}
|
|
14771
|
+
function useOverlayFocus({
|
|
14772
|
+
enabled,
|
|
14773
|
+
container,
|
|
14774
|
+
initialFocusRef,
|
|
14775
|
+
initialFocus = "first"
|
|
14776
|
+
}) {
|
|
14777
|
+
useEffect(() => {
|
|
14778
|
+
if (!enabled || !container) return;
|
|
14779
|
+
const restoreTo = getDeepActiveElement();
|
|
14780
|
+
const target = (initialFocusRef == null ? void 0 : initialFocusRef.current) ?? (initialFocus === "container" ? container : tabbables(container)[0] ?? container);
|
|
14781
|
+
target.focus({ preventScroll: true });
|
|
14782
|
+
const handleKeyDown = (event) => {
|
|
14783
|
+
if (event.key !== "Tab") return;
|
|
14784
|
+
const items = tabbables(container);
|
|
14785
|
+
const first = items[0];
|
|
14786
|
+
const last = items[items.length - 1];
|
|
14787
|
+
if (!first || !last) {
|
|
14788
|
+
event.preventDefault();
|
|
14789
|
+
return;
|
|
14790
|
+
}
|
|
14791
|
+
const active = getDeepActiveElement();
|
|
14792
|
+
const outside = !active || !container.contains(active);
|
|
14793
|
+
if (!event.shiftKey && (outside || active === last)) {
|
|
14794
|
+
event.preventDefault();
|
|
14795
|
+
first.focus({ preventScroll: true });
|
|
14796
|
+
} else if (event.shiftKey && (outside || active === first)) {
|
|
14797
|
+
event.preventDefault();
|
|
14798
|
+
last.focus({ preventScroll: true });
|
|
14799
|
+
}
|
|
14800
|
+
};
|
|
14801
|
+
container.addEventListener("keydown", handleKeyDown);
|
|
14802
|
+
return () => {
|
|
14803
|
+
container.removeEventListener("keydown", handleKeyDown);
|
|
14804
|
+
if (restoreTo == null ? void 0 : restoreTo.isConnected) restoreTo.focus({ preventScroll: true });
|
|
14805
|
+
};
|
|
14806
|
+
}, [enabled, container, initialFocusRef, initialFocus]);
|
|
14807
|
+
}
|
|
14590
14808
|
function OverlayShell({
|
|
14591
14809
|
open,
|
|
14592
14810
|
children,
|
|
@@ -14598,6 +14816,8 @@ function OverlayShell({
|
|
|
14598
14816
|
}) {
|
|
14599
14817
|
const onCloseRef = useRef(onClose);
|
|
14600
14818
|
onCloseRef.current = onClose;
|
|
14819
|
+
const [shell, setShell] = useState(null);
|
|
14820
|
+
useOverlayFocus({ enabled: open, container: shell, initialFocus: "container" });
|
|
14601
14821
|
useEffect(() => {
|
|
14602
14822
|
if (!open) return;
|
|
14603
14823
|
const handleKeyDown = (e) => {
|
|
@@ -14632,12 +14852,14 @@ function OverlayShell({
|
|
|
14632
14852
|
children: /* @__PURE__ */ jsx(
|
|
14633
14853
|
motion.div,
|
|
14634
14854
|
{
|
|
14855
|
+
ref: setShell,
|
|
14856
|
+
tabIndex: -1,
|
|
14635
14857
|
initial: { opacity: 0, y: 12, scale: 0.98 },
|
|
14636
14858
|
animate: { opacity: 1, y: 0, scale: 1 },
|
|
14637
14859
|
exit: { opacity: 0, y: 8, scale: 0.98 },
|
|
14638
14860
|
transition: { duration: 0.2, ease: [0.34, 1.2, 0.64, 1] },
|
|
14639
14861
|
className: cn$1(
|
|
14640
|
-
"issue-overlay-shell flex w-full flex-col overflow-hidden",
|
|
14862
|
+
"issue-overlay-shell flex w-full flex-col overflow-hidden focus:outline-none",
|
|
14641
14863
|
shellClassName
|
|
14642
14864
|
),
|
|
14643
14865
|
onClick: (e) => e.stopPropagation(),
|
|
@@ -14649,6 +14871,670 @@ function OverlayShell({
|
|
|
14649
14871
|
"overlay-backdrop"
|
|
14650
14872
|
) : null });
|
|
14651
14873
|
}
|
|
14874
|
+
const ISSUE_DRAFT_DISABLED_MESSAGE = "지금은 AI 작성을 사용할 수 없습니다. 제목과 설명을 직접 작성해주세요.";
|
|
14875
|
+
const LABEL = "기대한 동작";
|
|
14876
|
+
const PLACEHOLDER = "예: 할인 쿠폰을 적용하면 결제 금액에서 즉시 차감되어야 합니다";
|
|
14877
|
+
const SWITCH_TO_MANUAL = "직접 작성";
|
|
14878
|
+
const SUBMIT = "보내기";
|
|
14879
|
+
const MAX_INPUT_HEIGHT = 160;
|
|
14880
|
+
const FOCUS_RETRY_BUDGET_MS = 1e3;
|
|
14881
|
+
function isFocused(node2) {
|
|
14882
|
+
const root2 = node2.getRootNode();
|
|
14883
|
+
return root2 instanceof ShadowRoot ? root2.activeElement === node2 : document.activeElement === node2;
|
|
14884
|
+
}
|
|
14885
|
+
function IssueIntentBubble({
|
|
14886
|
+
value,
|
|
14887
|
+
onChange,
|
|
14888
|
+
onSubmit,
|
|
14889
|
+
onSwitchToManual,
|
|
14890
|
+
submitting = false,
|
|
14891
|
+
error = null
|
|
14892
|
+
}) {
|
|
14893
|
+
const inputRef = useRef(null);
|
|
14894
|
+
const canSubmit = !submitting && value.trim().length > 0;
|
|
14895
|
+
useEffect(() => {
|
|
14896
|
+
const node2 = inputRef.current;
|
|
14897
|
+
if (!node2) return;
|
|
14898
|
+
const deadline = Date.now() + FOCUS_RETRY_BUDGET_MS;
|
|
14899
|
+
let frame2 = 0;
|
|
14900
|
+
let abandoned = false;
|
|
14901
|
+
const abandon = () => {
|
|
14902
|
+
abandoned = true;
|
|
14903
|
+
};
|
|
14904
|
+
const attempt = () => {
|
|
14905
|
+
frame2 = 0;
|
|
14906
|
+
if (abandoned || isFocused(node2)) return;
|
|
14907
|
+
node2.focus({ preventScroll: true });
|
|
14908
|
+
if (isFocused(node2) || Date.now() >= deadline) return;
|
|
14909
|
+
frame2 = requestAnimationFrame(attempt);
|
|
14910
|
+
};
|
|
14911
|
+
window.addEventListener("pointerdown", abandon, { capture: true });
|
|
14912
|
+
window.addEventListener("keydown", abandon, { capture: true });
|
|
14913
|
+
attempt();
|
|
14914
|
+
return () => {
|
|
14915
|
+
window.removeEventListener("pointerdown", abandon, { capture: true });
|
|
14916
|
+
window.removeEventListener("keydown", abandon, { capture: true });
|
|
14917
|
+
if (frame2) cancelAnimationFrame(frame2);
|
|
14918
|
+
};
|
|
14919
|
+
}, []);
|
|
14920
|
+
useLayoutEffect(() => {
|
|
14921
|
+
const node2 = inputRef.current;
|
|
14922
|
+
if (!node2) return;
|
|
14923
|
+
node2.style.height = "auto";
|
|
14924
|
+
node2.style.height = `${Math.min(node2.scrollHeight, MAX_INPUT_HEIGHT)}px`;
|
|
14925
|
+
}, [value]);
|
|
14926
|
+
return (
|
|
14927
|
+
/*
|
|
14928
|
+
* 테두리는 포커스일 때만 브랜드 색으로 간다. 이 리포의 입력 컨트롤 관례가 그렇다
|
|
14929
|
+
* (ui/input.tsx, ui/textarea.tsx: 평소 border-line-subtle, 포커스에 line-focus).
|
|
14930
|
+
* `line-focus` 는 `--border-focus` = 브랜드 색상값이다(tokens.css).
|
|
14931
|
+
*
|
|
14932
|
+
* 관례는 `focus-visible:outline-line-focus` 지만 여기서는 border 로 바꾼다.
|
|
14933
|
+
* outline 은 border 상자 바깥에 그려지는데 말풍선 상자가 `overflow-hidden`
|
|
14934
|
+
* (IssueIntentBubbleAnchor)이라 잘린다. focus 가 아니라 focus-within 인 것은
|
|
14935
|
+
* 테두리가 카드에 있고 실제로 포커스를 받는 것은 그 안의 textarea 이기 때문이다.
|
|
14936
|
+
*/
|
|
14937
|
+
/* @__PURE__ */ jsxs(
|
|
14938
|
+
"div",
|
|
14939
|
+
{
|
|
14940
|
+
className: `${ISSUE_STICKER_REPORT_FORM_CLASS} w-80 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`,
|
|
14941
|
+
children: [
|
|
14942
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-1.5", children: [
|
|
14943
|
+
/* @__PURE__ */ jsx(
|
|
14944
|
+
"textarea",
|
|
14945
|
+
{
|
|
14946
|
+
ref: inputRef,
|
|
14947
|
+
"aria-label": LABEL,
|
|
14948
|
+
rows: 1,
|
|
14949
|
+
className: "max-h-40 flex-1 resize-none overflow-y-auto bg-transparent px-1.5 py-1 text-sm text-text-primary outline-none placeholder:text-text-tertiary disabled:opacity-50",
|
|
14950
|
+
placeholder: PLACEHOLDER,
|
|
14951
|
+
value,
|
|
14952
|
+
disabled: submitting,
|
|
14953
|
+
onChange: (event) => onChange(event.target.value)
|
|
14954
|
+
}
|
|
14955
|
+
),
|
|
14956
|
+
/* @__PURE__ */ jsx(
|
|
14957
|
+
"button",
|
|
14958
|
+
{
|
|
14959
|
+
type: "button",
|
|
14960
|
+
"aria-label": SUBMIT,
|
|
14961
|
+
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-brand text-brand-fg disabled:opacity-50",
|
|
14962
|
+
disabled: !canSubmit,
|
|
14963
|
+
onClick: () => canSubmit && onSubmit(),
|
|
14964
|
+
children: /* @__PURE__ */ jsx(ArrowUp, { className: "h-4 w-4" })
|
|
14965
|
+
}
|
|
14966
|
+
)
|
|
14967
|
+
] }),
|
|
14968
|
+
error ? /* @__PURE__ */ jsx("p", { className: "text-xs text-danger", children: error }) : null,
|
|
14969
|
+
/* @__PURE__ */ jsxs(Button, { type: "button", variant: "secondary", size: "sm", onClick: onSwitchToManual, children: [
|
|
14970
|
+
/* @__PURE__ */ jsx(PenLine, { className: "h-3.5 w-3.5" }),
|
|
14971
|
+
SWITCH_TO_MANUAL
|
|
14972
|
+
] })
|
|
14973
|
+
]
|
|
14974
|
+
}
|
|
14975
|
+
)
|
|
14976
|
+
);
|
|
14977
|
+
}
|
|
14978
|
+
var DIALOG_NAME = "Dialog";
|
|
14979
|
+
var [createDialogContext] = createContextScope(DIALOG_NAME);
|
|
14980
|
+
var [DialogProvider, useDialogContext] = createDialogContext(DIALOG_NAME);
|
|
14981
|
+
var Dialog$1 = (props) => {
|
|
14982
|
+
const {
|
|
14983
|
+
__scopeDialog,
|
|
14984
|
+
children,
|
|
14985
|
+
open: openProp,
|
|
14986
|
+
defaultOpen,
|
|
14987
|
+
onOpenChange,
|
|
14988
|
+
modal = true
|
|
14989
|
+
} = props;
|
|
14990
|
+
const triggerRef = React.useRef(null);
|
|
14991
|
+
const contentRef = React.useRef(null);
|
|
14992
|
+
const [open, setOpen] = useControllableState({
|
|
14993
|
+
prop: openProp,
|
|
14994
|
+
defaultProp: defaultOpen ?? false,
|
|
14995
|
+
onChange: onOpenChange,
|
|
14996
|
+
caller: DIALOG_NAME
|
|
14997
|
+
});
|
|
14998
|
+
return /* @__PURE__ */ jsx(
|
|
14999
|
+
DialogProvider,
|
|
15000
|
+
{
|
|
15001
|
+
scope: __scopeDialog,
|
|
15002
|
+
triggerRef,
|
|
15003
|
+
contentRef,
|
|
15004
|
+
contentId: useId$1(),
|
|
15005
|
+
titleId: useId$1(),
|
|
15006
|
+
descriptionId: useId$1(),
|
|
15007
|
+
open,
|
|
15008
|
+
onOpenChange: setOpen,
|
|
15009
|
+
onOpenToggle: React.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
|
|
15010
|
+
modal,
|
|
15011
|
+
children
|
|
15012
|
+
}
|
|
15013
|
+
);
|
|
15014
|
+
};
|
|
15015
|
+
Dialog$1.displayName = DIALOG_NAME;
|
|
15016
|
+
var TRIGGER_NAME$3 = "DialogTrigger";
|
|
15017
|
+
var DialogTrigger = React.forwardRef(
|
|
15018
|
+
(props, forwardedRef) => {
|
|
15019
|
+
const { __scopeDialog, ...triggerProps } = props;
|
|
15020
|
+
const context = useDialogContext(TRIGGER_NAME$3, __scopeDialog);
|
|
15021
|
+
const composedTriggerRef = useComposedRefs$1(forwardedRef, context.triggerRef);
|
|
15022
|
+
return /* @__PURE__ */ jsx(
|
|
15023
|
+
Primitive.button,
|
|
15024
|
+
{
|
|
15025
|
+
type: "button",
|
|
15026
|
+
"aria-haspopup": "dialog",
|
|
15027
|
+
"aria-expanded": context.open,
|
|
15028
|
+
"aria-controls": context.open ? context.contentId : void 0,
|
|
15029
|
+
"data-state": getState$2(context.open),
|
|
15030
|
+
...triggerProps,
|
|
15031
|
+
ref: composedTriggerRef,
|
|
15032
|
+
onClick: composeEventHandlers(props.onClick, context.onOpenToggle)
|
|
15033
|
+
}
|
|
15034
|
+
);
|
|
15035
|
+
}
|
|
15036
|
+
);
|
|
15037
|
+
DialogTrigger.displayName = TRIGGER_NAME$3;
|
|
15038
|
+
var PORTAL_NAME = "DialogPortal";
|
|
15039
|
+
var [PortalProvider, usePortalContext] = createDialogContext(PORTAL_NAME, {
|
|
15040
|
+
forceMount: void 0
|
|
15041
|
+
});
|
|
15042
|
+
var DialogPortal$1 = (props) => {
|
|
15043
|
+
const { __scopeDialog, forceMount, children, container } = props;
|
|
15044
|
+
const context = useDialogContext(PORTAL_NAME, __scopeDialog);
|
|
15045
|
+
return /* @__PURE__ */ jsx(PortalProvider, { scope: __scopeDialog, forceMount, children: React.Children.map(children, (child) => /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: /* @__PURE__ */ jsx(Portal$1, { asChild: true, container, children: child }) })) });
|
|
15046
|
+
};
|
|
15047
|
+
DialogPortal$1.displayName = PORTAL_NAME;
|
|
15048
|
+
var OVERLAY_NAME = "DialogOverlay";
|
|
15049
|
+
var DialogOverlay$1 = React.forwardRef(
|
|
15050
|
+
(props, forwardedRef) => {
|
|
15051
|
+
const portalContext = usePortalContext(OVERLAY_NAME, props.__scopeDialog);
|
|
15052
|
+
const { forceMount = portalContext.forceMount, ...overlayProps } = props;
|
|
15053
|
+
const context = useDialogContext(OVERLAY_NAME, props.__scopeDialog);
|
|
15054
|
+
return context.modal ? /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: /* @__PURE__ */ jsx(DialogOverlayImpl, { ...overlayProps, ref: forwardedRef }) }) : null;
|
|
15055
|
+
}
|
|
15056
|
+
);
|
|
15057
|
+
DialogOverlay$1.displayName = OVERLAY_NAME;
|
|
15058
|
+
var Slot = createSlot("DialogOverlay.RemoveScroll");
|
|
15059
|
+
var DialogOverlayImpl = React.forwardRef(
|
|
15060
|
+
(props, forwardedRef) => {
|
|
15061
|
+
const { __scopeDialog, ...overlayProps } = props;
|
|
15062
|
+
const context = useDialogContext(OVERLAY_NAME, __scopeDialog);
|
|
15063
|
+
const registerDismissableSurface = useDismissableLayerSurface();
|
|
15064
|
+
const composedRefs = useComposedRefs$1(forwardedRef, registerDismissableSurface);
|
|
15065
|
+
return (
|
|
15066
|
+
// Make sure `Content` is scrollable even when it doesn't live inside `RemoveScroll`
|
|
15067
|
+
// ie. when `Overlay` and `Content` are siblings
|
|
15068
|
+
/* @__PURE__ */ jsx(ReactRemoveScroll, { as: Slot, allowPinchZoom: true, shards: [context.contentRef], children: /* @__PURE__ */ jsx(
|
|
15069
|
+
Primitive.div,
|
|
15070
|
+
{
|
|
15071
|
+
"data-state": getState$2(context.open),
|
|
15072
|
+
...overlayProps,
|
|
15073
|
+
ref: composedRefs,
|
|
15074
|
+
style: { pointerEvents: "auto", ...overlayProps.style }
|
|
15075
|
+
}
|
|
15076
|
+
) })
|
|
15077
|
+
);
|
|
15078
|
+
}
|
|
15079
|
+
);
|
|
15080
|
+
var CONTENT_NAME$3 = "DialogContent";
|
|
15081
|
+
var DialogContent$1 = React.forwardRef(
|
|
15082
|
+
(props, forwardedRef) => {
|
|
15083
|
+
const portalContext = usePortalContext(CONTENT_NAME$3, props.__scopeDialog);
|
|
15084
|
+
const { forceMount = portalContext.forceMount, ...contentProps } = props;
|
|
15085
|
+
const context = useDialogContext(CONTENT_NAME$3, props.__scopeDialog);
|
|
15086
|
+
return /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: context.modal ? /* @__PURE__ */ jsx(DialogContentModal, { ...contentProps, ref: forwardedRef }) : /* @__PURE__ */ jsx(DialogContentNonModal, { ...contentProps, ref: forwardedRef }) });
|
|
15087
|
+
}
|
|
15088
|
+
);
|
|
15089
|
+
DialogContent$1.displayName = CONTENT_NAME$3;
|
|
15090
|
+
var DialogContentModal = React.forwardRef(
|
|
15091
|
+
(props, forwardedRef) => {
|
|
15092
|
+
const context = useDialogContext(CONTENT_NAME$3, props.__scopeDialog);
|
|
15093
|
+
const contentRef = React.useRef(null);
|
|
15094
|
+
const composedRefs = useComposedRefs$1(forwardedRef, context.contentRef, contentRef);
|
|
15095
|
+
React.useEffect(() => {
|
|
15096
|
+
const content2 = contentRef.current;
|
|
15097
|
+
if (content2) return hideOthers(content2);
|
|
15098
|
+
}, []);
|
|
15099
|
+
return /* @__PURE__ */ jsx(
|
|
15100
|
+
DialogContentImpl,
|
|
15101
|
+
{
|
|
15102
|
+
...props,
|
|
15103
|
+
ref: composedRefs,
|
|
15104
|
+
trapFocus: context.open,
|
|
15105
|
+
disableOutsidePointerEvents: context.open,
|
|
15106
|
+
onCloseAutoFocus: composeEventHandlers(props.onCloseAutoFocus, (event) => {
|
|
15107
|
+
var _a;
|
|
15108
|
+
event.preventDefault();
|
|
15109
|
+
(_a = context.triggerRef.current) == null ? void 0 : _a.focus();
|
|
15110
|
+
}),
|
|
15111
|
+
onPointerDownOutside: composeEventHandlers(props.onPointerDownOutside, (event) => {
|
|
15112
|
+
const originalEvent = event.detail.originalEvent;
|
|
15113
|
+
const ctrlLeftClick = originalEvent.button === 0 && originalEvent.ctrlKey === true;
|
|
15114
|
+
const isRightClick = originalEvent.button === 2 || ctrlLeftClick;
|
|
15115
|
+
if (isRightClick) event.preventDefault();
|
|
15116
|
+
}),
|
|
15117
|
+
onFocusOutside: composeEventHandlers(
|
|
15118
|
+
props.onFocusOutside,
|
|
15119
|
+
(event) => event.preventDefault()
|
|
15120
|
+
)
|
|
15121
|
+
}
|
|
15122
|
+
);
|
|
15123
|
+
}
|
|
15124
|
+
);
|
|
15125
|
+
var DialogContentNonModal = React.forwardRef(
|
|
15126
|
+
(props, forwardedRef) => {
|
|
15127
|
+
const context = useDialogContext(CONTENT_NAME$3, props.__scopeDialog);
|
|
15128
|
+
const hasInteractedOutsideRef = React.useRef(false);
|
|
15129
|
+
const hasPointerDownOutsideRef = React.useRef(false);
|
|
15130
|
+
return /* @__PURE__ */ jsx(
|
|
15131
|
+
DialogContentImpl,
|
|
15132
|
+
{
|
|
15133
|
+
...props,
|
|
15134
|
+
ref: forwardedRef,
|
|
15135
|
+
trapFocus: false,
|
|
15136
|
+
disableOutsidePointerEvents: false,
|
|
15137
|
+
onCloseAutoFocus: (event) => {
|
|
15138
|
+
var _a, _b2;
|
|
15139
|
+
(_a = props.onCloseAutoFocus) == null ? void 0 : _a.call(props, event);
|
|
15140
|
+
if (!event.defaultPrevented) {
|
|
15141
|
+
if (!hasInteractedOutsideRef.current) (_b2 = context.triggerRef.current) == null ? void 0 : _b2.focus();
|
|
15142
|
+
event.preventDefault();
|
|
15143
|
+
}
|
|
15144
|
+
hasInteractedOutsideRef.current = false;
|
|
15145
|
+
hasPointerDownOutsideRef.current = false;
|
|
15146
|
+
},
|
|
15147
|
+
onInteractOutside: (event) => {
|
|
15148
|
+
var _a, _b2;
|
|
15149
|
+
(_a = props.onInteractOutside) == null ? void 0 : _a.call(props, event);
|
|
15150
|
+
if (!event.defaultPrevented) {
|
|
15151
|
+
hasInteractedOutsideRef.current = true;
|
|
15152
|
+
if (event.detail.originalEvent.type === "pointerdown") {
|
|
15153
|
+
hasPointerDownOutsideRef.current = true;
|
|
15154
|
+
}
|
|
15155
|
+
}
|
|
15156
|
+
const target = event.target;
|
|
15157
|
+
const targetIsTrigger = (_b2 = context.triggerRef.current) == null ? void 0 : _b2.contains(target);
|
|
15158
|
+
if (targetIsTrigger) event.preventDefault();
|
|
15159
|
+
if (event.detail.originalEvent.type === "focusin" && hasPointerDownOutsideRef.current) {
|
|
15160
|
+
event.preventDefault();
|
|
15161
|
+
}
|
|
15162
|
+
}
|
|
15163
|
+
}
|
|
15164
|
+
);
|
|
15165
|
+
}
|
|
15166
|
+
);
|
|
15167
|
+
var DialogContentImpl = React.forwardRef(
|
|
15168
|
+
(props, forwardedRef) => {
|
|
15169
|
+
const { __scopeDialog, trapFocus, onOpenAutoFocus, onCloseAutoFocus, ...contentProps } = props;
|
|
15170
|
+
const context = useDialogContext(CONTENT_NAME$3, __scopeDialog);
|
|
15171
|
+
useFocusGuards();
|
|
15172
|
+
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
|
|
15173
|
+
FocusScope,
|
|
15174
|
+
{
|
|
15175
|
+
asChild: true,
|
|
15176
|
+
loop: true,
|
|
15177
|
+
trapped: trapFocus,
|
|
15178
|
+
onMountAutoFocus: onOpenAutoFocus,
|
|
15179
|
+
onUnmountAutoFocus: onCloseAutoFocus,
|
|
15180
|
+
children: /* @__PURE__ */ jsx(
|
|
15181
|
+
DismissableLayer,
|
|
15182
|
+
{
|
|
15183
|
+
role: "dialog",
|
|
15184
|
+
id: context.contentId,
|
|
15185
|
+
"aria-describedby": context.descriptionId,
|
|
15186
|
+
"aria-labelledby": context.titleId,
|
|
15187
|
+
"data-state": getState$2(context.open),
|
|
15188
|
+
...contentProps,
|
|
15189
|
+
ref: forwardedRef,
|
|
15190
|
+
deferPointerDownOutside: true,
|
|
15191
|
+
onDismiss: () => context.onOpenChange(false)
|
|
15192
|
+
}
|
|
15193
|
+
)
|
|
15194
|
+
}
|
|
15195
|
+
) });
|
|
15196
|
+
}
|
|
15197
|
+
);
|
|
15198
|
+
var TITLE_NAME = "DialogTitle";
|
|
15199
|
+
var DialogTitle$1 = React.forwardRef(
|
|
15200
|
+
(props, forwardedRef) => {
|
|
15201
|
+
const { __scopeDialog, ...titleProps } = props;
|
|
15202
|
+
const context = useDialogContext(TITLE_NAME, __scopeDialog);
|
|
15203
|
+
return /* @__PURE__ */ jsx(Primitive.h2, { id: context.titleId, ...titleProps, ref: forwardedRef });
|
|
15204
|
+
}
|
|
15205
|
+
);
|
|
15206
|
+
DialogTitle$1.displayName = TITLE_NAME;
|
|
15207
|
+
var DESCRIPTION_NAME = "DialogDescription";
|
|
15208
|
+
var DialogDescription$1 = React.forwardRef(
|
|
15209
|
+
(props, forwardedRef) => {
|
|
15210
|
+
const { __scopeDialog, ...descriptionProps } = props;
|
|
15211
|
+
const context = useDialogContext(DESCRIPTION_NAME, __scopeDialog);
|
|
15212
|
+
return /* @__PURE__ */ jsx(Primitive.p, { id: context.descriptionId, ...descriptionProps, ref: forwardedRef });
|
|
15213
|
+
}
|
|
15214
|
+
);
|
|
15215
|
+
DialogDescription$1.displayName = DESCRIPTION_NAME;
|
|
15216
|
+
var CLOSE_NAME = "DialogClose";
|
|
15217
|
+
var DialogClose$1 = React.forwardRef(
|
|
15218
|
+
(props, forwardedRef) => {
|
|
15219
|
+
const { __scopeDialog, ...closeProps } = props;
|
|
15220
|
+
const context = useDialogContext(CLOSE_NAME, __scopeDialog);
|
|
15221
|
+
return /* @__PURE__ */ jsx(
|
|
15222
|
+
Primitive.button,
|
|
15223
|
+
{
|
|
15224
|
+
type: "button",
|
|
15225
|
+
...closeProps,
|
|
15226
|
+
ref: forwardedRef,
|
|
15227
|
+
onClick: composeEventHandlers(props.onClick, () => context.onOpenChange(false))
|
|
15228
|
+
}
|
|
15229
|
+
);
|
|
15230
|
+
}
|
|
15231
|
+
);
|
|
15232
|
+
DialogClose$1.displayName = CLOSE_NAME;
|
|
15233
|
+
function getState$2(open) {
|
|
15234
|
+
return open ? "open" : "closed";
|
|
15235
|
+
}
|
|
15236
|
+
const Dialog = Dialog$1;
|
|
15237
|
+
const DialogClose = DialogClose$1;
|
|
15238
|
+
const DialogPortal = ({ children }) => {
|
|
15239
|
+
const portalContainer = usePortalContainer();
|
|
15240
|
+
return /* @__PURE__ */ jsx(DialogPortal$1, { container: portalContainer, children });
|
|
15241
|
+
};
|
|
15242
|
+
const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
15243
|
+
DialogOverlay$1,
|
|
15244
|
+
{
|
|
15245
|
+
ref,
|
|
15246
|
+
className: cn$1(
|
|
15247
|
+
`fixed inset-0 ${OVERLAY_LAYER.dialogOverlay} bg-black/24`,
|
|
15248
|
+
"data-[state=open]:animate-fade-in data-[state=closed]:animate-fade-out",
|
|
15249
|
+
"motion-reduce:animate-none",
|
|
15250
|
+
className
|
|
15251
|
+
),
|
|
15252
|
+
...props
|
|
15253
|
+
}
|
|
15254
|
+
));
|
|
15255
|
+
DialogOverlay.displayName = DialogOverlay$1.displayName;
|
|
15256
|
+
const DialogContent = React.forwardRef(
|
|
15257
|
+
({
|
|
15258
|
+
className,
|
|
15259
|
+
children,
|
|
15260
|
+
hideCloseButton = false,
|
|
15261
|
+
initialFocusRef,
|
|
15262
|
+
onEscapeKeyDown,
|
|
15263
|
+
onOpenAutoFocus,
|
|
15264
|
+
onCloseAutoFocus,
|
|
15265
|
+
style,
|
|
15266
|
+
...props
|
|
15267
|
+
}, ref) => {
|
|
15268
|
+
const [content2, setContent] = React.useState(null);
|
|
15269
|
+
const composedRef = React.useCallback(
|
|
15270
|
+
(node2) => {
|
|
15271
|
+
setContent(node2);
|
|
15272
|
+
if (typeof ref === "function") ref(node2);
|
|
15273
|
+
else if (ref) ref.current = node2;
|
|
15274
|
+
},
|
|
15275
|
+
[ref]
|
|
15276
|
+
);
|
|
15277
|
+
useOverlayFocus({ enabled: true, container: content2, initialFocusRef });
|
|
15278
|
+
return /* @__PURE__ */ jsxs(DialogPortal, { children: [
|
|
15279
|
+
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
15280
|
+
/* @__PURE__ */ jsxs(
|
|
15281
|
+
DialogContent$1,
|
|
15282
|
+
{
|
|
15283
|
+
ref: composedRef,
|
|
15284
|
+
onEscapeKeyDown: stopEscapePropagation(onEscapeKeyDown),
|
|
15285
|
+
onOpenAutoFocus: (event) => {
|
|
15286
|
+
onOpenAutoFocus == null ? void 0 : onOpenAutoFocus(event);
|
|
15287
|
+
event.preventDefault();
|
|
15288
|
+
},
|
|
15289
|
+
onCloseAutoFocus: (event) => {
|
|
15290
|
+
onCloseAutoFocus == null ? void 0 : onCloseAutoFocus(event);
|
|
15291
|
+
event.preventDefault();
|
|
15292
|
+
},
|
|
15293
|
+
style: {
|
|
15294
|
+
left: `calc(50% - (var(${BODY_SCROLLBAR_WIDTH_CSS_VAR}, 0px) / 2))`,
|
|
15295
|
+
...style
|
|
15296
|
+
},
|
|
15297
|
+
className: cn$1(
|
|
15298
|
+
`panel-shell fixed left-[50%] top-[50%] ${OVERLAY_LAYER.dialogContent} grid w-full max-w-[36rem] translate-x-[-50%] translate-y-[-50%] gap-5 rounded-2xl p-7 shadow-lg`,
|
|
15299
|
+
"focus:outline-none",
|
|
15300
|
+
"data-[state=open]:animate-dialog-in data-[state=closed]:animate-dialog-out",
|
|
15301
|
+
"motion-reduce:animate-none",
|
|
15302
|
+
className
|
|
15303
|
+
),
|
|
15304
|
+
...props,
|
|
15305
|
+
children: [
|
|
15306
|
+
children,
|
|
15307
|
+
!hideCloseButton ? /* @__PURE__ */ jsxs(
|
|
15308
|
+
DialogClose$1,
|
|
15309
|
+
{
|
|
15310
|
+
className: cn$1(
|
|
15311
|
+
"absolute right-4 top-4 rounded-md border border-transparent bg-surface-2 p-1.5 text-text-tertiary",
|
|
15312
|
+
"transition-colors duration-fast",
|
|
15313
|
+
"hover:border-line-subtle hover:bg-surface-3 hover:text-text-primary",
|
|
15314
|
+
"focus-visible:outline-none focus-visible:shadow-focus"
|
|
15315
|
+
),
|
|
15316
|
+
children: [
|
|
15317
|
+
/* @__PURE__ */ jsx(X$1, { className: "h-4 w-4" }),
|
|
15318
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "닫기" })
|
|
15319
|
+
]
|
|
15320
|
+
}
|
|
15321
|
+
) : null
|
|
15322
|
+
]
|
|
15323
|
+
}
|
|
15324
|
+
)
|
|
15325
|
+
] });
|
|
15326
|
+
}
|
|
15327
|
+
);
|
|
15328
|
+
DialogContent.displayName = DialogContent$1.displayName;
|
|
15329
|
+
const DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", { className: cn$1("flex flex-col space-y-2 text-left", className), ...props });
|
|
15330
|
+
const DialogTitle = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
15331
|
+
DialogTitle$1,
|
|
15332
|
+
{
|
|
15333
|
+
ref,
|
|
15334
|
+
className: cn$1("text-lg font-semibold text-text-primary", className),
|
|
15335
|
+
...props
|
|
15336
|
+
}
|
|
15337
|
+
));
|
|
15338
|
+
DialogTitle.displayName = DialogTitle$1.displayName;
|
|
15339
|
+
const DialogDescription = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
15340
|
+
DialogDescription$1,
|
|
15341
|
+
{
|
|
15342
|
+
ref,
|
|
15343
|
+
className: cn$1("text-sm leading-relaxed text-text-secondary", className),
|
|
15344
|
+
...props
|
|
15345
|
+
}
|
|
15346
|
+
));
|
|
15347
|
+
DialogDescription.displayName = DialogDescription$1.displayName;
|
|
15348
|
+
const VIEWPORT_MARGIN = 12;
|
|
15349
|
+
const PREVIEW_STICKER_SIZE = 32;
|
|
15350
|
+
const STICKER_GAP = 8;
|
|
15351
|
+
const BUBBLE_OFFSET = PREVIEW_STICKER_SIZE / 2 + STICKER_GAP;
|
|
15352
|
+
const DISCARD_MESSAGE = "지금 닫으면 작성 중인 내용이 사라집니다. 닫으시겠습니까?";
|
|
15353
|
+
const DISCARD_CANCEL = "취소";
|
|
15354
|
+
const DISCARD_CONFIRM = "닫기";
|
|
15355
|
+
function clampBubblePosition(x, y, width, height, viewportWidth, viewportHeight, margin = VIEWPORT_MARGIN) {
|
|
15356
|
+
const maxLeft = Math.max(margin, viewportWidth - width - margin);
|
|
15357
|
+
const maxTop = Math.max(margin, viewportHeight - height - margin);
|
|
15358
|
+
return {
|
|
15359
|
+
left: Math.min(Math.max(x, margin), maxLeft),
|
|
15360
|
+
top: Math.min(Math.max(y, margin), maxTop)
|
|
15361
|
+
};
|
|
15362
|
+
}
|
|
15363
|
+
function resolveBubbleLayout(stickerLeft, stickerTop, width, height, viewportWidth, viewportHeight, margin = VIEWPORT_MARGIN) {
|
|
15364
|
+
const rightLeft = stickerLeft + BUBBLE_OFFSET;
|
|
15365
|
+
const leftLeft = stickerLeft - BUBBLE_OFFSET - width;
|
|
15366
|
+
const side = rightLeft + width <= viewportWidth - margin ? "right" : leftLeft >= margin ? "left" : "right";
|
|
15367
|
+
const { left, top } = clampBubblePosition(
|
|
15368
|
+
side === "right" ? rightLeft : leftLeft,
|
|
15369
|
+
stickerTop - PREVIEW_STICKER_SIZE / 2,
|
|
15370
|
+
width,
|
|
15371
|
+
height,
|
|
15372
|
+
viewportWidth,
|
|
15373
|
+
viewportHeight,
|
|
15374
|
+
margin
|
|
15375
|
+
);
|
|
15376
|
+
return { left, top, side };
|
|
15377
|
+
}
|
|
15378
|
+
function IssueIntentBubbleAnchor({
|
|
15379
|
+
x,
|
|
15380
|
+
y,
|
|
15381
|
+
children,
|
|
15382
|
+
onClose,
|
|
15383
|
+
dirty = false,
|
|
15384
|
+
open = true,
|
|
15385
|
+
onExitComplete
|
|
15386
|
+
}) {
|
|
15387
|
+
const contentRef = useRef(null);
|
|
15388
|
+
const onCloseRef = useRef(onClose);
|
|
15389
|
+
onCloseRef.current = onClose;
|
|
15390
|
+
const dirtyRef = useRef(dirty);
|
|
15391
|
+
dirtyRef.current = dirty;
|
|
15392
|
+
const [confirmingDiscard, setConfirmingDiscard] = useState(false);
|
|
15393
|
+
const discardCancelRef = useRef(null);
|
|
15394
|
+
const confirmingDiscardRef = useRef(confirmingDiscard);
|
|
15395
|
+
confirmingDiscardRef.current = confirmingDiscard;
|
|
15396
|
+
const [layout2, setLayout] = useState(null);
|
|
15397
|
+
const reduceMotion = useReducedMotion();
|
|
15398
|
+
useLayoutEffect(() => {
|
|
15399
|
+
let frame2 = 0;
|
|
15400
|
+
const apply = () => {
|
|
15401
|
+
frame2 = 0;
|
|
15402
|
+
const node2 = contentRef.current;
|
|
15403
|
+
if (!node2) return;
|
|
15404
|
+
const { width, height } = node2.getBoundingClientRect();
|
|
15405
|
+
const stickerLeft = x - window.scrollX;
|
|
15406
|
+
const stickerTop = y - window.scrollY;
|
|
15407
|
+
const placement = resolveBubbleLayout(
|
|
15408
|
+
stickerLeft,
|
|
15409
|
+
stickerTop,
|
|
15410
|
+
width,
|
|
15411
|
+
height,
|
|
15412
|
+
window.innerWidth,
|
|
15413
|
+
window.innerHeight
|
|
15414
|
+
);
|
|
15415
|
+
setLayout({
|
|
15416
|
+
stickerLeft,
|
|
15417
|
+
stickerTop,
|
|
15418
|
+
bubbleTop: placement.top,
|
|
15419
|
+
// 왼쪽에 열 때는 right 를 고정해야 너비가 스티커 쪽으로 자란다.
|
|
15420
|
+
bubbleLeft: placement.side === "right" ? placement.left : null,
|
|
15421
|
+
bubbleRight: placement.side === "right" ? null : window.innerWidth - (placement.left + width)
|
|
15422
|
+
});
|
|
15423
|
+
};
|
|
15424
|
+
const schedule = () => {
|
|
15425
|
+
if (frame2) return;
|
|
15426
|
+
frame2 = window.requestAnimationFrame(apply);
|
|
15427
|
+
};
|
|
15428
|
+
apply();
|
|
15429
|
+
window.addEventListener("scroll", schedule, { capture: true, passive: true });
|
|
15430
|
+
window.addEventListener("resize", schedule, { passive: true });
|
|
15431
|
+
let contentObserver = null;
|
|
15432
|
+
if (contentRef.current && typeof ResizeObserver !== "undefined") {
|
|
15433
|
+
contentObserver = new ResizeObserver(schedule);
|
|
15434
|
+
contentObserver.observe(contentRef.current);
|
|
15435
|
+
}
|
|
15436
|
+
return () => {
|
|
15437
|
+
window.removeEventListener("scroll", schedule, { capture: true });
|
|
15438
|
+
window.removeEventListener("resize", schedule);
|
|
15439
|
+
contentObserver == null ? void 0 : contentObserver.disconnect();
|
|
15440
|
+
if (frame2) window.cancelAnimationFrame(frame2);
|
|
15441
|
+
};
|
|
15442
|
+
}, [x, y]);
|
|
15443
|
+
useEffect(() => {
|
|
15444
|
+
const handleKeyDown = (event) => {
|
|
15445
|
+
if (event.key !== "Escape") return;
|
|
15446
|
+
event.preventDefault();
|
|
15447
|
+
if (confirmingDiscardRef.current) return;
|
|
15448
|
+
if (dirtyRef.current) {
|
|
15449
|
+
setConfirmingDiscard(true);
|
|
15450
|
+
return;
|
|
15451
|
+
}
|
|
15452
|
+
onCloseRef.current();
|
|
15453
|
+
};
|
|
15454
|
+
window.addEventListener("keydown", handleKeyDown, { capture: true });
|
|
15455
|
+
return () => window.removeEventListener("keydown", handleKeyDown, { capture: true });
|
|
15456
|
+
}, []);
|
|
15457
|
+
useDisableBodyScroll(confirmingDiscard);
|
|
15458
|
+
const visibility = layout2 ? "visible" : "hidden";
|
|
15459
|
+
const stickerTransition = reduceMotion ? { duration: 0 } : { duration: 0.18, ease: [0.34, 1.2, 0.64, 1] };
|
|
15460
|
+
const bubbleTransition = reduceMotion ? { duration: 0 } : { duration: 0.22, ease: [0.34, 1.2, 0.64, 1] };
|
|
15461
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
15462
|
+
/* @__PURE__ */ jsx(AnimatePresence, { onExitComplete, children: open ? [
|
|
15463
|
+
/* @__PURE__ */ jsx(
|
|
15464
|
+
motion.div,
|
|
15465
|
+
{
|
|
15466
|
+
"data-issue-sticker-preview": "",
|
|
15467
|
+
"aria-hidden": "true",
|
|
15468
|
+
className: `pointer-events-none fixed ${OVERLAY_LAYER.shell} flex h-8 w-8 items-center justify-center rounded-full border border-white/80 bg-brand text-sm font-black leading-none text-white`,
|
|
15469
|
+
style: {
|
|
15470
|
+
left: (layout2 == null ? void 0 : layout2.stickerLeft) ?? x,
|
|
15471
|
+
top: (layout2 == null ? void 0 : layout2.stickerTop) ?? y,
|
|
15472
|
+
visibility
|
|
15473
|
+
},
|
|
15474
|
+
initial: { opacity: 0, scale: 0.5, x: "-50%", y: "-50%" },
|
|
15475
|
+
animate: { opacity: 1, scale: 1, x: "-50%", y: "-50%" },
|
|
15476
|
+
exit: { opacity: 0, scale: 0.5, x: "-50%", y: "-50%" },
|
|
15477
|
+
transition: stickerTransition,
|
|
15478
|
+
children: "!"
|
|
15479
|
+
},
|
|
15480
|
+
"preview-sticker"
|
|
15481
|
+
),
|
|
15482
|
+
/* @__PURE__ */ jsx(
|
|
15483
|
+
motion.div,
|
|
15484
|
+
{
|
|
15485
|
+
"data-issue-sticker-bubble": "",
|
|
15486
|
+
className: `pointer-events-auto fixed overflow-hidden ${OVERLAY_LAYER.shell}`,
|
|
15487
|
+
style: {
|
|
15488
|
+
left: (layout2 == null ? void 0 : layout2.bubbleLeft) ?? void 0,
|
|
15489
|
+
right: (layout2 == null ? void 0 : layout2.bubbleRight) ?? void 0,
|
|
15490
|
+
top: (layout2 == null ? void 0 : layout2.bubbleTop) ?? y
|
|
15491
|
+
},
|
|
15492
|
+
initial: { width: 0, height: 0, opacity: 0 },
|
|
15493
|
+
animate: layout2 ? { width: "auto", height: "auto", opacity: 1 } : { width: 0, height: 0, opacity: 0 },
|
|
15494
|
+
exit: { width: 0, height: 0, opacity: 0 },
|
|
15495
|
+
transition: bubbleTransition,
|
|
15496
|
+
children: /* @__PURE__ */ jsx("div", { ref: contentRef, className: "w-max", children })
|
|
15497
|
+
},
|
|
15498
|
+
"bubble"
|
|
15499
|
+
)
|
|
15500
|
+
] : null }),
|
|
15501
|
+
/* @__PURE__ */ jsx(
|
|
15502
|
+
Dialog,
|
|
15503
|
+
{
|
|
15504
|
+
open: confirmingDiscard,
|
|
15505
|
+
onOpenChange: (nextOpen) => {
|
|
15506
|
+
if (!nextOpen) setConfirmingDiscard(false);
|
|
15507
|
+
},
|
|
15508
|
+
children: /* @__PURE__ */ jsxs(
|
|
15509
|
+
DialogContent,
|
|
15510
|
+
{
|
|
15511
|
+
hideCloseButton: true,
|
|
15512
|
+
initialFocusRef: discardCancelRef,
|
|
15513
|
+
"aria-describedby": void 0,
|
|
15514
|
+
className: "w-[calc(100vw-32px)] max-w-[360px] gap-4 rounded-md p-5",
|
|
15515
|
+
children: [
|
|
15516
|
+
/* @__PURE__ */ jsx(DialogTitle, { className: "text-sm font-normal leading-relaxed", children: DISCARD_MESSAGE }),
|
|
15517
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
15518
|
+
/* @__PURE__ */ jsx(
|
|
15519
|
+
Button,
|
|
15520
|
+
{
|
|
15521
|
+
ref: discardCancelRef,
|
|
15522
|
+
type: "button",
|
|
15523
|
+
variant: "secondary",
|
|
15524
|
+
size: "sm",
|
|
15525
|
+
onClick: () => setConfirmingDiscard(false),
|
|
15526
|
+
children: DISCARD_CANCEL
|
|
15527
|
+
}
|
|
15528
|
+
),
|
|
15529
|
+
/* @__PURE__ */ jsx(Button, { type: "button", size: "sm", onClick: () => onClose(), children: DISCARD_CONFIRM })
|
|
15530
|
+
] })
|
|
15531
|
+
]
|
|
15532
|
+
}
|
|
15533
|
+
)
|
|
15534
|
+
}
|
|
15535
|
+
)
|
|
15536
|
+
] });
|
|
15537
|
+
}
|
|
14652
15538
|
function useProject() {
|
|
14653
15539
|
const auth = useAuthContext();
|
|
14654
15540
|
return {
|
|
@@ -14831,6 +15717,12 @@ function useJiraConnectNudge(projectId, enabled) {
|
|
|
14831
15717
|
function useIssueFormState({ initialDescription } = {}) {
|
|
14832
15718
|
const [title, setTitle] = useState("");
|
|
14833
15719
|
const [description, setDescription] = useState(initialDescription || "");
|
|
15720
|
+
const [mode, setMode] = useState("ai");
|
|
15721
|
+
const [renderedMode, setRenderedMode] = useState("ai");
|
|
15722
|
+
const [intent, setIntent] = useState("");
|
|
15723
|
+
const modeRef = useRef(mode);
|
|
15724
|
+
modeRef.current = mode;
|
|
15725
|
+
const finishModeTransition = useCallback(() => setRenderedMode(modeRef.current), []);
|
|
14834
15726
|
useEffect(() => {
|
|
14835
15727
|
if (initialDescription !== void 0) {
|
|
14836
15728
|
setDescription(initialDescription);
|
|
@@ -14840,7 +15732,13 @@ function useIssueFormState({ initialDescription } = {}) {
|
|
|
14840
15732
|
title,
|
|
14841
15733
|
description,
|
|
14842
15734
|
setTitle,
|
|
14843
|
-
setDescription
|
|
15735
|
+
setDescription,
|
|
15736
|
+
mode,
|
|
15737
|
+
setMode,
|
|
15738
|
+
renderedMode,
|
|
15739
|
+
finishModeTransition,
|
|
15740
|
+
intent,
|
|
15741
|
+
setIntent
|
|
14844
15742
|
};
|
|
14845
15743
|
}
|
|
14846
15744
|
function useDebouncedMutation({
|
|
@@ -14979,6 +15877,12 @@ const SCREENSHOT_EXCLUDE_SELECTORS = [
|
|
|
14979
15877
|
'[data-issue-sticker-capture-ignore="true"]',
|
|
14980
15878
|
"#issue-sticker-host"
|
|
14981
15879
|
];
|
|
15880
|
+
const CAPTURE_START_DELAY_MS = 300;
|
|
15881
|
+
function waitForFormOpen() {
|
|
15882
|
+
return new Promise((resolve) => {
|
|
15883
|
+
setTimeout(resolve, CAPTURE_START_DELAY_MS);
|
|
15884
|
+
});
|
|
15885
|
+
}
|
|
14982
15886
|
function useClickPosition(isCreatingIssueRef, recordedVideoFile) {
|
|
14983
15887
|
const [clickPosition, setClickPosition] = useState(null);
|
|
14984
15888
|
const clickPositionRef = useRef(null);
|
|
@@ -15052,6 +15956,7 @@ function useClickPosition(isCreatingIssueRef, recordedVideoFile) {
|
|
|
15052
15956
|
}, [recordedVideoFile]);
|
|
15053
15957
|
useEffect(() => {
|
|
15054
15958
|
const captureScreenshotsInBackground = async (position2) => {
|
|
15959
|
+
await waitForFormOpen();
|
|
15055
15960
|
const [fullScreenshotDataUrl, elementScreenshotDataUrl] = await pauseNetworkLogCollectionWhile(
|
|
15056
15961
|
() => Promise.all([
|
|
15057
15962
|
captureScreenshot({ excludeSelectors: SCREENSHOT_EXCLUDE_SELECTORS }),
|
|
@@ -15213,6 +16118,14 @@ async function uploadSessionReplay(snapshot) {
|
|
|
15213
16118
|
return null;
|
|
15214
16119
|
}
|
|
15215
16120
|
}
|
|
16121
|
+
function assertSubmittable(data) {
|
|
16122
|
+
var _a, _b2, _c2;
|
|
16123
|
+
const hasIntent = Boolean((_a = data.intent) == null ? void 0 : _a.trim());
|
|
16124
|
+
const hasManual = Boolean((_b2 = data.title) == null ? void 0 : _b2.trim()) && Boolean((_c2 = data.description) == null ? void 0 : _c2.trim());
|
|
16125
|
+
if (!hasIntent && !hasManual) {
|
|
16126
|
+
throw new Error("제목과 설명을 입력하거나 기대한 동작을 적어주세요.");
|
|
16127
|
+
}
|
|
16128
|
+
}
|
|
15216
16129
|
function useIssueSubmission({
|
|
15217
16130
|
position: position2,
|
|
15218
16131
|
onSubmit,
|
|
@@ -15232,13 +16145,8 @@ function useIssueSubmission({
|
|
|
15232
16145
|
const { upload: uploadVideo2 } = useVideoUpload();
|
|
15233
16146
|
const createIssueMutation = useDebouncedMutation({
|
|
15234
16147
|
mutationFn: async (data) => {
|
|
15235
|
-
var _a, _b2;
|
|
15236
|
-
|
|
15237
|
-
throw new Error("제목을 입력해주세요.");
|
|
15238
|
-
}
|
|
15239
|
-
if (!data.description.trim()) {
|
|
15240
|
-
throw new Error("설명을 입력해주세요.");
|
|
15241
|
-
}
|
|
16148
|
+
var _a, _b2, _c2, _d2, _e2, _f2;
|
|
16149
|
+
assertSubmittable(data);
|
|
15242
16150
|
let screenshotUrls;
|
|
15243
16151
|
let videoUrl;
|
|
15244
16152
|
if (data.videoFile) {
|
|
@@ -15272,8 +16180,9 @@ function useIssueSubmission({
|
|
|
15272
16180
|
const currentUrl = getCurrentPageUrl();
|
|
15273
16181
|
const issueData = {
|
|
15274
16182
|
url: currentUrl,
|
|
15275
|
-
title: data.title.trim(),
|
|
15276
|
-
description: data.description.trim(),
|
|
16183
|
+
title: ((_b2 = data.title) == null ? void 0 : _b2.trim()) || void 0,
|
|
16184
|
+
description: ((_c2 = data.description) == null ? void 0 : _c2.trim()) || void 0,
|
|
16185
|
+
intent: ((_d2 = data.intent) == null ? void 0 : _d2.trim()) || void 0,
|
|
15277
16186
|
position: {
|
|
15278
16187
|
x: data.position.x,
|
|
15279
16188
|
y: data.position.y,
|
|
@@ -15294,10 +16203,10 @@ function useIssueSubmission({
|
|
|
15294
16203
|
// 비어 있는 값은 보내지 않는다 — 저장해봐야 값 없음과 같고 요청만 커진다.
|
|
15295
16204
|
custom_field_values: Object.entries(customFieldValues ?? {}).filter(([, value]) => value !== null && value !== void 0 && value !== "").map(([field_definition_id, value]) => ({ field_definition_id, value }))
|
|
15296
16205
|
};
|
|
15297
|
-
if (((
|
|
16206
|
+
if (((_e2 = issueData.custom_field_values) == null ? void 0 : _e2.length) === 0) {
|
|
15298
16207
|
delete issueData.custom_field_values;
|
|
15299
16208
|
}
|
|
15300
|
-
if (
|
|
16209
|
+
if (sessionReplay == null ? void 0 : sessionReplay.canAttach) {
|
|
15301
16210
|
try {
|
|
15302
16211
|
const snapshot = await sessionReplay.takeSnapshot();
|
|
15303
16212
|
if (snapshot) {
|
|
@@ -15336,6 +16245,9 @@ function useIssueSubmission({
|
|
|
15336
16245
|
toast.error(getIntegrationFailureMessage(service));
|
|
15337
16246
|
}
|
|
15338
16247
|
failedServicesRef.current = createdIssue.failed_services ?? [];
|
|
16248
|
+
if (((_f2 = data.intent) == null ? void 0 : _f2.trim()) && createdIssue.generation_status === "skipped") {
|
|
16249
|
+
toast.warning(ISSUE_DRAFT_DISABLED_MESSAGE);
|
|
16250
|
+
}
|
|
15339
16251
|
return createdIssue;
|
|
15340
16252
|
},
|
|
15341
16253
|
onSuccess: (createdIssue) => {
|
|
@@ -15352,11 +16264,12 @@ function useIssueSubmission({
|
|
|
15352
16264
|
}
|
|
15353
16265
|
});
|
|
15354
16266
|
const doSubmit = useCallback(
|
|
15355
|
-
async (title, description) => {
|
|
16267
|
+
async ({ title, description, intent }) => {
|
|
15356
16268
|
failedServicesRef.current = [];
|
|
15357
16269
|
await createIssueMutation.mutateAsync({
|
|
15358
16270
|
title,
|
|
15359
16271
|
description,
|
|
16272
|
+
intent,
|
|
15360
16273
|
position: position2,
|
|
15361
16274
|
jiraFields,
|
|
15362
16275
|
notionFields,
|
|
@@ -15390,12 +16303,6 @@ function useSessionReplayController() {
|
|
|
15390
16303
|
controller.destroy();
|
|
15391
16304
|
};
|
|
15392
16305
|
}, [controller]);
|
|
15393
|
-
const setPreferOn = useCallback(
|
|
15394
|
-
(next) => {
|
|
15395
|
-
controller.setEnabled(next);
|
|
15396
|
-
},
|
|
15397
|
-
[controller]
|
|
15398
|
-
);
|
|
15399
16306
|
const takeSnapshot = useCallback(async () => {
|
|
15400
16307
|
return controller.takeSnapshot();
|
|
15401
16308
|
}, [controller]);
|
|
@@ -15404,8 +16311,6 @@ function useSessionReplayController() {
|
|
|
15404
16311
|
return {
|
|
15405
16312
|
available: snapshot.available,
|
|
15406
16313
|
canAttach: snapshot.canAttach,
|
|
15407
|
-
preferOn: snapshot.enabled,
|
|
15408
|
-
setPreferOn,
|
|
15409
16314
|
takeSnapshot,
|
|
15410
16315
|
markReportStart,
|
|
15411
16316
|
clearReportStart
|
|
@@ -15414,9 +16319,6 @@ function useSessionReplayController() {
|
|
|
15414
16319
|
const DISABLED_CONTROLLER = {
|
|
15415
16320
|
available: false,
|
|
15416
16321
|
canAttach: false,
|
|
15417
|
-
preferOn: false,
|
|
15418
|
-
setPreferOn: () => {
|
|
15419
|
-
},
|
|
15420
16322
|
takeSnapshot: async () => null,
|
|
15421
16323
|
markReportStart: () => {
|
|
15422
16324
|
},
|
|
@@ -15737,12 +16639,7 @@ function IssueFormCustomFields({
|
|
|
15737
16639
|
queryKey: ["customFieldDefinitions", projectId],
|
|
15738
16640
|
queryFn: () => fetchCustomFieldDefinitions(projectId)
|
|
15739
16641
|
});
|
|
15740
|
-
|
|
15741
|
-
useEffect(() => {
|
|
15742
|
-
if (defaultsAppliedRef.current || fields.length === 0) return;
|
|
15743
|
-
defaultsAppliedRef.current = true;
|
|
15744
|
-
onDefaultsResolved(getCustomFieldDefaultValues(fields));
|
|
15745
|
-
}, [fields, onDefaultsResolved]);
|
|
16642
|
+
useCustomFieldDefaults(fields, onDefaultsResolved);
|
|
15746
16643
|
return /* @__PURE__ */ jsx(
|
|
15747
16644
|
CustomFieldsSection,
|
|
15748
16645
|
{
|
|
@@ -15754,12 +16651,57 @@ function IssueFormCustomFields({
|
|
|
15754
16651
|
}
|
|
15755
16652
|
);
|
|
15756
16653
|
}
|
|
16654
|
+
function useCustomFieldDefaults(fields, onDefaultsResolved) {
|
|
16655
|
+
const defaultsAppliedRef = useRef(false);
|
|
16656
|
+
useEffect(() => {
|
|
16657
|
+
if (defaultsAppliedRef.current || fields.length === 0) return;
|
|
16658
|
+
defaultsAppliedRef.current = true;
|
|
16659
|
+
onDefaultsResolved(getCustomFieldDefaultValues(fields));
|
|
16660
|
+
}, [fields, onDefaultsResolved]);
|
|
16661
|
+
}
|
|
16662
|
+
function IssueFormCustomFieldDefaults({
|
|
16663
|
+
projectId,
|
|
16664
|
+
onDefaultsResolved
|
|
16665
|
+
}) {
|
|
16666
|
+
const { data: fields } = useQuery({
|
|
16667
|
+
queryKey: ["customFieldDefinitions", projectId],
|
|
16668
|
+
queryFn: () => fetchCustomFieldDefinitions(projectId)
|
|
16669
|
+
});
|
|
16670
|
+
useCustomFieldDefaults(fields ?? [], onDefaultsResolved);
|
|
16671
|
+
return null;
|
|
16672
|
+
}
|
|
16673
|
+
const useIsomorphicLayoutEffect = typeof window === "undefined" ? React.useEffect : React.useLayoutEffect;
|
|
15757
16674
|
const Textarea = React.forwardRef(
|
|
15758
|
-
({ className, ...props }, ref) => {
|
|
16675
|
+
({ className, autoGrow, ...props }, ref) => {
|
|
16676
|
+
const innerRef = React.useRef(null);
|
|
16677
|
+
React.useImperativeHandle(ref, () => innerRef.current);
|
|
16678
|
+
const grow = React.useCallback(() => {
|
|
16679
|
+
const element2 = innerRef.current;
|
|
16680
|
+
if (!element2) return;
|
|
16681
|
+
const style = getComputedStyle(element2);
|
|
16682
|
+
const borders = (parseFloat(style.borderTopWidth) || 0) + (parseFloat(style.borderBottomWidth) || 0);
|
|
16683
|
+
element2.style.height = "0px";
|
|
16684
|
+
element2.style.height = `${element2.scrollHeight + borders}px`;
|
|
16685
|
+
}, []);
|
|
16686
|
+
useIsomorphicLayoutEffect(() => {
|
|
16687
|
+
if (autoGrow) grow();
|
|
16688
|
+
});
|
|
16689
|
+
useIsomorphicLayoutEffect(() => {
|
|
16690
|
+
const element2 = innerRef.current;
|
|
16691
|
+
if (!autoGrow || !element2 || typeof ResizeObserver === "undefined") return;
|
|
16692
|
+
let lastWidth = element2.clientWidth;
|
|
16693
|
+
const observer2 = new ResizeObserver(() => {
|
|
16694
|
+
if (element2.clientWidth === lastWidth) return;
|
|
16695
|
+
lastWidth = element2.clientWidth;
|
|
16696
|
+
grow();
|
|
16697
|
+
});
|
|
16698
|
+
observer2.observe(element2);
|
|
16699
|
+
return () => observer2.disconnect();
|
|
16700
|
+
}, [autoGrow, grow]);
|
|
15759
16701
|
return /* @__PURE__ */ jsx(
|
|
15760
16702
|
"textarea",
|
|
15761
16703
|
{
|
|
15762
|
-
ref,
|
|
16704
|
+
ref: innerRef,
|
|
15763
16705
|
className: cn$1(
|
|
15764
16706
|
"flex min-h-[108px] w-full rounded-md border border-line-subtle bg-surface-1 px-4 py-3 text-sm text-text-primary",
|
|
15765
16707
|
"transition-[border-color,outline-color,box-shadow,background-color,transform] duration-fast ease-out",
|
|
@@ -15768,6 +16710,7 @@ const Textarea = React.forwardRef(
|
|
|
15768
16710
|
"focus-visible:border-transparent focus-visible:outline-line-focus focus-visible:bg-surface-1",
|
|
15769
16711
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
15770
16712
|
"resize-y",
|
|
16713
|
+
autoGrow && "min-h-0 resize-none overflow-hidden",
|
|
15771
16714
|
className
|
|
15772
16715
|
),
|
|
15773
16716
|
...props
|
|
@@ -15803,12 +16746,57 @@ function IssueFormDescriptionField({
|
|
|
15803
16746
|
function IssueFormError({ error }) {
|
|
15804
16747
|
return /* @__PURE__ */ jsx("div", { className: "rounded-[18px] border border-danger/20 bg-danger-subtle px-4 py-3 text-sm text-danger", children: error });
|
|
15805
16748
|
}
|
|
15806
|
-
|
|
16749
|
+
const SWITCH_TO_AI = "AI 작성으로 돌아가기";
|
|
16750
|
+
function IssueFormHeader({ title, onClose, onSwitchToAi }) {
|
|
15807
16751
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
15808
16752
|
/* @__PURE__ */ jsx("h2", { className: "m-0 text-lg font-semibold text-text-primary", children: title }),
|
|
15809
|
-
/* @__PURE__ */
|
|
16753
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
16754
|
+
onSwitchToAi ? /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 240, skipDelayDuration: 120, disableHoverableContent: true, children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
16755
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
16756
|
+
Button,
|
|
16757
|
+
{
|
|
16758
|
+
type: "button",
|
|
16759
|
+
variant: "ghost",
|
|
16760
|
+
size: "icon",
|
|
16761
|
+
onClick: onSwitchToAi,
|
|
16762
|
+
"aria-label": SWITCH_TO_AI,
|
|
16763
|
+
children: /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4" })
|
|
16764
|
+
}
|
|
16765
|
+
) }),
|
|
16766
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "top", sideOffset: 10, children: SWITCH_TO_AI })
|
|
16767
|
+
] }) }) : null,
|
|
16768
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: onClose, "aria-label": "닫기", children: /* @__PURE__ */ jsx(X$1, { className: "h-4 w-4" }) })
|
|
16769
|
+
] })
|
|
15810
16770
|
] });
|
|
15811
16771
|
}
|
|
16772
|
+
function useDraftCapability() {
|
|
16773
|
+
const { data } = useQuery({
|
|
16774
|
+
queryKey: ["issueDraftCapability"],
|
|
16775
|
+
queryFn: () => apiClient.get("/api/sdk/issues/capabilities/draft")
|
|
16776
|
+
});
|
|
16777
|
+
const capability = data == null ? void 0 : data.draft.canGenerateDraft;
|
|
16778
|
+
return {
|
|
16779
|
+
/** 응답을 실제로 받았을 때만 true. 조회 전·실패·null 응답에서는 false다. */
|
|
16780
|
+
resolved: Boolean(capability),
|
|
16781
|
+
enabled: (capability == null ? void 0 : capability.enabled) ?? true,
|
|
16782
|
+
titlePatternMissing: (data == null ? void 0 : data.draft.titlePatternMissing) ?? false
|
|
16783
|
+
};
|
|
16784
|
+
}
|
|
16785
|
+
function IssueFormDraftCapabilityGate({
|
|
16786
|
+
onResolved
|
|
16787
|
+
}) {
|
|
16788
|
+
const { resolved, enabled, titlePatternMissing } = useDraftCapability();
|
|
16789
|
+
useEffect(() => {
|
|
16790
|
+
if (!resolved) {
|
|
16791
|
+
return;
|
|
16792
|
+
}
|
|
16793
|
+
onResolved({
|
|
16794
|
+
disabledReason: enabled ? null : ISSUE_DRAFT_DISABLED_MESSAGE,
|
|
16795
|
+
titlePatternMissing
|
|
16796
|
+
});
|
|
16797
|
+
}, [resolved, enabled, titlePatternMissing, onResolved]);
|
|
16798
|
+
return null;
|
|
16799
|
+
}
|
|
15812
16800
|
function useJiraFieldSearchConfig(projectId) {
|
|
15813
16801
|
return useMemo(
|
|
15814
16802
|
() => createJiraFieldSearchConfig(
|
|
@@ -15845,13 +16833,7 @@ function IssueFormJiraFields({
|
|
|
15845
16833
|
const { getAuthInfo } = useAuthInfo();
|
|
15846
16834
|
const { fields: fieldDefinitions, exposedFields } = useJiraIntegrationFields(integrationId);
|
|
15847
16835
|
const getSearchConfig = useJiraFieldSearchConfig(getAuthInfo().projectId);
|
|
15848
|
-
|
|
15849
|
-
useEffect(() => {
|
|
15850
|
-
if (!onDefaultsResolved) return;
|
|
15851
|
-
if (appliedForRef.current === integrationId) return;
|
|
15852
|
-
appliedForRef.current = integrationId;
|
|
15853
|
-
onDefaultsResolved(getJiraFieldDefaultValues(exposedFields, fieldDefinitions));
|
|
15854
|
-
}, [integrationId, exposedFields, fieldDefinitions, onDefaultsResolved]);
|
|
16836
|
+
useJiraFieldDefaults(integrationId, exposedFields, fieldDefinitions, onDefaultsResolved);
|
|
15855
16837
|
return /* @__PURE__ */ jsx(
|
|
15856
16838
|
JiraFormFieldsSection,
|
|
15857
16839
|
{
|
|
@@ -15864,6 +16846,36 @@ function IssueFormJiraFields({
|
|
|
15864
16846
|
}
|
|
15865
16847
|
);
|
|
15866
16848
|
}
|
|
16849
|
+
function useJiraFieldDefaults(integrationId, exposedFields, fieldDefinitions, onDefaultsResolved, ready = true) {
|
|
16850
|
+
const appliedForRef = useRef(null);
|
|
16851
|
+
useEffect(() => {
|
|
16852
|
+
if (!onDefaultsResolved || !ready) return;
|
|
16853
|
+
if (appliedForRef.current === integrationId) return;
|
|
16854
|
+
appliedForRef.current = integrationId;
|
|
16855
|
+
onDefaultsResolved(getJiraFieldDefaultValues(exposedFields, fieldDefinitions));
|
|
16856
|
+
}, [integrationId, exposedFields, fieldDefinitions, onDefaultsResolved, ready]);
|
|
16857
|
+
}
|
|
16858
|
+
function IssueFormJiraFieldDefaults({
|
|
16859
|
+
integrationId,
|
|
16860
|
+
onDefaultsResolved
|
|
16861
|
+
}) {
|
|
16862
|
+
const { data } = useQuery({
|
|
16863
|
+
queryKey: ["jiraIntegrationFields", integrationId],
|
|
16864
|
+
queryFn: () => jiraService.loadIntegrationFields(integrationId)
|
|
16865
|
+
});
|
|
16866
|
+
useEffect(() => {
|
|
16867
|
+
if (!data) return;
|
|
16868
|
+
jiraService.updateExposedFields(integrationId, data.exposedFields);
|
|
16869
|
+
}, [data, integrationId]);
|
|
16870
|
+
useJiraFieldDefaults(
|
|
16871
|
+
integrationId,
|
|
16872
|
+
data == null ? void 0 : data.exposedFields,
|
|
16873
|
+
data == null ? void 0 : data.fields,
|
|
16874
|
+
onDefaultsResolved,
|
|
16875
|
+
Boolean(data)
|
|
16876
|
+
);
|
|
16877
|
+
return null;
|
|
16878
|
+
}
|
|
15867
16879
|
function JiraConnectNudge({ onConnect, isConnecting }) {
|
|
15868
16880
|
return /* @__PURE__ */ jsx("div", { className: "rounded-[18px] border border-warning/20 bg-warning-subtle px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
15869
16881
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
@@ -15940,83 +16952,14 @@ function IssueFormTitleField({
|
|
|
15940
16952
|
)
|
|
15941
16953
|
] });
|
|
15942
16954
|
}
|
|
15943
|
-
function
|
|
15944
|
-
return cn$1(
|
|
15945
|
-
"relative inline-flex h-[22px] w-[38px] shrink-0 items-center rounded-full",
|
|
15946
|
-
"transition-colors duration-fast ease-out",
|
|
15947
|
-
checked ? "bg-brand" : "bg-line"
|
|
15948
|
-
);
|
|
15949
|
-
}
|
|
15950
|
-
function switchThumbClassName(checked) {
|
|
15951
|
-
return cn$1(
|
|
15952
|
-
"pointer-events-none block h-[18px] w-[18px] rounded-full bg-surface-1 shadow-sm",
|
|
15953
|
-
"transition-transform duration-fast ease-out",
|
|
15954
|
-
checked ? "translate-x-[18px]" : "translate-x-[2px]"
|
|
15955
|
-
);
|
|
15956
|
-
}
|
|
15957
|
-
const Switch = React.forwardRef(
|
|
15958
|
-
({ checked, onCheckedChange, disabled = false, id: id2, className, ...aria2 }, ref) => /* @__PURE__ */ jsx(
|
|
15959
|
-
"button",
|
|
15960
|
-
{
|
|
15961
|
-
ref,
|
|
15962
|
-
id: id2,
|
|
15963
|
-
type: "button",
|
|
15964
|
-
role: "switch",
|
|
15965
|
-
"aria-checked": checked,
|
|
15966
|
-
disabled,
|
|
15967
|
-
onClick: () => onCheckedChange(!checked),
|
|
15968
|
-
className: cn$1(
|
|
15969
|
-
switchTrackClassName(checked),
|
|
15970
|
-
"outline-none",
|
|
15971
|
-
"focus-visible:shadow-focus",
|
|
15972
|
-
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
15973
|
-
className
|
|
15974
|
-
),
|
|
15975
|
-
...aria2,
|
|
15976
|
-
children: /* @__PURE__ */ jsx("span", { className: switchThumbClassName(checked) })
|
|
15977
|
-
}
|
|
15978
|
-
)
|
|
15979
|
-
);
|
|
15980
|
-
Switch.displayName = "Switch";
|
|
15981
|
-
function SessionReplayAttachToggle({
|
|
16955
|
+
function SessionReplayLimitNotice({
|
|
15982
16956
|
available,
|
|
15983
|
-
canAttach
|
|
15984
|
-
preferOn,
|
|
15985
|
-
onPreferOnChange
|
|
16957
|
+
canAttach
|
|
15986
16958
|
}) {
|
|
15987
|
-
if (!available) {
|
|
16959
|
+
if (!available || canAttach) {
|
|
15988
16960
|
return null;
|
|
15989
16961
|
}
|
|
15990
|
-
return /* @__PURE__ */
|
|
15991
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
15992
|
-
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
15993
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "session-replay-attach", children: "세션 리플레이 첨부" }),
|
|
15994
|
-
/* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
15995
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
15996
|
-
"button",
|
|
15997
|
-
{
|
|
15998
|
-
type: "button",
|
|
15999
|
-
"aria-label": "세션 리플레이 첨부 안내",
|
|
16000
|
-
className: "text-text-tertiary",
|
|
16001
|
-
children: /* @__PURE__ */ jsx(CircleHelp, { className: "h-3.5 w-3.5" })
|
|
16002
|
-
}
|
|
16003
|
-
) }),
|
|
16004
|
-
/* @__PURE__ */ jsx(TooltipContent, { className: "max-w-[240px] whitespace-normal", children: "제출 직전 최근 2분 화면 조작을 함께 기록합니다. 입력값·민감 텍스트는 자동으로 마스킹됩니다." })
|
|
16005
|
-
] }) })
|
|
16006
|
-
] }),
|
|
16007
|
-
/* @__PURE__ */ jsx(
|
|
16008
|
-
Switch,
|
|
16009
|
-
{
|
|
16010
|
-
id: "session-replay-attach",
|
|
16011
|
-
checked: canAttach && preferOn,
|
|
16012
|
-
disabled: !canAttach,
|
|
16013
|
-
onCheckedChange: onPreferOnChange,
|
|
16014
|
-
"aria-label": "세션 리플레이 첨부"
|
|
16015
|
-
}
|
|
16016
|
-
)
|
|
16017
|
-
] }),
|
|
16018
|
-
!canAttach ? /* @__PURE__ */ jsx("p", { className: "text-xs text-warning", children: "이번 달 리플레이 한도를 모두 사용했습니다. 다음 달에 한도가 초기화되거나, 플랜을 업그레이드하면 다시 첨부할 수 있습니다." }) : null
|
|
16019
|
-
] });
|
|
16962
|
+
return /* @__PURE__ */ jsx("p", { className: "text-xs text-warning", children: "이번 달 리플레이 한도를 모두 사용했습니다. 다음 달에 한도가 초기화되거나, 플랜을 업그레이드하면 다시 첨부할 수 있습니다." });
|
|
16020
16963
|
}
|
|
16021
16964
|
function IssueForm({
|
|
16022
16965
|
position: position2,
|
|
@@ -16038,6 +16981,19 @@ function IssueForm({
|
|
|
16038
16981
|
const [jiraFields, setJiraFields] = useState(
|
|
16039
16982
|
{}
|
|
16040
16983
|
);
|
|
16984
|
+
const [draftDisabledReason, setDraftDisabledReason] = useState(null);
|
|
16985
|
+
const intentRef = useRef(formState.intent);
|
|
16986
|
+
intentRef.current = formState.intent;
|
|
16987
|
+
const setFormMode = formState.setMode;
|
|
16988
|
+
const handleDraftCapabilityResolved = useCallback(
|
|
16989
|
+
({ disabledReason }) => {
|
|
16990
|
+
setDraftDisabledReason(disabledReason);
|
|
16991
|
+
if (disabledReason && intentRef.current.trim().length === 0) {
|
|
16992
|
+
setFormMode("manual");
|
|
16993
|
+
}
|
|
16994
|
+
},
|
|
16995
|
+
[setFormMode]
|
|
16996
|
+
);
|
|
16041
16997
|
const [notionFields, setNotionFields] = useState({});
|
|
16042
16998
|
const [customFieldValues, setCustomFieldValues] = useState({});
|
|
16043
16999
|
const { selectedProjectId } = useProject();
|
|
@@ -16059,6 +17015,10 @@ function IssueForm({
|
|
|
16059
17015
|
const issueSubmission = useIssueSubmission({
|
|
16060
17016
|
position: position2,
|
|
16061
17017
|
onSubmit: (hasIntegrationFailure) => {
|
|
17018
|
+
if (formState.mode === "ai") {
|
|
17019
|
+
onSubmit(hasIntegrationFailure);
|
|
17020
|
+
return;
|
|
17021
|
+
}
|
|
16062
17022
|
submitSucceededRef.current = true;
|
|
16063
17023
|
hadIntegrationFailureRef.current = hasIntegrationFailure;
|
|
16064
17024
|
setIsOpen(false);
|
|
@@ -16071,8 +17031,9 @@ function IssueForm({
|
|
|
16071
17031
|
videoFile: position2.videoFile,
|
|
16072
17032
|
sessionReplay
|
|
16073
17033
|
});
|
|
16074
|
-
useDisableBodyScroll();
|
|
17034
|
+
useDisableBodyScroll(formState.mode !== "ai");
|
|
16075
17035
|
useEffect(() => {
|
|
17036
|
+
if (formState.mode === "ai") return;
|
|
16076
17037
|
const handleKeyDown = (e) => {
|
|
16077
17038
|
const path2 = e.composedPath();
|
|
16078
17039
|
const actualTarget = path2.find(
|
|
@@ -16099,22 +17060,86 @@ function IssueForm({
|
|
|
16099
17060
|
};
|
|
16100
17061
|
window.addEventListener("keydown", handleKeyDown, { passive: false });
|
|
16101
17062
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
16102
|
-
}, []);
|
|
17063
|
+
}, [formState.mode]);
|
|
17064
|
+
const submitCurrentMode = async () => {
|
|
17065
|
+
if (formState.mode === "ai") {
|
|
17066
|
+
await issueSubmission.submitIssue({ intent: formState.intent });
|
|
17067
|
+
} else {
|
|
17068
|
+
await issueSubmission.submitIssue({
|
|
17069
|
+
title: formState.title,
|
|
17070
|
+
description: formState.description
|
|
17071
|
+
});
|
|
17072
|
+
}
|
|
17073
|
+
};
|
|
16103
17074
|
const handleSubmit = async (e) => {
|
|
16104
17075
|
e.preventDefault();
|
|
16105
17076
|
e.stopPropagation();
|
|
16106
|
-
await
|
|
17077
|
+
await submitCurrentMode();
|
|
16107
17078
|
};
|
|
16108
|
-
const handleClose = () =>
|
|
17079
|
+
const handleClose = () => {
|
|
17080
|
+
if (formState.mode === "ai") {
|
|
17081
|
+
onCancel();
|
|
17082
|
+
return;
|
|
17083
|
+
}
|
|
17084
|
+
setIsOpen(false);
|
|
17085
|
+
};
|
|
17086
|
+
if (formState.renderedMode === "ai") {
|
|
17087
|
+
return createPortal(
|
|
17088
|
+
/* @__PURE__ */ jsxs(
|
|
17089
|
+
IssueIntentBubbleAnchor,
|
|
17090
|
+
{
|
|
17091
|
+
x: position2.x,
|
|
17092
|
+
y: position2.y,
|
|
17093
|
+
onClose: handleClose,
|
|
17094
|
+
dirty: formState.intent.trim().length > 0,
|
|
17095
|
+
open: formState.mode === "ai",
|
|
17096
|
+
onExitComplete: formState.finishModeTransition,
|
|
17097
|
+
children: [
|
|
17098
|
+
showJiraFields ? /* @__PURE__ */ jsx(QueryBoundary, { loadingFallback: null, onError: () => null, children: /* @__PURE__ */ jsx(
|
|
17099
|
+
IssueFormJiraFieldDefaults,
|
|
17100
|
+
{
|
|
17101
|
+
integrationId: jiraIntegrationId,
|
|
17102
|
+
onDefaultsResolved: (defaults) => setJiraFields((current) => ({ ...defaults, ...current }))
|
|
17103
|
+
}
|
|
17104
|
+
) }) : null,
|
|
17105
|
+
showCustomFields ? /* @__PURE__ */ jsx(QueryBoundary, { loadingFallback: null, onError: () => null, children: /* @__PURE__ */ jsx(
|
|
17106
|
+
IssueFormCustomFieldDefaults,
|
|
17107
|
+
{
|
|
17108
|
+
projectId: selectedProjectId,
|
|
17109
|
+
onDefaultsResolved: (defaults) => setCustomFieldValues((current) => ({ ...defaults, ...current }))
|
|
17110
|
+
}
|
|
17111
|
+
) }) : null,
|
|
17112
|
+
/* @__PURE__ */ jsx(QueryBoundary, { loadingFallback: null, onError: () => null, children: /* @__PURE__ */ jsx(IssueFormDraftCapabilityGate, { onResolved: handleDraftCapabilityResolved }) }),
|
|
17113
|
+
/* @__PURE__ */ jsx(
|
|
17114
|
+
IssueIntentBubble,
|
|
17115
|
+
{
|
|
17116
|
+
value: formState.intent,
|
|
17117
|
+
onChange: formState.setIntent,
|
|
17118
|
+
onSubmit: () => void submitCurrentMode(),
|
|
17119
|
+
onSwitchToManual: () => formState.setMode("manual"),
|
|
17120
|
+
submitting: issueSubmission.isSubmitting,
|
|
17121
|
+
error: issueSubmission.error
|
|
17122
|
+
}
|
|
17123
|
+
)
|
|
17124
|
+
]
|
|
17125
|
+
}
|
|
17126
|
+
),
|
|
17127
|
+
getPortalContainer()
|
|
17128
|
+
);
|
|
17129
|
+
}
|
|
16109
17130
|
return createPortal(
|
|
16110
17131
|
/* @__PURE__ */ jsx(
|
|
16111
17132
|
OverlayShell,
|
|
16112
17133
|
{
|
|
16113
|
-
open: isOpen,
|
|
17134
|
+
open: isOpen && formState.mode === "manual",
|
|
16114
17135
|
shellClassName: "h-auto max-h-[min(80vh,640px)] max-w-[480px]",
|
|
16115
17136
|
closeOnBackdropClick: false,
|
|
16116
17137
|
onClose: handleClose,
|
|
16117
17138
|
onExitComplete: () => {
|
|
17139
|
+
if (formState.mode === "ai") {
|
|
17140
|
+
formState.finishModeTransition();
|
|
17141
|
+
return;
|
|
17142
|
+
}
|
|
16118
17143
|
if (submitSucceededRef.current) {
|
|
16119
17144
|
onSubmit(hadIntegrationFailureRef.current);
|
|
16120
17145
|
} else {
|
|
@@ -16127,19 +17152,24 @@ function IssueForm({
|
|
|
16127
17152
|
className: `${ISSUE_STICKER_REPORT_FORM_CLASS} flex min-h-0 flex-1 flex-col`,
|
|
16128
17153
|
onSubmit: handleSubmit,
|
|
16129
17154
|
children: [
|
|
16130
|
-
/* @__PURE__ */ jsx("div", { className: "border-b border-line-subtle px-4 py-3", children: /* @__PURE__ */ jsx(
|
|
17155
|
+
/* @__PURE__ */ jsx("div", { className: "border-b border-line-subtle px-4 py-3", children: /* @__PURE__ */ jsx(
|
|
17156
|
+
IssueFormHeader,
|
|
17157
|
+
{
|
|
17158
|
+
title: "이슈 생성",
|
|
17159
|
+
onClose: handleClose,
|
|
17160
|
+
onSwitchToAi: draftDisabledReason ? void 0 : () => formState.setMode("ai")
|
|
17161
|
+
}
|
|
17162
|
+
) }),
|
|
16131
17163
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-h-0 space-y-3 overflow-y-auto px-4 py-3", children: [
|
|
17164
|
+
issueSubmission.error && /* @__PURE__ */ jsx(IssueFormError, { error: issueSubmission.error }),
|
|
16132
17165
|
hasJiraIntegration && jiraConnect.needsConnect ? /* @__PURE__ */ jsx(JiraConnectNudge, { onConnect: () => void jiraConnect.connect() }) : null,
|
|
16133
|
-
/* @__PURE__ */ jsx(
|
|
16134
|
-
|
|
17166
|
+
isMember ? /* @__PURE__ */ jsx(
|
|
17167
|
+
SessionReplayLimitNotice,
|
|
16135
17168
|
{
|
|
16136
17169
|
available: sessionReplay.available,
|
|
16137
|
-
canAttach: sessionReplay.canAttach
|
|
16138
|
-
preferOn: sessionReplay.preferOn,
|
|
16139
|
-
onPreferOnChange: sessionReplay.setPreferOn
|
|
17170
|
+
canAttach: sessionReplay.canAttach
|
|
16140
17171
|
}
|
|
16141
|
-
),
|
|
16142
|
-
issueSubmission.error && /* @__PURE__ */ jsx(IssueFormError, { error: issueSubmission.error }),
|
|
17172
|
+
) : null,
|
|
16143
17173
|
/* @__PURE__ */ jsx(
|
|
16144
17174
|
IssueFormTitleField,
|
|
16145
17175
|
{
|
|
@@ -16156,6 +17186,7 @@ function IssueForm({
|
|
|
16156
17186
|
disabled: issueSubmission.isSubmitting
|
|
16157
17187
|
}
|
|
16158
17188
|
),
|
|
17189
|
+
draftDisabledReason ? /* @__PURE__ */ jsx("p", { className: "text-xs text-text-secondary", children: draftDisabledReason }) : null,
|
|
16159
17190
|
showIntegrationSkeleton ? /* @__PURE__ */ jsx(IntegrationFieldsSkeleton, {}) : null,
|
|
16160
17191
|
showJiraFields ? /* @__PURE__ */ jsx(
|
|
16161
17192
|
QueryBoundary,
|
|
@@ -16288,6 +17319,11 @@ function getAdminIssueUrl(issueId) {
|
|
|
16288
17319
|
adminBaseUrl: readViteEnv("VITE_ADMIN_BASE_URL")
|
|
16289
17320
|
});
|
|
16290
17321
|
}
|
|
17322
|
+
function getAdminProjectTitlePatternUrl(projectId) {
|
|
17323
|
+
return buildAdminProjectTitlePatternUrl(projectId, {
|
|
17324
|
+
adminBaseUrl: readViteEnv("VITE_ADMIN_BASE_URL")
|
|
17325
|
+
});
|
|
17326
|
+
}
|
|
16291
17327
|
function BrowserInfo({ userAgent }) {
|
|
16292
17328
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
16293
17329
|
/* @__PURE__ */ jsx("h4", { className: "mb-2 text-base font-medium text-text-secondary", children: "브라우저 정보" }),
|
|
@@ -16324,7 +17360,7 @@ var Collapsible = React.forwardRef(
|
|
|
16324
17360
|
children: /* @__PURE__ */ jsx(
|
|
16325
17361
|
Primitive.div,
|
|
16326
17362
|
{
|
|
16327
|
-
"data-state": getState$
|
|
17363
|
+
"data-state": getState$1(open),
|
|
16328
17364
|
"data-disabled": disabled ? "" : void 0,
|
|
16329
17365
|
...collapsibleProps,
|
|
16330
17366
|
ref: forwardedRef
|
|
@@ -16335,18 +17371,18 @@ var Collapsible = React.forwardRef(
|
|
|
16335
17371
|
}
|
|
16336
17372
|
);
|
|
16337
17373
|
Collapsible.displayName = COLLAPSIBLE_NAME;
|
|
16338
|
-
var TRIGGER_NAME$
|
|
17374
|
+
var TRIGGER_NAME$2 = "CollapsibleTrigger";
|
|
16339
17375
|
var CollapsibleTrigger = React.forwardRef(
|
|
16340
17376
|
(props, forwardedRef) => {
|
|
16341
17377
|
const { __scopeCollapsible, ...triggerProps } = props;
|
|
16342
|
-
const context = useCollapsibleContext(TRIGGER_NAME$
|
|
17378
|
+
const context = useCollapsibleContext(TRIGGER_NAME$2, __scopeCollapsible);
|
|
16343
17379
|
return /* @__PURE__ */ jsx(
|
|
16344
17380
|
Primitive.button,
|
|
16345
17381
|
{
|
|
16346
17382
|
type: "button",
|
|
16347
17383
|
"aria-controls": context.open ? context.contentId : void 0,
|
|
16348
17384
|
"aria-expanded": context.open || false,
|
|
16349
|
-
"data-state": getState$
|
|
17385
|
+
"data-state": getState$1(context.open),
|
|
16350
17386
|
"data-disabled": context.disabled ? "" : void 0,
|
|
16351
17387
|
disabled: context.disabled,
|
|
16352
17388
|
...triggerProps,
|
|
@@ -16356,19 +17392,19 @@ var CollapsibleTrigger = React.forwardRef(
|
|
|
16356
17392
|
);
|
|
16357
17393
|
}
|
|
16358
17394
|
);
|
|
16359
|
-
CollapsibleTrigger.displayName = TRIGGER_NAME$
|
|
16360
|
-
var CONTENT_NAME$
|
|
17395
|
+
CollapsibleTrigger.displayName = TRIGGER_NAME$2;
|
|
17396
|
+
var CONTENT_NAME$2 = "CollapsibleContent";
|
|
16361
17397
|
var CollapsibleContent = React.forwardRef(
|
|
16362
17398
|
(props, forwardedRef) => {
|
|
16363
17399
|
const { forceMount, ...contentProps } = props;
|
|
16364
|
-
const context = useCollapsibleContext(CONTENT_NAME$
|
|
17400
|
+
const context = useCollapsibleContext(CONTENT_NAME$2, props.__scopeCollapsible);
|
|
16365
17401
|
return /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: ({ present }) => /* @__PURE__ */ jsx(CollapsibleContentImpl, { ...contentProps, ref: forwardedRef, present }) });
|
|
16366
17402
|
}
|
|
16367
17403
|
);
|
|
16368
|
-
CollapsibleContent.displayName = CONTENT_NAME$
|
|
17404
|
+
CollapsibleContent.displayName = CONTENT_NAME$2;
|
|
16369
17405
|
var CollapsibleContentImpl = React.forwardRef((props, forwardedRef) => {
|
|
16370
17406
|
const { __scopeCollapsible, present, children, ...contentProps } = props;
|
|
16371
|
-
const context = useCollapsibleContext(CONTENT_NAME$
|
|
17407
|
+
const context = useCollapsibleContext(CONTENT_NAME$2, __scopeCollapsible);
|
|
16372
17408
|
const [isPresent, setIsPresent] = React.useState(present);
|
|
16373
17409
|
const ref = React.useRef(null);
|
|
16374
17410
|
const composedRefs = useComposedRefs$1(forwardedRef, ref);
|
|
@@ -16405,7 +17441,7 @@ var CollapsibleContentImpl = React.forwardRef((props, forwardedRef) => {
|
|
|
16405
17441
|
return /* @__PURE__ */ jsx(
|
|
16406
17442
|
Primitive.div,
|
|
16407
17443
|
{
|
|
16408
|
-
"data-state": getState$
|
|
17444
|
+
"data-state": getState$1(context.open),
|
|
16409
17445
|
"data-disabled": context.disabled ? "" : void 0,
|
|
16410
17446
|
id: context.contentId,
|
|
16411
17447
|
hidden: !isOpen,
|
|
@@ -16420,7 +17456,7 @@ var CollapsibleContentImpl = React.forwardRef((props, forwardedRef) => {
|
|
|
16420
17456
|
}
|
|
16421
17457
|
);
|
|
16422
17458
|
});
|
|
16423
|
-
function getState$
|
|
17459
|
+
function getState$1(open) {
|
|
16424
17460
|
return open ? "open" : "closed";
|
|
16425
17461
|
}
|
|
16426
17462
|
var Root$1 = Collapsible;
|
|
@@ -16626,7 +17662,7 @@ var AccordionItem$1 = React.forwardRef(
|
|
|
16626
17662
|
Root$1,
|
|
16627
17663
|
{
|
|
16628
17664
|
"data-orientation": accordionContext.orientation,
|
|
16629
|
-
"data-state": getState
|
|
17665
|
+
"data-state": getState(open),
|
|
16630
17666
|
...collapsibleScope,
|
|
16631
17667
|
...accordionItemProps,
|
|
16632
17668
|
ref: forwardedRef,
|
|
@@ -16656,7 +17692,7 @@ var AccordionHeader = React.forwardRef(
|
|
|
16656
17692
|
Primitive.h3,
|
|
16657
17693
|
{
|
|
16658
17694
|
"data-orientation": accordionContext.orientation,
|
|
16659
|
-
"data-state": getState
|
|
17695
|
+
"data-state": getState(itemContext.open),
|
|
16660
17696
|
"data-disabled": itemContext.disabled ? "" : void 0,
|
|
16661
17697
|
...headerProps,
|
|
16662
17698
|
ref: forwardedRef
|
|
@@ -16665,13 +17701,13 @@ var AccordionHeader = React.forwardRef(
|
|
|
16665
17701
|
}
|
|
16666
17702
|
);
|
|
16667
17703
|
AccordionHeader.displayName = HEADER_NAME;
|
|
16668
|
-
var TRIGGER_NAME$
|
|
17704
|
+
var TRIGGER_NAME$1 = "AccordionTrigger";
|
|
16669
17705
|
var AccordionTrigger$1 = React.forwardRef(
|
|
16670
17706
|
(props, forwardedRef) => {
|
|
16671
17707
|
const { __scopeAccordion, ...triggerProps } = props;
|
|
16672
17708
|
const accordionContext = useAccordionContext(ACCORDION_NAME, __scopeAccordion);
|
|
16673
|
-
const itemContext = useAccordionItemContext(TRIGGER_NAME$
|
|
16674
|
-
const collapsibleContext = useAccordionCollapsibleContext(TRIGGER_NAME$
|
|
17709
|
+
const itemContext = useAccordionItemContext(TRIGGER_NAME$1, __scopeAccordion);
|
|
17710
|
+
const collapsibleContext = useAccordionCollapsibleContext(TRIGGER_NAME$1, __scopeAccordion);
|
|
16675
17711
|
const collapsibleScope = useCollapsibleScope(__scopeAccordion);
|
|
16676
17712
|
return /* @__PURE__ */ jsx(Collection$1.ItemSlot, { scope: __scopeAccordion, children: /* @__PURE__ */ jsx(
|
|
16677
17713
|
Trigger$1,
|
|
@@ -16686,13 +17722,13 @@ var AccordionTrigger$1 = React.forwardRef(
|
|
|
16686
17722
|
) });
|
|
16687
17723
|
}
|
|
16688
17724
|
);
|
|
16689
|
-
AccordionTrigger$1.displayName = TRIGGER_NAME$
|
|
16690
|
-
var CONTENT_NAME$
|
|
17725
|
+
AccordionTrigger$1.displayName = TRIGGER_NAME$1;
|
|
17726
|
+
var CONTENT_NAME$1 = "AccordionContent";
|
|
16691
17727
|
var AccordionContent$1 = React.forwardRef(
|
|
16692
17728
|
(props, forwardedRef) => {
|
|
16693
17729
|
const { __scopeAccordion, ...contentProps } = props;
|
|
16694
17730
|
const accordionContext = useAccordionContext(ACCORDION_NAME, __scopeAccordion);
|
|
16695
|
-
const itemContext = useAccordionItemContext(CONTENT_NAME$
|
|
17731
|
+
const itemContext = useAccordionItemContext(CONTENT_NAME$1, __scopeAccordion);
|
|
16696
17732
|
const collapsibleScope = useCollapsibleScope(__scopeAccordion);
|
|
16697
17733
|
return /* @__PURE__ */ jsx(
|
|
16698
17734
|
Content$1,
|
|
@@ -16712,8 +17748,8 @@ var AccordionContent$1 = React.forwardRef(
|
|
|
16712
17748
|
);
|
|
16713
17749
|
}
|
|
16714
17750
|
);
|
|
16715
|
-
AccordionContent$1.displayName = CONTENT_NAME$
|
|
16716
|
-
function getState
|
|
17751
|
+
AccordionContent$1.displayName = CONTENT_NAME$1;
|
|
17752
|
+
function getState(open) {
|
|
16717
17753
|
return open ? "open" : "closed";
|
|
16718
17754
|
}
|
|
16719
17755
|
var Root2$1 = Accordion$1;
|
|
@@ -17458,11 +18494,11 @@ var TabsList$1 = React.forwardRef(
|
|
|
17458
18494
|
}
|
|
17459
18495
|
);
|
|
17460
18496
|
TabsList$1.displayName = TAB_LIST_NAME;
|
|
17461
|
-
var TRIGGER_NAME
|
|
18497
|
+
var TRIGGER_NAME = "TabsTrigger";
|
|
17462
18498
|
var TabsTrigger$1 = React.forwardRef(
|
|
17463
18499
|
(props, forwardedRef) => {
|
|
17464
18500
|
const { __scopeTabs, value, disabled = false, ...triggerProps } = props;
|
|
17465
|
-
const context = useTabsContext(TRIGGER_NAME
|
|
18501
|
+
const context = useTabsContext(TRIGGER_NAME, __scopeTabs);
|
|
17466
18502
|
const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);
|
|
17467
18503
|
const triggerId = makeTriggerId(context.baseId, value);
|
|
17468
18504
|
const contentId = makeContentId(context.baseId, value);
|
|
@@ -17509,12 +18545,12 @@ var TabsTrigger$1 = React.forwardRef(
|
|
|
17509
18545
|
);
|
|
17510
18546
|
}
|
|
17511
18547
|
);
|
|
17512
|
-
TabsTrigger$1.displayName = TRIGGER_NAME
|
|
17513
|
-
var CONTENT_NAME
|
|
18548
|
+
TabsTrigger$1.displayName = TRIGGER_NAME;
|
|
18549
|
+
var CONTENT_NAME = "TabsContent";
|
|
17514
18550
|
var TabsContent$1 = React.forwardRef(
|
|
17515
18551
|
(props, forwardedRef) => {
|
|
17516
18552
|
const { __scopeTabs, value, forceMount, children, ...contentProps } = props;
|
|
17517
|
-
const context = useTabsContext(CONTENT_NAME
|
|
18553
|
+
const context = useTabsContext(CONTENT_NAME, __scopeTabs);
|
|
17518
18554
|
const triggerId = makeTriggerId(context.baseId, value);
|
|
17519
18555
|
const contentId = makeContentId(context.baseId, value);
|
|
17520
18556
|
const isSelected = value === context.value;
|
|
@@ -17544,7 +18580,7 @@ var TabsContent$1 = React.forwardRef(
|
|
|
17544
18580
|
) });
|
|
17545
18581
|
}
|
|
17546
18582
|
);
|
|
17547
|
-
TabsContent$1.displayName = CONTENT_NAME
|
|
18583
|
+
TabsContent$1.displayName = CONTENT_NAME;
|
|
17548
18584
|
function makeTriggerId(baseId, value) {
|
|
17549
18585
|
return `${baseId}-trigger-${value}`;
|
|
17550
18586
|
}
|
|
@@ -32944,343 +33980,6 @@ function IssueDescription({
|
|
|
32944
33980
|
error ? /* @__PURE__ */ jsx("p", { className: "text-xs text-danger", children: error }) : null
|
|
32945
33981
|
] });
|
|
32946
33982
|
}
|
|
32947
|
-
var DIALOG_NAME = "Dialog";
|
|
32948
|
-
var [createDialogContext] = createContextScope(DIALOG_NAME);
|
|
32949
|
-
var [DialogProvider, useDialogContext] = createDialogContext(DIALOG_NAME);
|
|
32950
|
-
var Dialog$1 = (props) => {
|
|
32951
|
-
const {
|
|
32952
|
-
__scopeDialog,
|
|
32953
|
-
children,
|
|
32954
|
-
open: openProp,
|
|
32955
|
-
defaultOpen,
|
|
32956
|
-
onOpenChange,
|
|
32957
|
-
modal = true
|
|
32958
|
-
} = props;
|
|
32959
|
-
const triggerRef = React.useRef(null);
|
|
32960
|
-
const contentRef = React.useRef(null);
|
|
32961
|
-
const [open, setOpen] = useControllableState({
|
|
32962
|
-
prop: openProp,
|
|
32963
|
-
defaultProp: defaultOpen ?? false,
|
|
32964
|
-
onChange: onOpenChange,
|
|
32965
|
-
caller: DIALOG_NAME
|
|
32966
|
-
});
|
|
32967
|
-
return /* @__PURE__ */ jsx(
|
|
32968
|
-
DialogProvider,
|
|
32969
|
-
{
|
|
32970
|
-
scope: __scopeDialog,
|
|
32971
|
-
triggerRef,
|
|
32972
|
-
contentRef,
|
|
32973
|
-
contentId: useId$1(),
|
|
32974
|
-
titleId: useId$1(),
|
|
32975
|
-
descriptionId: useId$1(),
|
|
32976
|
-
open,
|
|
32977
|
-
onOpenChange: setOpen,
|
|
32978
|
-
onOpenToggle: React.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
|
|
32979
|
-
modal,
|
|
32980
|
-
children
|
|
32981
|
-
}
|
|
32982
|
-
);
|
|
32983
|
-
};
|
|
32984
|
-
Dialog$1.displayName = DIALOG_NAME;
|
|
32985
|
-
var TRIGGER_NAME = "DialogTrigger";
|
|
32986
|
-
var DialogTrigger = React.forwardRef(
|
|
32987
|
-
(props, forwardedRef) => {
|
|
32988
|
-
const { __scopeDialog, ...triggerProps } = props;
|
|
32989
|
-
const context = useDialogContext(TRIGGER_NAME, __scopeDialog);
|
|
32990
|
-
const composedTriggerRef = useComposedRefs$1(forwardedRef, context.triggerRef);
|
|
32991
|
-
return /* @__PURE__ */ jsx(
|
|
32992
|
-
Primitive.button,
|
|
32993
|
-
{
|
|
32994
|
-
type: "button",
|
|
32995
|
-
"aria-haspopup": "dialog",
|
|
32996
|
-
"aria-expanded": context.open,
|
|
32997
|
-
"aria-controls": context.open ? context.contentId : void 0,
|
|
32998
|
-
"data-state": getState(context.open),
|
|
32999
|
-
...triggerProps,
|
|
33000
|
-
ref: composedTriggerRef,
|
|
33001
|
-
onClick: composeEventHandlers(props.onClick, context.onOpenToggle)
|
|
33002
|
-
}
|
|
33003
|
-
);
|
|
33004
|
-
}
|
|
33005
|
-
);
|
|
33006
|
-
DialogTrigger.displayName = TRIGGER_NAME;
|
|
33007
|
-
var PORTAL_NAME = "DialogPortal";
|
|
33008
|
-
var [PortalProvider, usePortalContext] = createDialogContext(PORTAL_NAME, {
|
|
33009
|
-
forceMount: void 0
|
|
33010
|
-
});
|
|
33011
|
-
var DialogPortal$1 = (props) => {
|
|
33012
|
-
const { __scopeDialog, forceMount, children, container } = props;
|
|
33013
|
-
const context = useDialogContext(PORTAL_NAME, __scopeDialog);
|
|
33014
|
-
return /* @__PURE__ */ jsx(PortalProvider, { scope: __scopeDialog, forceMount, children: React.Children.map(children, (child) => /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: /* @__PURE__ */ jsx(Portal$1, { asChild: true, container, children: child }) })) });
|
|
33015
|
-
};
|
|
33016
|
-
DialogPortal$1.displayName = PORTAL_NAME;
|
|
33017
|
-
var OVERLAY_NAME = "DialogOverlay";
|
|
33018
|
-
var DialogOverlay$1 = React.forwardRef(
|
|
33019
|
-
(props, forwardedRef) => {
|
|
33020
|
-
const portalContext = usePortalContext(OVERLAY_NAME, props.__scopeDialog);
|
|
33021
|
-
const { forceMount = portalContext.forceMount, ...overlayProps } = props;
|
|
33022
|
-
const context = useDialogContext(OVERLAY_NAME, props.__scopeDialog);
|
|
33023
|
-
return context.modal ? /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: /* @__PURE__ */ jsx(DialogOverlayImpl, { ...overlayProps, ref: forwardedRef }) }) : null;
|
|
33024
|
-
}
|
|
33025
|
-
);
|
|
33026
|
-
DialogOverlay$1.displayName = OVERLAY_NAME;
|
|
33027
|
-
var Slot = createSlot("DialogOverlay.RemoveScroll");
|
|
33028
|
-
var DialogOverlayImpl = React.forwardRef(
|
|
33029
|
-
(props, forwardedRef) => {
|
|
33030
|
-
const { __scopeDialog, ...overlayProps } = props;
|
|
33031
|
-
const context = useDialogContext(OVERLAY_NAME, __scopeDialog);
|
|
33032
|
-
const registerDismissableSurface = useDismissableLayerSurface();
|
|
33033
|
-
const composedRefs = useComposedRefs$1(forwardedRef, registerDismissableSurface);
|
|
33034
|
-
return (
|
|
33035
|
-
// Make sure `Content` is scrollable even when it doesn't live inside `RemoveScroll`
|
|
33036
|
-
// ie. when `Overlay` and `Content` are siblings
|
|
33037
|
-
/* @__PURE__ */ jsx(ReactRemoveScroll, { as: Slot, allowPinchZoom: true, shards: [context.contentRef], children: /* @__PURE__ */ jsx(
|
|
33038
|
-
Primitive.div,
|
|
33039
|
-
{
|
|
33040
|
-
"data-state": getState(context.open),
|
|
33041
|
-
...overlayProps,
|
|
33042
|
-
ref: composedRefs,
|
|
33043
|
-
style: { pointerEvents: "auto", ...overlayProps.style }
|
|
33044
|
-
}
|
|
33045
|
-
) })
|
|
33046
|
-
);
|
|
33047
|
-
}
|
|
33048
|
-
);
|
|
33049
|
-
var CONTENT_NAME = "DialogContent";
|
|
33050
|
-
var DialogContent$1 = React.forwardRef(
|
|
33051
|
-
(props, forwardedRef) => {
|
|
33052
|
-
const portalContext = usePortalContext(CONTENT_NAME, props.__scopeDialog);
|
|
33053
|
-
const { forceMount = portalContext.forceMount, ...contentProps } = props;
|
|
33054
|
-
const context = useDialogContext(CONTENT_NAME, props.__scopeDialog);
|
|
33055
|
-
return /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: context.modal ? /* @__PURE__ */ jsx(DialogContentModal, { ...contentProps, ref: forwardedRef }) : /* @__PURE__ */ jsx(DialogContentNonModal, { ...contentProps, ref: forwardedRef }) });
|
|
33056
|
-
}
|
|
33057
|
-
);
|
|
33058
|
-
DialogContent$1.displayName = CONTENT_NAME;
|
|
33059
|
-
var DialogContentModal = React.forwardRef(
|
|
33060
|
-
(props, forwardedRef) => {
|
|
33061
|
-
const context = useDialogContext(CONTENT_NAME, props.__scopeDialog);
|
|
33062
|
-
const contentRef = React.useRef(null);
|
|
33063
|
-
const composedRefs = useComposedRefs$1(forwardedRef, context.contentRef, contentRef);
|
|
33064
|
-
React.useEffect(() => {
|
|
33065
|
-
const content2 = contentRef.current;
|
|
33066
|
-
if (content2) return hideOthers(content2);
|
|
33067
|
-
}, []);
|
|
33068
|
-
return /* @__PURE__ */ jsx(
|
|
33069
|
-
DialogContentImpl,
|
|
33070
|
-
{
|
|
33071
|
-
...props,
|
|
33072
|
-
ref: composedRefs,
|
|
33073
|
-
trapFocus: context.open,
|
|
33074
|
-
disableOutsidePointerEvents: context.open,
|
|
33075
|
-
onCloseAutoFocus: composeEventHandlers(props.onCloseAutoFocus, (event) => {
|
|
33076
|
-
var _a;
|
|
33077
|
-
event.preventDefault();
|
|
33078
|
-
(_a = context.triggerRef.current) == null ? void 0 : _a.focus();
|
|
33079
|
-
}),
|
|
33080
|
-
onPointerDownOutside: composeEventHandlers(props.onPointerDownOutside, (event) => {
|
|
33081
|
-
const originalEvent = event.detail.originalEvent;
|
|
33082
|
-
const ctrlLeftClick = originalEvent.button === 0 && originalEvent.ctrlKey === true;
|
|
33083
|
-
const isRightClick = originalEvent.button === 2 || ctrlLeftClick;
|
|
33084
|
-
if (isRightClick) event.preventDefault();
|
|
33085
|
-
}),
|
|
33086
|
-
onFocusOutside: composeEventHandlers(
|
|
33087
|
-
props.onFocusOutside,
|
|
33088
|
-
(event) => event.preventDefault()
|
|
33089
|
-
)
|
|
33090
|
-
}
|
|
33091
|
-
);
|
|
33092
|
-
}
|
|
33093
|
-
);
|
|
33094
|
-
var DialogContentNonModal = React.forwardRef(
|
|
33095
|
-
(props, forwardedRef) => {
|
|
33096
|
-
const context = useDialogContext(CONTENT_NAME, props.__scopeDialog);
|
|
33097
|
-
const hasInteractedOutsideRef = React.useRef(false);
|
|
33098
|
-
const hasPointerDownOutsideRef = React.useRef(false);
|
|
33099
|
-
return /* @__PURE__ */ jsx(
|
|
33100
|
-
DialogContentImpl,
|
|
33101
|
-
{
|
|
33102
|
-
...props,
|
|
33103
|
-
ref: forwardedRef,
|
|
33104
|
-
trapFocus: false,
|
|
33105
|
-
disableOutsidePointerEvents: false,
|
|
33106
|
-
onCloseAutoFocus: (event) => {
|
|
33107
|
-
var _a, _b2;
|
|
33108
|
-
(_a = props.onCloseAutoFocus) == null ? void 0 : _a.call(props, event);
|
|
33109
|
-
if (!event.defaultPrevented) {
|
|
33110
|
-
if (!hasInteractedOutsideRef.current) (_b2 = context.triggerRef.current) == null ? void 0 : _b2.focus();
|
|
33111
|
-
event.preventDefault();
|
|
33112
|
-
}
|
|
33113
|
-
hasInteractedOutsideRef.current = false;
|
|
33114
|
-
hasPointerDownOutsideRef.current = false;
|
|
33115
|
-
},
|
|
33116
|
-
onInteractOutside: (event) => {
|
|
33117
|
-
var _a, _b2;
|
|
33118
|
-
(_a = props.onInteractOutside) == null ? void 0 : _a.call(props, event);
|
|
33119
|
-
if (!event.defaultPrevented) {
|
|
33120
|
-
hasInteractedOutsideRef.current = true;
|
|
33121
|
-
if (event.detail.originalEvent.type === "pointerdown") {
|
|
33122
|
-
hasPointerDownOutsideRef.current = true;
|
|
33123
|
-
}
|
|
33124
|
-
}
|
|
33125
|
-
const target = event.target;
|
|
33126
|
-
const targetIsTrigger = (_b2 = context.triggerRef.current) == null ? void 0 : _b2.contains(target);
|
|
33127
|
-
if (targetIsTrigger) event.preventDefault();
|
|
33128
|
-
if (event.detail.originalEvent.type === "focusin" && hasPointerDownOutsideRef.current) {
|
|
33129
|
-
event.preventDefault();
|
|
33130
|
-
}
|
|
33131
|
-
}
|
|
33132
|
-
}
|
|
33133
|
-
);
|
|
33134
|
-
}
|
|
33135
|
-
);
|
|
33136
|
-
var DialogContentImpl = React.forwardRef(
|
|
33137
|
-
(props, forwardedRef) => {
|
|
33138
|
-
const { __scopeDialog, trapFocus, onOpenAutoFocus, onCloseAutoFocus, ...contentProps } = props;
|
|
33139
|
-
const context = useDialogContext(CONTENT_NAME, __scopeDialog);
|
|
33140
|
-
useFocusGuards();
|
|
33141
|
-
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
|
|
33142
|
-
FocusScope,
|
|
33143
|
-
{
|
|
33144
|
-
asChild: true,
|
|
33145
|
-
loop: true,
|
|
33146
|
-
trapped: trapFocus,
|
|
33147
|
-
onMountAutoFocus: onOpenAutoFocus,
|
|
33148
|
-
onUnmountAutoFocus: onCloseAutoFocus,
|
|
33149
|
-
children: /* @__PURE__ */ jsx(
|
|
33150
|
-
DismissableLayer,
|
|
33151
|
-
{
|
|
33152
|
-
role: "dialog",
|
|
33153
|
-
id: context.contentId,
|
|
33154
|
-
"aria-describedby": context.descriptionId,
|
|
33155
|
-
"aria-labelledby": context.titleId,
|
|
33156
|
-
"data-state": getState(context.open),
|
|
33157
|
-
...contentProps,
|
|
33158
|
-
ref: forwardedRef,
|
|
33159
|
-
deferPointerDownOutside: true,
|
|
33160
|
-
onDismiss: () => context.onOpenChange(false)
|
|
33161
|
-
}
|
|
33162
|
-
)
|
|
33163
|
-
}
|
|
33164
|
-
) });
|
|
33165
|
-
}
|
|
33166
|
-
);
|
|
33167
|
-
var TITLE_NAME = "DialogTitle";
|
|
33168
|
-
var DialogTitle$1 = React.forwardRef(
|
|
33169
|
-
(props, forwardedRef) => {
|
|
33170
|
-
const { __scopeDialog, ...titleProps } = props;
|
|
33171
|
-
const context = useDialogContext(TITLE_NAME, __scopeDialog);
|
|
33172
|
-
return /* @__PURE__ */ jsx(Primitive.h2, { id: context.titleId, ...titleProps, ref: forwardedRef });
|
|
33173
|
-
}
|
|
33174
|
-
);
|
|
33175
|
-
DialogTitle$1.displayName = TITLE_NAME;
|
|
33176
|
-
var DESCRIPTION_NAME = "DialogDescription";
|
|
33177
|
-
var DialogDescription$1 = React.forwardRef(
|
|
33178
|
-
(props, forwardedRef) => {
|
|
33179
|
-
const { __scopeDialog, ...descriptionProps } = props;
|
|
33180
|
-
const context = useDialogContext(DESCRIPTION_NAME, __scopeDialog);
|
|
33181
|
-
return /* @__PURE__ */ jsx(Primitive.p, { id: context.descriptionId, ...descriptionProps, ref: forwardedRef });
|
|
33182
|
-
}
|
|
33183
|
-
);
|
|
33184
|
-
DialogDescription$1.displayName = DESCRIPTION_NAME;
|
|
33185
|
-
var CLOSE_NAME = "DialogClose";
|
|
33186
|
-
var DialogClose$1 = React.forwardRef(
|
|
33187
|
-
(props, forwardedRef) => {
|
|
33188
|
-
const { __scopeDialog, ...closeProps } = props;
|
|
33189
|
-
const context = useDialogContext(CLOSE_NAME, __scopeDialog);
|
|
33190
|
-
return /* @__PURE__ */ jsx(
|
|
33191
|
-
Primitive.button,
|
|
33192
|
-
{
|
|
33193
|
-
type: "button",
|
|
33194
|
-
...closeProps,
|
|
33195
|
-
ref: forwardedRef,
|
|
33196
|
-
onClick: composeEventHandlers(props.onClick, () => context.onOpenChange(false))
|
|
33197
|
-
}
|
|
33198
|
-
);
|
|
33199
|
-
}
|
|
33200
|
-
);
|
|
33201
|
-
DialogClose$1.displayName = CLOSE_NAME;
|
|
33202
|
-
function getState(open) {
|
|
33203
|
-
return open ? "open" : "closed";
|
|
33204
|
-
}
|
|
33205
|
-
const Dialog = Dialog$1;
|
|
33206
|
-
const DialogClose = DialogClose$1;
|
|
33207
|
-
const DialogPortal = ({ children }) => {
|
|
33208
|
-
const portalContainer = usePortalContainer();
|
|
33209
|
-
return /* @__PURE__ */ jsx(DialogPortal$1, { container: portalContainer, children });
|
|
33210
|
-
};
|
|
33211
|
-
const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
33212
|
-
DialogOverlay$1,
|
|
33213
|
-
{
|
|
33214
|
-
ref,
|
|
33215
|
-
className: cn$1(
|
|
33216
|
-
`fixed inset-0 ${OVERLAY_LAYER.dialogOverlay} bg-black/24`,
|
|
33217
|
-
"data-[state=open]:animate-fade-in data-[state=closed]:animate-fade-out",
|
|
33218
|
-
"motion-reduce:animate-none",
|
|
33219
|
-
className
|
|
33220
|
-
),
|
|
33221
|
-
...props
|
|
33222
|
-
}
|
|
33223
|
-
));
|
|
33224
|
-
DialogOverlay.displayName = DialogOverlay$1.displayName;
|
|
33225
|
-
const DialogContent = React.forwardRef(({ className, children, hideCloseButton = false, onEscapeKeyDown, style, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
|
|
33226
|
-
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
33227
|
-
/* @__PURE__ */ jsxs(
|
|
33228
|
-
DialogContent$1,
|
|
33229
|
-
{
|
|
33230
|
-
ref,
|
|
33231
|
-
onEscapeKeyDown: stopEscapePropagation(onEscapeKeyDown),
|
|
33232
|
-
style: {
|
|
33233
|
-
left: `calc(50% - (var(${BODY_SCROLLBAR_WIDTH_CSS_VAR}, 0px) / 2))`,
|
|
33234
|
-
...style
|
|
33235
|
-
},
|
|
33236
|
-
className: cn$1(
|
|
33237
|
-
`panel-shell fixed left-[50%] top-[50%] ${OVERLAY_LAYER.dialogContent} grid w-full max-w-[36rem] translate-x-[-50%] translate-y-[-50%] gap-5 rounded-2xl p-7 shadow-lg`,
|
|
33238
|
-
"data-[state=open]:animate-dialog-in data-[state=closed]:animate-dialog-out",
|
|
33239
|
-
"motion-reduce:animate-none",
|
|
33240
|
-
className
|
|
33241
|
-
),
|
|
33242
|
-
...props,
|
|
33243
|
-
children: [
|
|
33244
|
-
children,
|
|
33245
|
-
!hideCloseButton ? /* @__PURE__ */ jsxs(
|
|
33246
|
-
DialogClose$1,
|
|
33247
|
-
{
|
|
33248
|
-
className: cn$1(
|
|
33249
|
-
"absolute right-4 top-4 rounded-md border border-transparent bg-surface-2 p-1.5 text-text-tertiary",
|
|
33250
|
-
"transition-colors duration-fast",
|
|
33251
|
-
"hover:border-line-subtle hover:bg-surface-3 hover:text-text-primary",
|
|
33252
|
-
"focus-visible:outline-none focus-visible:shadow-focus"
|
|
33253
|
-
),
|
|
33254
|
-
children: [
|
|
33255
|
-
/* @__PURE__ */ jsx(X$1, { className: "h-4 w-4" }),
|
|
33256
|
-
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "닫기" })
|
|
33257
|
-
]
|
|
33258
|
-
}
|
|
33259
|
-
) : null
|
|
33260
|
-
]
|
|
33261
|
-
}
|
|
33262
|
-
)
|
|
33263
|
-
] }));
|
|
33264
|
-
DialogContent.displayName = DialogContent$1.displayName;
|
|
33265
|
-
const DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", { className: cn$1("flex flex-col space-y-2 text-left", className), ...props });
|
|
33266
|
-
const DialogTitle = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
33267
|
-
DialogTitle$1,
|
|
33268
|
-
{
|
|
33269
|
-
ref,
|
|
33270
|
-
className: cn$1("text-lg font-semibold text-text-primary", className),
|
|
33271
|
-
...props
|
|
33272
|
-
}
|
|
33273
|
-
));
|
|
33274
|
-
DialogTitle.displayName = DialogTitle$1.displayName;
|
|
33275
|
-
const DialogDescription = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
33276
|
-
DialogDescription$1,
|
|
33277
|
-
{
|
|
33278
|
-
ref,
|
|
33279
|
-
className: cn$1("text-sm leading-relaxed text-text-secondary", className),
|
|
33280
|
-
...props
|
|
33281
|
-
}
|
|
33282
|
-
));
|
|
33283
|
-
DialogDescription.displayName = DialogDescription$1.displayName;
|
|
33284
33983
|
function getServiceDisplayName(serviceType) {
|
|
33285
33984
|
switch (serviceType) {
|
|
33286
33985
|
case "JIRA":
|
|
@@ -33470,13 +34169,78 @@ function IssueDeleteActions({
|
|
|
33470
34169
|
}
|
|
33471
34170
|
);
|
|
33472
34171
|
}
|
|
34172
|
+
function IssueGenerationFailure$1({
|
|
34173
|
+
isRegenerating,
|
|
34174
|
+
regenerate
|
|
34175
|
+
}) {
|
|
34176
|
+
const handleClick = async () => {
|
|
34177
|
+
const success = await regenerate();
|
|
34178
|
+
if (!success) {
|
|
34179
|
+
toast.error("재생성에 실패했습니다. 잠시 후 다시 시도해주세요.");
|
|
34180
|
+
}
|
|
34181
|
+
};
|
|
34182
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 text-sm", children: [
|
|
34183
|
+
/* @__PURE__ */ jsx("span", { className: "text-danger", children: "제목과 설명을 생성하지 못했습니다. 입력한 내용은 그대로 남아 있습니다." }),
|
|
34184
|
+
/* @__PURE__ */ jsx(
|
|
34185
|
+
Button,
|
|
34186
|
+
{
|
|
34187
|
+
type: "button",
|
|
34188
|
+
variant: "outline",
|
|
34189
|
+
size: "sm",
|
|
34190
|
+
onClick: handleClick,
|
|
34191
|
+
disabled: isRegenerating,
|
|
34192
|
+
children: "다시 생성"
|
|
34193
|
+
}
|
|
34194
|
+
)
|
|
34195
|
+
] });
|
|
34196
|
+
}
|
|
34197
|
+
function useRegenerateIssue(issueId, onUpdated) {
|
|
34198
|
+
const [isRegenerating, setIsRegenerating] = useState(false);
|
|
34199
|
+
const regenerate = useCallback(async () => {
|
|
34200
|
+
setIsRegenerating(true);
|
|
34201
|
+
try {
|
|
34202
|
+
await issueService.regenerateIssue(issueId);
|
|
34203
|
+
applyIssueMutationEffects(issueId);
|
|
34204
|
+
onUpdated == null ? void 0 : onUpdated();
|
|
34205
|
+
return true;
|
|
34206
|
+
} catch {
|
|
34207
|
+
return false;
|
|
34208
|
+
} finally {
|
|
34209
|
+
setIsRegenerating(false);
|
|
34210
|
+
}
|
|
34211
|
+
}, [issueId, onUpdated]);
|
|
34212
|
+
return { isRegenerating, regenerate };
|
|
34213
|
+
}
|
|
34214
|
+
function IssueGenerationFailure({
|
|
34215
|
+
issueId,
|
|
34216
|
+
onUpdated
|
|
34217
|
+
}) {
|
|
34218
|
+
const { isRegenerating, regenerate } = useRegenerateIssue(issueId, onUpdated);
|
|
34219
|
+
return /* @__PURE__ */ jsx(IssueGenerationFailure$1, { isRegenerating, regenerate });
|
|
34220
|
+
}
|
|
34221
|
+
function IssueGenerationBadge({ status }) {
|
|
34222
|
+
if (status === "pending" || status === "processing") {
|
|
34223
|
+
return /* @__PURE__ */ jsxs("span", { className: "inline-flex shrink-0 items-center gap-1.5 rounded-md bg-warning-subtle px-2 py-0.5 text-2xs font-medium text-warning", children: [
|
|
34224
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-md bg-warning" }),
|
|
34225
|
+
"제목 생성 중"
|
|
34226
|
+
] });
|
|
34227
|
+
}
|
|
34228
|
+
if (status === "failed") {
|
|
34229
|
+
return /* @__PURE__ */ jsxs("span", { className: "inline-flex shrink-0 items-center gap-1.5 rounded-md bg-danger-subtle px-2 py-0.5 text-2xs font-medium text-danger", children: [
|
|
34230
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-md bg-danger" }),
|
|
34231
|
+
"제목 생성 실패"
|
|
34232
|
+
] });
|
|
34233
|
+
}
|
|
34234
|
+
return null;
|
|
34235
|
+
}
|
|
33473
34236
|
function IssueHeader({
|
|
33474
34237
|
issueId,
|
|
33475
34238
|
title,
|
|
33476
34239
|
adminIssueUrl,
|
|
33477
34240
|
onClose,
|
|
33478
34241
|
onUpdated,
|
|
33479
|
-
readOnly = false
|
|
34242
|
+
readOnly = false,
|
|
34243
|
+
generationStatus
|
|
33480
34244
|
}) {
|
|
33481
34245
|
const [draftTitle, setDraftTitle] = useState(title);
|
|
33482
34246
|
const [isEditing, setIsEditing] = useState(false);
|
|
@@ -33532,6 +34296,7 @@ function IssueHeader({
|
|
|
33532
34296
|
}
|
|
33533
34297
|
) : /* @__PURE__ */ jsxs("div", { className: "flex max-w-full min-w-0 items-center gap-1", children: [
|
|
33534
34298
|
/* @__PURE__ */ jsx("h2", { className: "m-0 min-w-0 max-w-[28rem] truncate text-xl font-semibold text-text-primary", children: title }),
|
|
34299
|
+
/* @__PURE__ */ jsx(IssueGenerationBadge, { status: generationStatus }),
|
|
33535
34300
|
!readOnly ? /* @__PURE__ */ jsx(
|
|
33536
34301
|
Button,
|
|
33537
34302
|
{
|
|
@@ -34493,7 +35258,7 @@ function IssuePopover({ issueId, onClose, onUpdate }) {
|
|
|
34493
35258
|
const { isMember } = useAuthContext();
|
|
34494
35259
|
const canWriteContent = canWriteIssueContent(isMember, (_a = SDKManager.getInstance().config.user) == null ? void 0 : _a.id);
|
|
34495
35260
|
const { selectedProjectId } = useProject();
|
|
34496
|
-
const { data: issue } = useSuspenseQuery({
|
|
35261
|
+
const { data: issue, refetch } = useSuspenseQuery({
|
|
34497
35262
|
queryKey: ["issue", issueId, selectedProjectId],
|
|
34498
35263
|
queryFn: async () => {
|
|
34499
35264
|
const issueData = await issueService.getIssue(issueId);
|
|
@@ -34501,6 +35266,7 @@ function IssuePopover({ issueId, onClose, onUpdate }) {
|
|
|
34501
35266
|
}
|
|
34502
35267
|
});
|
|
34503
35268
|
const adminIssueUrl = getAdminIssueUrl(issue.id);
|
|
35269
|
+
useGenerationPolling({ status: issue.generation_status, onRefetch: refetch });
|
|
34504
35270
|
useDisableBodyScroll();
|
|
34505
35271
|
const mapping = getPrimaryJiraMapping(issue);
|
|
34506
35272
|
const meta = getJiraMetadata(mapping);
|
|
@@ -34534,10 +35300,12 @@ function IssuePopover({ issueId, onClose, onUpdate }) {
|
|
|
34534
35300
|
adminIssueUrl,
|
|
34535
35301
|
onClose: handleClose,
|
|
34536
35302
|
onUpdated: onUpdate,
|
|
34537
|
-
readOnly: !canWriteContent
|
|
35303
|
+
readOnly: !canWriteContent,
|
|
35304
|
+
generationStatus: issue.generation_status
|
|
34538
35305
|
}
|
|
34539
35306
|
) }),
|
|
34540
35307
|
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
35308
|
+
issue.generation_status === "failed" ? /* @__PURE__ */ jsx(IssueGenerationFailure, { issueId: issue.id, onUpdated: onUpdate }) : null,
|
|
34541
35309
|
/* @__PURE__ */ jsx(
|
|
34542
35310
|
IssueDescription,
|
|
34543
35311
|
{
|
|
@@ -34994,6 +35762,7 @@ function getEstimatedExpandedWidth(title) {
|
|
|
34994
35762
|
STICKER_MAX_WIDTH
|
|
34995
35763
|
);
|
|
34996
35764
|
}
|
|
35765
|
+
const GENERATING_LABEL = "제목 생성 중";
|
|
34997
35766
|
function IssueSticker$1({
|
|
34998
35767
|
issue,
|
|
34999
35768
|
isPopoverOpen,
|
|
@@ -35005,7 +35774,11 @@ function IssueSticker$1({
|
|
|
35005
35774
|
clusterStackIndex = 0
|
|
35006
35775
|
}) {
|
|
35007
35776
|
const position2 = documentPosition;
|
|
35008
|
-
const
|
|
35777
|
+
const resolvedClusterCount = clusterCount ?? 0;
|
|
35778
|
+
const isCluster = resolvedClusterCount > 1;
|
|
35779
|
+
const generating = isGeneratingStatus(issue.generation_status) && !isCluster;
|
|
35780
|
+
const visibleTitle = isCluster ? `스티커 ${resolvedClusterCount}개 펼치기` : generating ? GENERATING_LABEL : issue.title;
|
|
35781
|
+
const expandedWidth = getEstimatedExpandedWidth(visibleTitle);
|
|
35009
35782
|
const viewportWidth = typeof window === "undefined" ? Number.POSITIVE_INFINITY : window.innerWidth;
|
|
35010
35783
|
const scrollX = typeof window === "undefined" ? 0 : window.scrollX || window.pageXOffset || 0;
|
|
35011
35784
|
const viewportX = position2.x - scrollX;
|
|
@@ -35018,10 +35791,7 @@ function IssueSticker$1({
|
|
|
35018
35791
|
transform: shouldExpandLeft ? "translate3d(calc(-100% + 16px), -50%, 0)" : "translate3d(-16px, -50%, 0)"
|
|
35019
35792
|
};
|
|
35020
35793
|
const wrapperClassName = isPopoverOpen ? "z-[10001]" : "z-[9999] hover:z-[10001] focus-within:z-[10001]";
|
|
35021
|
-
const
|
|
35022
|
-
const isCluster = resolvedClusterCount > 1;
|
|
35023
|
-
const buttonClassName = `issue-sticker-marker issue-marker-breathe group flex h-8 w-max cursor-pointer items-center overflow-hidden rounded-full border text-white shadow-lg outline-none transition-[max-width,box-shadow,background-color,border-color,transform,opacity] duration-[var(--issue-marker-expand-duration)] ease-out focus-visible:ring-4 ${isCluster ? "max-w-8 border-white/80 bg-brand shadow-lg ring-2 ring-brand-subtle hover:max-w-[160px] hover:bg-brand-hover hover:shadow-xl focus-visible:max-w-[160px] focus-visible:ring-brand-subtle" : "max-w-8 border-white/80 bg-brand hover:max-w-[480px] hover:bg-brand-hover hover:shadow-xl focus-visible:max-w-[480px] focus-visible:ring-brand-subtle"} ${shouldExpandLeft ? "flex-row-reverse" : ""} ${isPopoverOpen ? "shadow-xl ring-4 ring-brand-subtle" : ""}`;
|
|
35024
|
-
const visibleTitle = isCluster ? `스티커 ${resolvedClusterCount}개 펼치기` : issue.title;
|
|
35794
|
+
const buttonClassName = `issue-sticker-marker ${generating ? "" : "issue-marker-breathe"} group flex h-8 w-max cursor-pointer items-center overflow-hidden rounded-full border text-white shadow-lg outline-none transition-[max-width,box-shadow,background-color,border-color,transform,opacity] duration-[var(--issue-marker-expand-duration)] ease-out focus-visible:ring-4 ${isCluster ? "max-w-8 border-white/80 bg-brand shadow-lg ring-2 ring-brand-subtle hover:max-w-[160px] hover:bg-brand-hover hover:shadow-xl focus-visible:max-w-[160px] focus-visible:ring-brand-subtle" : `max-w-8 bg-brand hover:max-w-[480px] hover:bg-brand-hover hover:shadow-xl focus-visible:max-w-[480px] focus-visible:ring-brand-subtle ${generating ? "border-dashed border-white/70" : "border-white/80"}`} ${shouldExpandLeft ? "flex-row-reverse" : ""} ${isPopoverOpen ? "shadow-xl ring-4 ring-brand-subtle" : ""}`;
|
|
35025
35795
|
const buttonStyle = clusterStackAnimation ? {
|
|
35026
35796
|
animation: clusterStackAnimation === "enter" ? "issue-sticker-cluster-sticker-enter 240ms cubic-bezier(0.34, 1.2, 0.64, 1) both" : "issue-sticker-cluster-sticker-exit 220ms ease-in both",
|
|
35027
35797
|
animationDelay: clusterStackAnimation === "enter" ? `${clusterStackIndex * 24}ms` : void 0
|
|
@@ -35043,14 +35813,14 @@ function IssueSticker$1({
|
|
|
35043
35813
|
"data-cluster-count": isCluster ? resolvedClusterCount : void 0,
|
|
35044
35814
|
"data-expand-side": expandSide,
|
|
35045
35815
|
onClick: handleClick,
|
|
35046
|
-
"aria-label": isCluster ? `겹친 스티커 ${resolvedClusterCount}개 펼치기` : `이슈 열기: ${
|
|
35816
|
+
"aria-label": isCluster ? `겹친 스티커 ${resolvedClusterCount}개 펼치기` : `이슈 열기: ${visibleTitle}`,
|
|
35047
35817
|
children: [
|
|
35048
35818
|
/* @__PURE__ */ jsx(
|
|
35049
35819
|
"span",
|
|
35050
35820
|
{
|
|
35051
|
-
className: "flex
|
|
35821
|
+
className: "flex aspect-square h-full shrink-0 items-center justify-center text-sm font-black leading-none",
|
|
35052
35822
|
"aria-hidden": "true",
|
|
35053
|
-
children: isCluster ? `+${resolvedClusterCount}` : "!"
|
|
35823
|
+
children: isCluster ? `+${resolvedClusterCount}` : generating ? /* @__PURE__ */ jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" }) : "!"
|
|
35054
35824
|
}
|
|
35055
35825
|
),
|
|
35056
35826
|
/* @__PURE__ */ jsx(
|
|
@@ -36780,6 +37550,8 @@ function useToolbarDrag({
|
|
|
36780
37550
|
);
|
|
36781
37551
|
return { isDragging: isDragging2, onPointerDown };
|
|
36782
37552
|
}
|
|
37553
|
+
const TITLE_PATTERN_MISSING_MESSAGE = "제목 패턴을 아직 추출하지 않아 팀 형식이 적용되지 않습니다. 누르면 프로젝트 설정이 새 탭으로 열립니다.";
|
|
37554
|
+
const TITLE_PATTERN_EXTRACT_LABEL = "패턴 추출하기";
|
|
36783
37555
|
function formatElapsed(startedAt, now2) {
|
|
36784
37556
|
const seconds = startedAt === null ? 0 : Math.max(0, Math.floor((now2 - startedAt) / 1e3));
|
|
36785
37557
|
const mm = String(Math.floor(seconds / 60)).padStart(2, "0");
|
|
@@ -36805,7 +37577,8 @@ function Toolbar({
|
|
|
36805
37577
|
onboardingEnabled = true,
|
|
36806
37578
|
targets,
|
|
36807
37579
|
account,
|
|
36808
|
-
blocked
|
|
37580
|
+
blocked,
|
|
37581
|
+
titlePatternExtract
|
|
36809
37582
|
}) {
|
|
36810
37583
|
const rootRef = useRef(null);
|
|
36811
37584
|
const [internalOpen, setInternalOpen] = useState(false);
|
|
@@ -36941,6 +37714,19 @@ function Toolbar({
|
|
|
36941
37714
|
getShortcutLabel("CAPTURE_VIDEO")
|
|
36942
37715
|
] })
|
|
36943
37716
|
] }),
|
|
37717
|
+
titlePatternExtract ? /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
37718
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
37719
|
+
"button",
|
|
37720
|
+
{
|
|
37721
|
+
type: "button",
|
|
37722
|
+
className: "toolbar-btn",
|
|
37723
|
+
"aria-label": TITLE_PATTERN_EXTRACT_LABEL,
|
|
37724
|
+
onClick: titlePatternExtract.onClick,
|
|
37725
|
+
children: /* @__PURE__ */ jsx(WandSparkles, { size: 15 })
|
|
37726
|
+
}
|
|
37727
|
+
) }),
|
|
37728
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "top", sideOffset: 10, className: "whitespace-normal text-left", children: TITLE_PATTERN_MISSING_MESSAGE })
|
|
37729
|
+
] }) : null,
|
|
36944
37730
|
/* @__PURE__ */ jsx("div", { className: "toolbar-divider" }),
|
|
36945
37731
|
/* @__PURE__ */ jsxs(
|
|
36946
37732
|
"button",
|
|
@@ -37060,9 +37846,15 @@ const SDK_TOOLBAR_TARGETS = {
|
|
|
37060
37846
|
menuSession: "sdk-more-session"
|
|
37061
37847
|
};
|
|
37062
37848
|
function SdkToolbar(props) {
|
|
37063
|
-
const { isMember, login, logout } = useAuthContext();
|
|
37849
|
+
const { isMember, projectId, login, logout } = useAuthContext();
|
|
37064
37850
|
const [error, setError] = useState("");
|
|
37065
37851
|
const [loading, setLoading] = useState(false);
|
|
37852
|
+
const [titlePatternMissing, setTitlePatternMissing] = useState(false);
|
|
37853
|
+
const handleDraftCapabilityResolved = useCallback(
|
|
37854
|
+
({ titlePatternMissing: missing }) => setTitlePatternMissing(missing),
|
|
37855
|
+
[]
|
|
37856
|
+
);
|
|
37857
|
+
const titlePatternUrl = isMember && titlePatternMissing && projectId ? getAdminProjectTitlePatternUrl(projectId) : null;
|
|
37066
37858
|
const handleLogin = async () => {
|
|
37067
37859
|
var _a;
|
|
37068
37860
|
setError("");
|
|
@@ -37081,21 +37873,25 @@ function SdkToolbar(props) {
|
|
|
37081
37873
|
await logout();
|
|
37082
37874
|
(_a = props.onMenuOpenChange) == null ? void 0 : _a.call(props, false);
|
|
37083
37875
|
};
|
|
37084
|
-
return /* @__PURE__ */
|
|
37085
|
-
|
|
37086
|
-
|
|
37087
|
-
|
|
37088
|
-
|
|
37089
|
-
|
|
37090
|
-
|
|
37091
|
-
|
|
37092
|
-
|
|
37093
|
-
|
|
37094
|
-
|
|
37095
|
-
|
|
37876
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
37877
|
+
isMember ? /* @__PURE__ */ jsx(QueryBoundary, { loadingFallback: null, onError: () => null, children: /* @__PURE__ */ jsx(IssueFormDraftCapabilityGate, { onResolved: handleDraftCapabilityResolved }) }) : null,
|
|
37878
|
+
/* @__PURE__ */ jsx(
|
|
37879
|
+
Toolbar,
|
|
37880
|
+
{
|
|
37881
|
+
...props,
|
|
37882
|
+
targets: SDK_TOOLBAR_TARGETS,
|
|
37883
|
+
titlePatternExtract: titlePatternUrl ? { onClick: () => window.open(titlePatternUrl, "_blank", "noopener,noreferrer") } : void 0,
|
|
37884
|
+
account: {
|
|
37885
|
+
isMember,
|
|
37886
|
+
canLogin: !isDemoMode,
|
|
37887
|
+
loading,
|
|
37888
|
+
error,
|
|
37889
|
+
onLogin: () => void handleLogin(),
|
|
37890
|
+
onLogout: () => void handleLogout()
|
|
37891
|
+
}
|
|
37096
37892
|
}
|
|
37097
|
-
|
|
37098
|
-
);
|
|
37893
|
+
)
|
|
37894
|
+
] });
|
|
37099
37895
|
}
|
|
37100
37896
|
function ContentAppWrapper({
|
|
37101
37897
|
fabEnabled,
|
|
@@ -37150,7 +37946,6 @@ function ContentAppWrapper({
|
|
|
37150
37946
|
onBeforeStart: handleBeforeOnboardingStart,
|
|
37151
37947
|
isCreatingIssueFlow: isCreatingIssue || clickPosition !== null,
|
|
37152
37948
|
children: [
|
|
37153
|
-
/* @__PURE__ */ jsx(ToastContainer, { position: "top-right" }),
|
|
37154
37949
|
fabEnabled && /* @__PURE__ */ jsx(
|
|
37155
37950
|
SdkToolbar,
|
|
37156
37951
|
{
|
|
@@ -37172,7 +37967,7 @@ function ContentAppWrapper({
|
|
|
37172
37967
|
onMenuOpenChange: setMoreMenuOpen
|
|
37173
37968
|
}
|
|
37174
37969
|
),
|
|
37175
|
-
/* @__PURE__ */ jsx(QueryBoundary, { loadingFallback: /* @__PURE__ */ jsx(Fragment, {}), children: /* @__PURE__ */ jsx(
|
|
37970
|
+
/* @__PURE__ */ jsx(QueryBoundary, { loadingFallback: /* @__PURE__ */ jsx(Fragment, {}), onError: OverlayErrorFallback, children: /* @__PURE__ */ jsx(
|
|
37176
37971
|
IssueStickersContainer,
|
|
37177
37972
|
{
|
|
37178
37973
|
issues,
|
|
@@ -37194,7 +37989,7 @@ function ContentAppWrapper({
|
|
|
37194
37989
|
renderMode
|
|
37195
37990
|
}
|
|
37196
37991
|
),
|
|
37197
|
-
openPopoverIssueId && isExtensionEnabled && /* @__PURE__ */ jsx(QueryBoundary, { loadingFallback: /* @__PURE__ */ jsx(Fragment, {}), children: /* @__PURE__ */ jsx(
|
|
37992
|
+
openPopoverIssueId && isExtensionEnabled && /* @__PURE__ */ jsx(QueryBoundary, { loadingFallback: /* @__PURE__ */ jsx(Fragment, {}), onError: OverlayErrorFallback, children: /* @__PURE__ */ jsx(
|
|
37198
37993
|
IssuePopover,
|
|
37199
37994
|
{
|
|
37200
37995
|
issueId: openPopoverIssueId,
|
|
@@ -37202,7 +37997,7 @@ function ContentAppWrapper({
|
|
|
37202
37997
|
onUpdate: loadIssues
|
|
37203
37998
|
}
|
|
37204
37999
|
) }),
|
|
37205
|
-
clickPosition && isExtensionEnabled && /* @__PURE__ */ jsx(QueryBoundary, { loadingFallback: /* @__PURE__ */ jsx(Fragment, {}), children: /* @__PURE__ */ jsx(
|
|
38000
|
+
clickPosition && isExtensionEnabled && /* @__PURE__ */ jsx(QueryBoundary, { loadingFallback: /* @__PURE__ */ jsx(Fragment, {}), onError: OverlayErrorFallback, children: /* @__PURE__ */ jsx(
|
|
37206
38001
|
IssueForm,
|
|
37207
38002
|
{
|
|
37208
38003
|
position: clickPosition,
|
|
@@ -37247,18 +38042,21 @@ function AuthGate() {
|
|
|
37247
38042
|
if (!isAuthenticated) {
|
|
37248
38043
|
return null;
|
|
37249
38044
|
}
|
|
37250
|
-
return /* @__PURE__ */
|
|
37251
|
-
|
|
37252
|
-
{
|
|
37253
|
-
|
|
37254
|
-
|
|
37255
|
-
|
|
37256
|
-
|
|
37257
|
-
|
|
38045
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38046
|
+
/* @__PURE__ */ jsx(ToastContainer, { position: "top-right" }),
|
|
38047
|
+
/* @__PURE__ */ jsx(QueryBoundary, { loadingFallback: /* @__PURE__ */ jsx(Fragment, {}), onError: OverlayErrorFallback, children: /* @__PURE__ */ jsx(
|
|
38048
|
+
ContentAppWrapper,
|
|
38049
|
+
{
|
|
38050
|
+
fabEnabled,
|
|
38051
|
+
toolbar: sdk.config.toolbar,
|
|
38052
|
+
onboarding
|
|
38053
|
+
}
|
|
38054
|
+
) })
|
|
38055
|
+
] });
|
|
37258
38056
|
}
|
|
37259
|
-
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;--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%;--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{--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;--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%;--border-subtle: 144 8% 91%;--border-default: 144 6% 85%}@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}@keyframes onboarding-confetti-x{0%{transform:translate(0)}to{transform:translate(var(--confetti-x))}}@keyframes onboarding-confetti-y{0%{transform:translateY(0);animation-timing-function:cubic-bezier(.12,.66,.32,1)}42%{transform:translateY(var(--confetti-lift));animation-timing-function:cubic-bezier(.58,0,.86,.32)}to{transform:translateY(var(--confetti-drop))}}@keyframes onboarding-confetti-piece{0%{opacity:0;transform:scale(.7) rotate(0)}6%{opacity:1}72%{opacity:1}to{opacity:0;transform:scale(1) rotate(var(--confetti-spin))}}.onboarding-confetti-x,.onboarding-confetti-y,.onboarding-confetti-piece{--confetti-duration: 1.9s;animation-duration:var(--confetti-duration);animation-delay:var(--confetti-delay, 0ms);animation-fill-mode:both}.onboarding-confetti-x{animation-name:onboarding-confetti-x;animation-timing-function:cubic-bezier(.08,.62,.3,1)}.onboarding-confetti-y{animation-name:onboarding-confetti-y;animation-timing-function:linear}.onboarding-confetti-piece{width:var(--confetti-size);height:var(--confetti-size);border-radius:var(--radius-pill) var(--radius-xs) var(--radius-pill) var(--radius-pill);animation-name:onboarding-confetti-piece;animation-timing-function:linear}@media(prefers-reduced-motion:reduce){.onboarding-confetti-x,.onboarding-confetti-y,.onboarding-confetti-piece{animation:none;opacity:0}}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:.5rem;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{background-color:transparent;--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.container{width:100%}@media(min-width:640px){.container{max-width:640px}}@media(min-width:768px){.container{max-width:768px}}@media(min-width:1024px){.container{max-width:1024px}}@media(min-width:1280px){.container{max-width:1280px}}@media(min-width:1536px){.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:.25rem;right:.25rem}.inset-y-0{top:0;bottom:0}.-bottom-px{bottom:-1px}.bottom-0{bottom:0}.bottom-4{bottom:1rem}.bottom-6{bottom:1.5rem}.left-0{left:0}.left-1\\/2{left:50%}.left-2{left:.5rem}.left-4{left:1rem}.left-\\[50\\%\\]{left:50%}.right-0{right:0}.right-2{right:.5rem}.right-3{right:.75rem}.right-4{right:1rem}.right-5{right:1.25rem}.right-6{right:1.5rem}.top-0{top:0}.top-1{top:.25rem}.top-1\\/2{top:50%}.top-2{top:.5rem}.top-3{top:.75rem}.top-4{top:1rem}.top-5{top:1.25rem}.top-\\[50\\%\\]{top:50%}.top-\\[calc\\(\\(3\\.5rem-1\\.75rem\\)\\/2\\)\\]{top:.875rem}.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:-.5rem;margin-right:-.5rem}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-2\\.5{margin-top:.625rem;margin-bottom:.625rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.mb-0\\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-5{margin-bottom:1.25rem}.ml-0\\.5{margin-left:.125rem}.ml-4{margin-left:1rem}.mr-1\\.5{margin-right:.375rem}.mt-0{margin-top:0}.mt-0\\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.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-10{height:2.5rem}.h-11{height:2.75rem}.h-2{height:.5rem}.h-24{height:6rem}.h-3{height:.75rem}.h-3\\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\\[1\\.4rem\\]{height:1.4rem}.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\\(--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:6rem}.max-h-56{max-height:14rem}.max-h-96{max-height:24rem}.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 - 3rem)}.max-h-\\[min\\(80vh\\,640px\\)\\]{max-height:min(80vh,640px)}.min-h-0{min-height:0px}.min-h-11{min-height:2.75rem}.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:.125rem}.w-1{width:.25rem}.w-1\\/2{width:50%}.w-10{width:2.5rem}.w-14{width:3.5rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-3\\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\\[1\\.4rem\\]{width:1.4rem}.w-\\[18px\\]{width:18px}.w-\\[21rem\\]{width:21rem}.w-\\[22rem\\]{width:22rem}.w-\\[30px\\]{width:30px}.w-\\[38px\\]{width:38px}.w-\\[calc\\(100vw-32px\\)\\]{width:calc(100vw - 32px)}.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:2.25rem}.min-w-\\[12rem\\]{min-width:12rem}.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-8{max-width:2rem}.max-w-\\[120px\\]{max-width:120px}.max-w-\\[160px\\]{max-width:160px}.max-w-\\[240px\\]{max-width:240px}.max-w-\\[280px\\]{max-width:280px}.max-w-\\[28rem\\]{max-width:28rem}.max-w-\\[300px\\]{max-width:300px}.max-w-\\[360px\\]{max-width:360px}.max-w-\\[36rem\\]{max-width:36rem}.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-full{max-width:100%}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.caption-bottom{caption-side:bottom}.border-collapse{border-collapse:collapse}.-translate-x-1{--tw-translate-x: -.25rem;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-1{--tw-translate-x: .25rem;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:.125rem}.gap-1{gap:.25rem}.gap-1\\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-3\\.5{gap:.875rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.gap-x-5{-moz-column-gap:1.25rem;column-gap:1.25rem}.gap-y-2{row-gap:.5rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * 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-all{word-break:break-all}.\\!rounded-none{border-radius:0!important}.rounded{border-radius:.25rem}.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-\\[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-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\\/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-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-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}.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:.125rem}.p-1{padding:.25rem}.p-1\\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-3\\.5{padding:.875rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-7{padding:1.75rem}.p-8{padding:2rem}.p-\\[18px\\]{padding:18px}.px-0{padding-left:0;padding-right:0}.px-1\\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-9{padding-left:2.25rem;padding-right:2.25rem}.py-0\\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\\.5{padding-top:.875rem;padding-bottom:.875rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-1{padding-bottom:.25rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pl-5{padding-left:1.25rem}.pl-8{padding-left:2rem}.pr-10{padding-right:2.5rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-2\\.5{padding-top:.625rem}.pt-4{padding-top:1rem}.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:10.5px;line-height:14px}.text-\\[0\\.72rem\\]{font-size:.72rem}.text-\\[1\\.45rem\\]{font-size:1.45rem}.text-\\[1\\.75rem\\]{font-size:1.75rem}.text-\\[10px\\]{font-size:10px}.text-base{font-size:14px;line-height:21px}.text-lg{font-size:17px;line-height:25px}.text-sm{font-size:13px;line-height:19px}.text-xl{font-size:20px;line-height:29px}.text-xs{font-size:12px;line-height:16px}.font-black{font-weight:900}.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:1rem}.leading-5{line-height:1.25rem}.leading-8{line-height:2rem}.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}.\\!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\\,box-shadow\\,background-color\\,border-color\\,transform\\,opacity\\]{transition-property:max-width,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}.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:13px;line-height:19px}.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))}.focus-within\\:z-\\[10001\\]:focus-within{z-index:10001}.hover\\:z-\\[10001\\]:hover{z-index:10001}.hover\\:max-w-\\[160px\\]:hover{max-width:160px}.hover\\:max-w-\\[480px\\]:hover{max-width:480px}.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}.hover\\:shadow-xl:hover{--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)}.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\\: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-50:disabled{opacity:.5}.group:hover .group-hover\\: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:hover .group-hover\\:opacity-100{opacity:1}.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:1rem;padding-right:1rem}.group.toaster .group-\\[\\.toaster\\]\\:py-3{padding-top:.75rem;padding-bottom:.75rem}.group.toast .group-\\[\\.toast\\]\\:text-sm{font-size:13px;line-height:19px}.group.toast .group-\\[\\.toast\\]\\:text-xs{font-size:12px;line-height:16px}.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:.625rem}.sm\\:p-5{padding:1.25rem}.sm\\:p-6{padding:1.5rem}.sm\\:text-2xl{font-size:26px;line-height:34px}.sm\\:text-\\[2\\.1rem\\]{font-size:2.1rem}}@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:1rem}.\\[\\&\\>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))}`;
|
|
38057
|
+
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;--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%;--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{--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;--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)}@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}@keyframes onboarding-confetti-x{0%{transform:translate(0)}to{transform:translate(var(--confetti-x))}}@keyframes onboarding-confetti-y{0%{transform:translateY(0);animation-timing-function:cubic-bezier(.12,.66,.32,1)}42%{transform:translateY(var(--confetti-lift));animation-timing-function:cubic-bezier(.58,0,.86,.32)}to{transform:translateY(var(--confetti-drop))}}@keyframes onboarding-confetti-piece{0%{opacity:0;transform:scale(.7) rotate(0)}6%{opacity:1}72%{opacity:1}to{opacity:0;transform:scale(1) rotate(var(--confetti-spin))}}.onboarding-confetti-x,.onboarding-confetti-y,.onboarding-confetti-piece{--confetti-duration: 1.9s;animation-duration:var(--confetti-duration);animation-delay:var(--confetti-delay, 0ms);animation-fill-mode:both}.onboarding-confetti-x{animation-name:onboarding-confetti-x;animation-timing-function:cubic-bezier(.08,.62,.3,1)}.onboarding-confetti-y{animation-name:onboarding-confetti-y;animation-timing-function:linear}.onboarding-confetti-piece{width:var(--confetti-size);height:var(--confetti-size);border-radius:var(--radius-pill) var(--radius-xs) var(--radius-pill) var(--radius-pill);animation-name:onboarding-confetti-piece;animation-timing-function:linear}@media(prefers-reduced-motion:reduce){.onboarding-confetti-x,.onboarding-confetti-y,.onboarding-confetti-piece{animation:none;opacity:0}}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:.5rem;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{background-color:transparent;--tw-text-opacity: 1;color:hsl(var(--text-primary) / var(--tw-text-opacity, 1))}.\\!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:.25rem;right:.25rem}.inset-y-0{top:0;bottom:0}.-bottom-px{bottom:-1px}.bottom-0{bottom:0}.bottom-4{bottom:1rem}.bottom-6{bottom:1.5rem}.left-0{left:0}.left-1\\/2{left:50%}.left-2{left:.5rem}.left-4{left:1rem}.left-\\[50\\%\\]{left:50%}.right-0{right:0}.right-2{right:.5rem}.right-3{right:.75rem}.right-4{right:1rem}.right-5{right:1.25rem}.right-6{right:1.5rem}.top-0{top:0}.top-1{top:.25rem}.top-1\\/2{top:50%}.top-2{top:.5rem}.top-3{top:.75rem}.top-4{top:1rem}.top-5{top:1.25rem}.top-\\[50\\%\\]{top:50%}.top-\\[calc\\(\\(3\\.5rem-1\\.75rem\\)\\/2\\)\\]{top:.875rem}.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:-.5rem;margin-right:-.5rem}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-2\\.5{margin-top:.625rem;margin-bottom:.625rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.mb-0\\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-5{margin-bottom:1.25rem}.ml-0\\.5{margin-left:.125rem}.ml-4{margin-left:1rem}.mr-1\\.5{margin-right:.375rem}.mt-0{margin-top:0}.mt-0\\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.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:.375rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-2{height:.5rem}.h-24{height:6rem}.h-3{height:.75rem}.h-3\\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\\[1\\.4rem\\]{height:1.4rem}.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\\(--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:6rem}.max-h-40{max-height:10rem}.max-h-56{max-height:14rem}.max-h-96{max-height:24rem}.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 - 3rem)}.max-h-\\[min\\(80vh\\,640px\\)\\]{max-height:min(80vh,640px)}.min-h-0{min-height:0px}.min-h-11{min-height:2.75rem}.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:.125rem}.w-1{width:.25rem}.w-1\\.5{width:.375rem}.w-1\\/2{width:50%}.w-10{width:2.5rem}.w-14{width:3.5rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-3\\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-80{width:20rem}.w-9{width:2.25rem}.w-\\[1\\.4rem\\]{width:1.4rem}.w-\\[18px\\]{width:18px}.w-\\[21rem\\]{width:21rem}.w-\\[22rem\\]{width:22rem}.w-\\[30px\\]{width:30px}.w-\\[38px\\]{width:38px}.w-\\[calc\\(100vw-32px\\)\\]{width:calc(100vw - 32px)}.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:2.25rem}.min-w-\\[12rem\\]{min-width:12rem}.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-8{max-width:2rem}.max-w-\\[120px\\]{max-width:120px}.max-w-\\[160px\\]{max-width:160px}.max-w-\\[280px\\]{max-width:280px}.max-w-\\[28rem\\]{max-width:28rem}.max-w-\\[300px\\]{max-width:300px}.max-w-\\[360px\\]{max-width:360px}.max-w-\\[36rem\\]{max-width:36rem}.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-full{max-width:100%}.max-w-md{max-width:28rem}.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: -.25rem;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-1{--tw-translate-x: .25rem;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:.125rem}.gap-1{gap:.25rem}.gap-1\\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-3\\.5{gap:.875rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.gap-x-5{-moz-column-gap:1.25rem;column-gap:1.25rem}.gap-y-2{row-gap:.5rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * 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-all{word-break:break-all}.\\!rounded-none{border-radius:0!important}.rounded{border-radius:.25rem}.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}.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:.125rem}.p-1{padding:.25rem}.p-1\\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-3\\.5{padding:.875rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-7{padding:1.75rem}.p-8{padding:2rem}.p-\\[18px\\]{padding:18px}.px-0{padding-left:0;padding-right:0}.px-1\\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-9{padding-left:2.25rem;padding-right:2.25rem}.py-0\\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\\.5{padding-top:.875rem;padding-bottom:.875rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-1{padding-bottom:.25rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pl-5{padding-left:1.25rem}.pl-8{padding-left:2rem}.pr-10{padding-right:2.5rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-2\\.5{padding-top:.625rem}.pt-4{padding-top:1rem}.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:10.5px;line-height:14px}.text-\\[0\\.72rem\\]{font-size:.72rem}.text-\\[1\\.45rem\\]{font-size:1.45rem}.text-\\[1\\.75rem\\]{font-size:1.75rem}.text-\\[10px\\]{font-size:10px}.text-base{font-size:14px;line-height:21px}.text-lg{font-size:17px;line-height:25px}.text-sm{font-size:13px;line-height:19px}.text-xl{font-size:20px;line-height:29px}.text-xs{font-size:12px;line-height:16px}.font-black{font-weight:900}.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:1rem}.leading-5{line-height:1.25rem}.leading-8{line-height:2rem}.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}.\\!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\\,box-shadow\\,background-color\\,border-color\\,transform\\,opacity\\]{transition-property:max-width,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}.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:13px;line-height:19px}.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))}.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\\:max-w-\\[160px\\]:hover{max-width:160px}.hover\\:max-w-\\[480px\\]:hover{max-width:480px}.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}.hover\\:shadow-xl:hover{--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)}.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\\: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-50:disabled{opacity:.5}.group:hover .group-hover\\: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:hover .group-hover\\:opacity-100{opacity:1}.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:1rem;padding-right:1rem}.group.toaster .group-\\[\\.toaster\\]\\:py-3{padding-top:.75rem;padding-bottom:.75rem}.group.toast .group-\\[\\.toast\\]\\:text-sm{font-size:13px;line-height:19px}.group.toast .group-\\[\\.toast\\]\\:text-xs{font-size:12px;line-height:16px}.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:.625rem}.sm\\:p-5{padding:1.25rem}.sm\\:p-6{padding:1.5rem}.sm\\:text-2xl{font-size:26px;line-height:34px}.sm\\:text-\\[2\\.1rem\\]{font-size:2.1rem}}@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:1rem}.\\[\\&\\>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))}`;
|
|
37260
38058
|
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}";
|
|
37261
|
-
const contentPageCSS = `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='%
|
|
38059
|
+
const contentPageCSS = `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' stroke='%23ffffff' stroke-width='2' stroke-linejoin='round'/%3E%3C/svg%3E") 3 29,crosshair!important}body.issue-sticker-mode-active .issue-sticker-marker{cursor:default!important}body.issue-sticker-mode-active button,body.issue-sticker-mode-active a,body.issue-sticker-mode-active input,body.issue-sticker-mode-active textarea,body.issue-sticker-mode-active select{cursor:pointer!important}.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}`;
|
|
37262
38060
|
const PAGE_STYLE_ID = "issue-sticker-page-style";
|
|
37263
38061
|
let root = null;
|
|
37264
38062
|
let mountContainer = null;
|