@aiquants/resize-panels 1.7.3 → 1.8.1

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/src/utils.ts ADDED
@@ -0,0 +1,279 @@
1
+ /**
2
+ * @file Utility functions for custom resizable panels
3
+ * カスタムリサイズ可能パネルのためのユーティリティ関数
4
+ */
5
+
6
+ import { roundHalfToEven } from "./roundHalfToEven"
7
+ import { type ContainerAxisMeasurement, type FlexibleSize, PANEL_COLLAPSE_THRESHOLD_MAX, PANEL_COLLAPSE_THRESHOLD_RATIO, PANEL_MIN_SNAP_THRESHOLD, PANEL_SNAP_THRESHOLD, type PanelDirection, type PanelLayoutData, type SizeConfig, type SizeUnit } from "./types"
8
+
9
+ /**
10
+ * Number of significant fractional digits to preserve when rounding percentage-based sizes.
11
+ * パーセンテージサイズの丸めで保持する有効小数桁数。
12
+ */
13
+ export const PERCENTAGE_DIGITS = 6
14
+
15
+ /**
16
+ * Convert a pixel measurement to a percentage of the container using banker’s rounding.
17
+ * ピクセル寸法をバンカーズラウンディングでコンテナ比率のパーセンテージへ変換する。
18
+ */
19
+ export const toRoundedPercentage = (size: number, base: number): number => {
20
+ if (base <= 0) {
21
+ return 0
22
+ }
23
+ const percentage = roundHalfToEven((size / base) * 100, PERCENTAGE_DIGITS)
24
+ return percentage
25
+ }
26
+
27
+ /**
28
+ * Translate a percentage-based size back into pixels relative to the container.
29
+ * パーセンテージ指定のサイズをコンテナ基準のピクセル値へ変換する。
30
+ */
31
+ export const percentageToPixels = (percentage: number | undefined, base: number): number => {
32
+ if (!percentage || base <= 0) {
33
+ return 0
34
+ }
35
+ return (percentage / 100) * base
36
+ }
37
+
38
+ /**
39
+ * Normalize a flexible size into a size configuration object.
40
+ * 柔軟なサイズ指定をサイズ設定オブジェクトへ正規化する。
41
+ */
42
+ export const normalizeSizeConfig = (size: FlexibleSize | undefined, defaultUnit: SizeUnit = "percentage"): SizeConfig | undefined => {
43
+ if (size === undefined) return undefined
44
+ if (typeof size === "number") {
45
+ return { value: size, unit: defaultUnit }
46
+ }
47
+ return size
48
+ }
49
+
50
+ /**
51
+ * Convert flexible size constraints into pixel values based on container size.
52
+ * 柔軟サイズ制約をコンテナサイズ基準のピクセル値へ変換する。
53
+ */
54
+ export const getConstraintInPixels = (size: FlexibleSize | undefined, defaultValue: number, containerSize: number): number => {
55
+ if (!size) return defaultValue
56
+ if (typeof size === "number") {
57
+ return (size / 100) * containerSize
58
+ }
59
+ if (size.unit === "pixels") {
60
+ return size.value
61
+ }
62
+ return (size.value / 100) * containerSize
63
+ }
64
+
65
+ /**
66
+ * Clamp a numeric value between the provided bounds.
67
+ * 数値を指定した範囲に収めるためのクリッピング処理。
68
+ */
69
+ export const clamp = (value: number, min: number, max: number): number => {
70
+ return Math.min(Math.max(value, min), max)
71
+ }
72
+
73
+ /**
74
+ * Derive a snap-to-zero threshold from the container axis length using shared heuristics.
75
+ * 共通ヒューリスティクスに基づきコンテナ軸長からゼロスナップ閾値を算出する。
76
+ */
77
+ export const calculateSnapThreshold = (axisSize: number): number => {
78
+ if (!Number.isFinite(axisSize) || axisSize <= 0) {
79
+ return PANEL_MIN_SNAP_THRESHOLD
80
+ }
81
+ const thresholdFromRatio = axisSize * PANEL_COLLAPSE_THRESHOLD_RATIO
82
+ const cappedThreshold = Math.min(PANEL_SNAP_THRESHOLD, PANEL_COLLAPSE_THRESHOLD_MAX, thresholdFromRatio)
83
+ return Math.max(PANEL_MIN_SNAP_THRESHOLD, cappedThreshold)
84
+ }
85
+
86
+ /**
87
+ * Adjust pixel-based panels in priority order to absorb remaining space.
88
+ * 残余スペースを吸収するためにピクセル基準パネルを優先度順に調整する。
89
+ */
90
+ type AdjustPixelOptions = {
91
+ enforceAutoMinSize?: boolean
92
+ }
93
+
94
+ export const adjustPixelPanelsByPriority = (panels: PanelLayoutData[], sizeDiff: number, containerSize: number, options: AdjustPixelOptions = {}): number => {
95
+ if (Math.abs(sizeDiff) <= 0) {
96
+ return sizeDiff
97
+ }
98
+
99
+ const enforceAutoMinSize = options.enforceAutoMinSize ?? false
100
+
101
+ // ピクセル指定パネルを抽出してインデックスを保持
102
+ const pixelPanels = panels.map((panel, index) => ({ panel, index })).filter(({ panel }) => !panel.collapsed && (panel.sizeUnit === "pixels" || panel.originalPixelSize !== undefined))
103
+
104
+ if (pixelPanels.length === 0) {
105
+ return sizeDiff
106
+ }
107
+
108
+ // 優先度順にソート (優先度が同じ場合はインデックス順)
109
+ const isGrowing = sizeDiff > 0
110
+ const sortedPanels = [...pixelPanels].sort((a, b) => {
111
+ const priorityA = a.panel.pixelAdjustPriority ?? a.index + 1
112
+ const priorityB = b.panel.pixelAdjustPriority ?? b.index + 1
113
+ if (priorityA === priorityB) {
114
+ return isGrowing ? b.index - a.index : a.index - b.index
115
+ }
116
+ return isGrowing ? priorityB - priorityA : priorityA - priorityB
117
+ })
118
+
119
+ let remaining = sizeDiff
120
+
121
+ /**
122
+ * Execute a single adjustment pass for pixel panels.
123
+ * ピクセルパネル調整処理
124
+ */
125
+ const adjustPass = (respectPreferred: boolean) => {
126
+ for (const { index } of sortedPanels) {
127
+ if (Math.abs(remaining) <= 0) {
128
+ break
129
+ }
130
+
131
+ const panel = panels[index]
132
+ if (!panel || panel.collapsed) {
133
+ continue
134
+ }
135
+
136
+ // パネルの最小・最大サイズをピクセル値へ変換
137
+ // bare number は他の全経路 (getConstraintInPixels) と同じくパーセンテージとして解釈する
138
+ const minSizeFromConstraint = getConstraintInPixels(panel.minSize, 0, containerSize)
139
+ const maxSize = getConstraintInPixels(panel.maxSize, containerSize, containerSize)
140
+ const autoMinSize = enforceAutoMinSize ? getConstraintInPixels(panel.autoMinSize, 0, containerSize) : 0
141
+ const minSize = Math.max(minSizeFromConstraint, autoMinSize)
142
+ const preferredPixel = panel.preferredPixelSize !== undefined ? clamp(panel.preferredPixelSize, minSize, maxSize) : panel.size
143
+
144
+ const panelIsCollapsible = panel.collapseFromStart || panel.collapseFromEnd
145
+ if (remaining > 0) {
146
+ // パネルサイズがゼロかどうかを判定
147
+ const isZero = Math.abs(panel.size) <= 0 && (panel.percentageSize ?? 0) <= 0
148
+ // ゼロのパネルはスキップ
149
+ if (isZero) {
150
+ continue
151
+ }
152
+
153
+ const targetSize = Math.max(panel.size, preferredPixel)
154
+ const effectiveMax = respectPreferred ? Math.min(maxSize, targetSize) : maxSize
155
+ const expansionCapacity = Math.max(0, effectiveMax - panel.size)
156
+ if (expansionCapacity <= 0) {
157
+ continue
158
+ }
159
+ const applied = Math.min(expansionCapacity, remaining)
160
+ if (applied <= 0) {
161
+ continue
162
+ }
163
+ const nextSize = panel.size + applied
164
+ const nextPercentage = toRoundedPercentage(nextSize, containerSize)
165
+ const nextSizeBeforeCollapse = panelIsCollapsible ? nextSize : panel.sizeBeforeCollapse
166
+ const nextMeasuredPixelBeforeCollapse = panelIsCollapsible ? (panel.measuredPixelSizeBeforeCollapse ?? panel.measuredPixelSize ?? nextSize) : panel.measuredPixelSizeBeforeCollapse
167
+ panels[index] = {
168
+ ...panel,
169
+ size: nextSize,
170
+ percentageSize: nextPercentage,
171
+ sizeBeforeCollapse: nextSizeBeforeCollapse,
172
+ measuredPixelSizeBeforeCollapse: nextMeasuredPixelBeforeCollapse,
173
+ measuredPixelSize: undefined,
174
+ measuredPercentageSize: undefined,
175
+ }
176
+ remaining -= applied
177
+ } else {
178
+ const shrinkFloor = respectPreferred ? preferredPixel : minSize
179
+ const reductionCapacity = Math.max(0, panel.size - shrinkFloor)
180
+ if (reductionCapacity <= 0) {
181
+ continue
182
+ }
183
+ const applied = Math.min(reductionCapacity, Math.abs(remaining))
184
+ if (applied <= 0) {
185
+ continue
186
+ }
187
+ const nextSize = panel.size - applied
188
+ const nextPercentage = toRoundedPercentage(nextSize, containerSize)
189
+ const nextSizeBeforeCollapse = panelIsCollapsible ? nextSize : panel.sizeBeforeCollapse
190
+ const nextMeasuredPixelBeforeCollapse = panelIsCollapsible ? (panel.measuredPixelSizeBeforeCollapse ?? panel.measuredPixelSize ?? nextSize) : panel.measuredPixelSizeBeforeCollapse
191
+ panels[index] = {
192
+ ...panel,
193
+ size: nextSize,
194
+ percentageSize: nextPercentage,
195
+ sizeBeforeCollapse: nextSizeBeforeCollapse,
196
+ measuredPixelSizeBeforeCollapse: nextMeasuredPixelBeforeCollapse,
197
+ measuredPixelSize: undefined,
198
+ measuredPercentageSize: undefined,
199
+ }
200
+ remaining += applied
201
+ }
202
+ }
203
+ }
204
+
205
+ adjustPass(true)
206
+
207
+ if ((isGrowing && remaining > 0) || (!isGrowing && remaining < 0)) {
208
+ adjustPass(false)
209
+ }
210
+
211
+ return remaining
212
+ }
213
+
214
+ /**
215
+ * Parse a CSS pixel string and return the numeric portion.
216
+ * CSS ピクセル表現から数値を抽出する処理。
217
+ */
218
+ const parsePixelValue = (value: string | null): number => {
219
+ if (!value) {
220
+ return 0
221
+ }
222
+ const parsed = parseFloat(value)
223
+ return Number.isFinite(parsed) ? parsed : 0
224
+ }
225
+
226
+ /**
227
+ * Measure container size in a given direction, returning inner (borderless) and outer dimensions.
228
+ * 指定方向で枠線控除後と控除前の両方の寸法を測定して返す。
229
+ */
230
+ export const getContainerSize = (element: HTMLElement, direction: PanelDirection): ContainerAxisMeasurement => {
231
+ const rect = element.getBoundingClientRect()
232
+ const styles = typeof window !== "undefined" ? window.getComputedStyle(element) : null
233
+ const horizontalBorder = styles ? parsePixelValue(styles.borderLeftWidth) + parsePixelValue(styles.borderRightWidth) : 0
234
+ const verticalBorder = styles ? parsePixelValue(styles.borderTopWidth) + parsePixelValue(styles.borderBottomWidth) : 0
235
+ if (direction === "horizontal") {
236
+ const rectWidth = rect.width > 0 ? rect.width : element.offsetWidth
237
+ const innerWidth = rect.width > 0 ? Math.max(0, rect.width - horizontalBorder) : element.clientWidth
238
+ const outerWidthFallback = element.offsetWidth > 0 ? element.offsetWidth : element.clientWidth + horizontalBorder
239
+ // 枠線の厚みを控除した実効幅と控除前の幅をそれぞれ算出
240
+ return {
241
+ inner: innerWidth > 0 ? innerWidth : Math.max(0, element.clientWidth),
242
+ outer: rectWidth > 0 ? rectWidth : Math.max(0, outerWidthFallback),
243
+ }
244
+ }
245
+ const rectHeight = rect.height > 0 ? rect.height : element.offsetHeight
246
+ const innerHeight = rect.height > 0 ? Math.max(0, rect.height - verticalBorder) : element.clientHeight
247
+ const outerHeightFallback = element.offsetHeight > 0 ? element.offsetHeight : element.clientHeight + verticalBorder
248
+ // 枠線控除後の高さと控除前の高さを併せて返却
249
+ return {
250
+ inner: innerHeight > 0 ? innerHeight : Math.max(0, element.clientHeight),
251
+ outer: rectHeight > 0 ? rectHeight : Math.max(0, outerHeightFallback),
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Persist panel layout sizes to local storage.
257
+ * パネルレイアウトのサイズをローカルストレージへ保存する。
258
+ */
259
+ export const saveLayout = (id: string, sizes: number[]): void => {
260
+ try {
261
+ localStorage.setItem(`panel-layout-${id}`, JSON.stringify(sizes))
262
+ } catch (error) {
263
+ console.warn("Failed to save panel layout:", error)
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Restore panel layout sizes from local storage.
269
+ * パネルレイアウトのサイズをローカルストレージから復元する。
270
+ */
271
+ export const loadLayout = (id: string): number[] | null => {
272
+ try {
273
+ const saved = localStorage.getItem(`panel-layout-${id}`)
274
+ return saved ? JSON.parse(saved) : null
275
+ } catch (error) {
276
+ console.warn("Failed to load panel layout:", error)
277
+ return null
278
+ }
279
+ }
@@ -1 +0,0 @@
1
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-orange-100:oklch(95.4% .038 75.164);--color-yellow-50:oklch(98.7% .026 102.212);--color-yellow-100:oklch(97.3% .071 103.193);--color-lime-100:oklch(96.7% .067 122.328);--color-green-50:oklch(98.2% .018 155.826);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-700:oklch(52.7% .154 150.069);--color-green-800:oklch(44.8% .119 151.328);--color-emerald-300:oklch(84.5% .143 164.978);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--color-blue-50:oklch(97% .014 254.604);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-blue-800:oklch(42.4% .199 265.638);--color-blue-900:oklch(37.9% .146 265.522);--color-indigo-50:oklch(96.2% .018 272.314);--color-indigo-500:oklch(58.5% .233 277.117);--color-indigo-700:oklch(45.7% .24 277.023);--color-indigo-800:oklch(39.8% .195 277.366);--color-purple-50:oklch(97.7% .014 308.299);--color-slate-50:oklch(98.4% .003 247.858);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-600:oklch(44.6% .043 257.281);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-800:oklch(27.9% .041 260.031);--color-slate-900:oklch(20.8% .042 265.755);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-white:#fff;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--font-weight-semibold:600;--leading-relaxed:1.625;--radius-lg:.5rem;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components.components;@layer utilities{.pointer-events-auto{pointer-events:auto}.collapse{visibility:collapse}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.bottom-1{bottom:calc(var(--spacing) * 1)}.bottom-2{bottom:calc(var(--spacing) * 2)}.bottom-3{bottom:calc(var(--spacing) * 3)}.bottom-4{bottom:calc(var(--spacing) * 4)}.bottom-5{bottom:calc(var(--spacing) * 5)}.bottom-6{bottom:calc(var(--spacing) * 6)}.z-20{z-index:20}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.h-3\/4{height:75%}.h-8{height:calc(var(--spacing) * 8)}.h-12{height:calc(var(--spacing) * 12)}.h-96{height:calc(var(--spacing) * 96)}.h-\[2px\]{height:2px}.h-\[6px\]{height:6px}.h-full{height:100%}.h-screen{height:100vh}.min-h-0{min-height:calc(var(--spacing) * 0)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-\[2px\]{width:2px}.w-\[6px\]{width:6px}.w-full{width:100%}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.flex-shrink,.shrink{flex-shrink:1}.flex-grow,.grow{flex-grow:1}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-col-resize{cursor:col-resize}.cursor-not-allowed{cursor:not-allowed}.cursor-row-resize{cursor:row-resize}.resize{resize:both}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-hidden{overflow-x:hidden}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.border{border-style:var(--tw-border-style);border-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-none{--tw-border-style:none;border-style:none}.border-blue-200{border-color:var(--color-blue-200)}.border-blue-400{border-color:var(--color-blue-400)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-green-200{border-color:var(--color-green-200)}.border-indigo-500{border-color:var(--color-indigo-500)}.border-red-200{border-color:var(--color-red-200)}.border-slate-300{border-color:var(--color-slate-300)}.bg-blue-50{background-color:var(--color-blue-50)}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-200\/80{background-color:#bedbffcc}@supports (color:color-mix(in lab,red,red)){.bg-blue-200\/80{background-color:color-mix(in oklab,var(--color-blue-200) 80%,transparent)}}.bg-blue-500{background-color:var(--color-blue-500)}.bg-emerald-500{background-color:var(--color-emerald-500)}.bg-gray-50{background-color:var(--color-gray-50)}.bg-green-50{background-color:var(--color-green-50)}.bg-green-100{background-color:var(--color-green-100)}.bg-indigo-50{background-color:var(--color-indigo-50)}.bg-lime-100{background-color:var(--color-lime-100)}.bg-orange-100{background-color:var(--color-orange-100)}.bg-purple-50{background-color:var(--color-purple-50)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-slate-50{background-color:var(--color-slate-50)}.bg-slate-100{background-color:var(--color-slate-100)}.bg-slate-100\/70{background-color:#f1f5f9b3}@supports (color:color-mix(in lab,red,red)){.bg-slate-100\/70{background-color:color-mix(in oklab,var(--color-slate-100) 70%,transparent)}}.bg-slate-600{background-color:var(--color-slate-600)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-white\/70{background-color:#ffffffb3}@supports (color:color-mix(in lab,red,red)){.bg-white\/70{background-color:color-mix(in oklab,var(--color-white) 70%,transparent)}}.bg-yellow-50{background-color:var(--color-yellow-50)}.bg-yellow-100{background-color:var(--color-yellow-100)}.p-1{padding:calc(var(--spacing) * 1)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-\[2px\]{padding-inline:2px}.py-1{padding-block:calc(var(--spacing) * 1)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-\[2px\]{padding-block:2px}.text-center{text-align:center}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-blue-700{color:var(--color-blue-700)}.text-blue-800{color:var(--color-blue-800)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-green-700{color:var(--color-green-700)}.text-green-800{color:var(--color-green-800)}.text-indigo-700{color:var(--color-indigo-700)}.text-indigo-800{color:var(--color-indigo-800)}.text-red-700{color:var(--color-red-700)}.text-red-800{color:var(--color-red-800)}.text-slate-500{color:var(--color-slate-500)}.text-slate-600{color:var(--color-slate-600)}.text-slate-700{color:var(--color-slate-700)}.text-slate-900{color:var(--color-slate-900)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.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,)}.opacity-50{opacity:.5}.shadow-\[0_0_0_1px_rgba\(191\,219\,254\,0\.65\)\,0_1px_4px_rgba\(59\,130\,246\,0\.45\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,#bfdbfea6), 0 1px 4px var(--tw-shadow-color,#3b82f673);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_0_0_1px_rgba\(255\,255\,255\,0\.9\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,#ffffffe6);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-\[0_0_0_1px_rgba\(255\,255\,255\,0\.85\)\,0_1px_3px_rgba\(15\,23\,42\,0\.18\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,#ffffffd9), 0 1px 3px var(--tw-shadow-color,#0f172a2e);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-blue-300{--tw-ring-color:var(--color-blue-300)}.ring-white\/80{--tw-ring-color:#fffc}@supports (color:color-mix(in lab,red,red)){.ring-white\/80{--tw-ring-color:color-mix(in oklab, var(--color-white) 80%, transparent)}}.backdrop-blur{--tw-backdrop-blur:blur(8px);-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,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-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,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.select-none{-webkit-user-select:none;user-select:none}.ring-inset{--tw-ring-inset:inset}@media(hover:hover){.group-hover\:bg-slate-700:is(:where(.group):hover *){background-color:var(--color-slate-700)}.hover\:bg-blue-50:hover{background-color:var(--color-blue-50)}.hover\:bg-blue-600:hover{background-color:var(--color-blue-600)}.hover\:bg-emerald-600:hover{background-color:var(--color-emerald-600)}.hover\:bg-gray-200\/70:hover{background-color:#e5e7ebb3}@supports (color:color-mix(in lab,red,red)){.hover\:bg-gray-200\/70:hover{background-color:color-mix(in oklab,var(--color-gray-200) 70%,transparent)}}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.active\:bg-blue-300\/60:active{background-color:#90c5ff99}@supports (color:color-mix(in lab,red,red)){.active\:bg-blue-300\/60:active{background-color:color-mix(in oklab,var(--color-blue-300) 60%,transparent)}}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-blue-300:disabled{background-color:var(--color-blue-300)}.disabled\:bg-emerald-300:disabled{background-color:var(--color-emerald-300)}@media(min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(prefers-color-scheme:dark){.dark\:border-slate-600{border-color:var(--color-slate-600)}.dark\:bg-blue-300{background-color:var(--color-blue-300)}.dark\:bg-blue-800\/60{background-color:#193cb899}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-800\/60{background-color:color-mix(in oklab,var(--color-blue-800) 60%,transparent)}}.dark\:bg-blue-900\/70{background-color:#1c398eb3}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-900\/70{background-color:color-mix(in oklab,var(--color-blue-900) 70%,transparent)}}.dark\:bg-slate-200{background-color:var(--color-slate-200)}.dark\:bg-slate-800\/50{background-color:#1d293d80}@supports (color:color-mix(in lab,red,red)){.dark\:bg-slate-800\/50{background-color:color-mix(in oklab,var(--color-slate-800) 50%,transparent)}}.dark\:bg-slate-800\/95{background-color:#1d293df2}@supports (color:color-mix(in lab,red,red)){.dark\:bg-slate-800\/95{background-color:color-mix(in oklab,var(--color-slate-800) 95%,transparent)}}.dark\:text-slate-200{color:var(--color-slate-200)}.dark\:text-slate-400{color:var(--color-slate-400)}.dark\:shadow-\[0_0_0_1px_rgba\(15\,23\,42\,0\.55\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,#0f172a8c);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark\:shadow-\[0_0_0_1px_rgba\(147\,197\,253\,0\.5\)\,0_1px_4px_rgba\(59\,130\,246\,0\.55\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,#93c5fd80), 0 1px 4px var(--tw-shadow-color,#3b82f68c);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark\:shadow-\[0_0_0_1px_rgba\(148\,163\,184\,0\.45\)\,0_1px_3px_rgba\(2\,6\,23\,0\.55\)\]{--tw-shadow:0 0 0 1px var(--tw-shadow-color,#94a3b873), 0 1px 3px var(--tw-shadow-color,#0206178c);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.dark\:ring-blue-400{--tw-ring-color:var(--color-blue-400)}.dark\:ring-slate-500\/60{--tw-ring-color:#62748e99}@supports (color:color-mix(in lab,red,red)){.dark\:ring-slate-500\/60{--tw-ring-color:color-mix(in oklab, var(--color-slate-500) 60%, transparent)}}@media(hover:hover){.dark\:group-hover\:bg-slate-100:is(:where(.group):hover *){background-color:var(--color-slate-100)}.dark\:hover\:bg-gray-700\/70:hover{background-color:#364153b3}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-gray-700\/70:hover{background-color:color-mix(in oklab,var(--color-gray-700) 70%,transparent)}}}.dark\:active\:bg-blue-700\/60:active{background-color:#1447e699}@supports (color:color-mix(in lab,red,red)){.dark\:active\:bg-blue-700\/60:active{background-color:color-mix(in oklab,var(--color-blue-700) 60%,transparent)}}}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}