@leavepulse/ui 0.12.0 → 0.14.4

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.
Files changed (160) hide show
  1. package/dist/assets/LpCommandPalette-D315B47-.css +16 -0
  2. package/dist/chunks/LayoutCanvas-Dd20boUG.js +50 -0
  3. package/dist/chunks/LayoutNode-DGpZA9LX.js +225 -0
  4. package/dist/chunks/LpAlert-BAYHDSS1.js +69 -0
  5. package/dist/chunks/LpAppShell-C22642JK.js +170 -0
  6. package/dist/chunks/LpAutocomplete-B9uoCs4t.js +196 -0
  7. package/dist/chunks/LpAvatar-B1Ez0ZAx.js +88 -0
  8. package/dist/chunks/LpBadge-CXzBPnwO.js +42 -0
  9. package/dist/chunks/LpBreadcrumbs-B7Aqatzw.js +42 -0
  10. package/dist/chunks/LpButton-5VXXCjwF.js +101 -0
  11. package/dist/chunks/LpCalendar-B7ZL0ty3.js +150 -0
  12. package/dist/chunks/LpCard-1yvWT9fx.js +66 -0
  13. package/dist/chunks/LpCheckbox-BO0zuuPh.js +55 -0
  14. package/dist/chunks/LpCodeBlock-CSiOSPs8.js +253 -0
  15. package/dist/chunks/LpConfirmDialog-CnHtHWHm.js +62 -0
  16. package/dist/chunks/LpContextMenu-D0mzXqA7.js +151 -0
  17. package/dist/chunks/LpDatePicker-dY2q4dr4.js +122 -0
  18. package/dist/chunks/LpDisclosure-DP6yfgfM.js +76 -0
  19. package/dist/chunks/LpDivider-CUX46KR0.js +43 -0
  20. package/dist/chunks/LpDrawer-CBNVhkg0.js +310 -0
  21. package/dist/chunks/LpDropdownMenu-DJpoqRCb.js +65 -0
  22. package/dist/chunks/LpEmptyState-CrfyRJUG.js +47 -0
  23. package/dist/chunks/LpFormField-Bn-ZwC6z.js +47 -0
  24. package/dist/chunks/LpIcon-CCnX5_2j.js +41 -0
  25. package/dist/chunks/LpInfraNode-C_PHcthC.js +99 -0
  26. package/dist/chunks/LpInput-BauZRyzm.js +136 -0
  27. package/dist/chunks/LpLaneNode-HtnQf2FF.js +33 -0
  28. package/dist/chunks/LpLink-DHwOEp4e.js +36 -0
  29. package/dist/chunks/LpLogViewer-CDBjhXl6.js +469 -0
  30. package/dist/chunks/LpModal-D17Bocq7.js +128 -0
  31. package/dist/chunks/LpNotificationBell-Bm51SkgV.js +232 -0
  32. package/dist/chunks/LpNumberField-C5ar-OwE.js +55 -0
  33. package/dist/chunks/LpOtpInput-DqaT0Z75.js +65 -0
  34. package/dist/chunks/LpPagination-CeGuNe_q.js +132 -0
  35. package/dist/chunks/LpPasswordInput-DIHFw4u0.js +64 -0
  36. package/dist/chunks/LpPhoneInput-LQvVFGMw.js +199 -0
  37. package/dist/chunks/LpPopover-8268UmlC.js +54 -0
  38. package/dist/chunks/LpProgress-BhI0IbjC.js +37 -0
  39. package/dist/chunks/LpRadio-CL-pI0_O.js +39 -0
  40. package/dist/chunks/LpRadioGroup-B4yDTmi6.js +52 -0
  41. package/dist/chunks/LpScrollArea-CcHul_XS.js +67 -0
  42. package/dist/chunks/LpSegmented-BmpanyAo.js +74 -0
  43. package/dist/chunks/LpSelect-DznhPEWX.js +222 -0
  44. package/dist/chunks/LpServiceNode-Dc-Ut5RS.js +51 -0
  45. package/dist/chunks/LpSidebar-CTUdtX7W.js +152 -0
  46. package/dist/chunks/LpSidebarNav-BRwovIc8.js +126 -0
  47. package/dist/chunks/LpSkeleton-eSFIo4kD.js +22 -0
  48. package/dist/chunks/LpSlider-B1SF3pRV.js +53 -0
  49. package/dist/chunks/LpStat-CdbEFOvn.js +88 -0
  50. package/dist/chunks/LpStepper-5dFTrW21.js +53 -0
  51. package/dist/chunks/LpSwitch-v6fnccSM.js +28 -0
  52. package/dist/chunks/LpTable-PHE6UKor.js +229 -0
  53. package/dist/chunks/LpTableOfContents-CjvxPMpU.js +155 -0
  54. package/dist/chunks/LpTabs-D7aTVPOb.js +115 -0
  55. package/dist/chunks/LpTextarea-CRAkhemZ.js +44 -0
  56. package/dist/chunks/LpThemeSwitcher-BgWwfkxs.js +601 -0
  57. package/dist/chunks/LpTilt-DuyKbpkA.js +49 -0
  58. package/dist/chunks/LpToaster-gDB1EaU3.js +196 -0
  59. package/dist/chunks/LpTooltip-CIwoRCag.js +49 -0
  60. package/dist/chunks/LpTopologyCanvas-DVwyQfKa.js +237 -0
  61. package/dist/chunks/LpUptimeBar-CB366k0O.js +127 -0
  62. package/dist/component-names.d.ts +1 -1
  63. package/dist/components/LayoutCanvas.vue.js +4 -0
  64. package/dist/components/LayoutNode.vue.js +4 -0
  65. package/dist/components/LpAlert.vue.js +4 -0
  66. package/dist/components/LpAppShell.vue.js +4 -0
  67. package/dist/components/LpAutocomplete.vue.js +4 -0
  68. package/dist/components/LpAvatar.vue.js +4 -0
  69. package/dist/components/LpBadge.vue.js +4 -0
  70. package/dist/components/LpBreadcrumbs.vue.js +4 -0
  71. package/dist/components/LpButton.vue.js +4 -0
  72. package/dist/components/LpCalendar.vue.js +4 -0
  73. package/dist/components/LpCard.vue.js +4 -0
  74. package/dist/components/LpCheckbox.vue.js +4 -0
  75. package/dist/components/LpCodeBlock.vue.js +4 -0
  76. package/dist/components/LpCommandPalette.vue.js +310 -0
  77. package/dist/components/LpConfirmDialog.vue.js +4 -0
  78. package/dist/components/LpContextMenu.vue.d.ts +7 -0
  79. package/dist/components/LpContextMenu.vue.js +4 -0
  80. package/dist/components/LpDatePicker.vue.js +4 -0
  81. package/dist/components/LpDisclosure.vue.js +4 -0
  82. package/dist/components/LpDivider.vue.js +4 -0
  83. package/dist/components/LpDrawer.vue.d.ts +8 -0
  84. package/dist/components/LpDrawer.vue.js +4 -0
  85. package/dist/components/LpDropdownMenu.vue.js +4 -0
  86. package/dist/components/LpEmptyState.vue.js +4 -0
  87. package/dist/components/LpFormField.vue.js +4 -0
  88. package/dist/components/LpIcon.vue.js +4 -0
  89. package/dist/components/LpInfraNode.vue.js +4 -0
  90. package/dist/components/LpInput.vue.js +4 -0
  91. package/dist/components/LpLaneNode.vue.js +4 -0
  92. package/dist/components/LpLink.vue.js +4 -0
  93. package/dist/components/LpLogViewer.vue.js +4 -0
  94. package/dist/components/LpModal.vue.js +4 -0
  95. package/dist/components/LpNotificationBell.vue.js +4 -0
  96. package/dist/components/LpNumberField.vue.js +4 -0
  97. package/dist/components/LpOtpInput.vue.js +4 -0
  98. package/dist/components/LpPagination.vue.js +4 -0
  99. package/dist/components/LpPasswordInput.vue.js +4 -0
  100. package/dist/components/LpPhoneInput.vue.js +4 -0
  101. package/dist/components/LpPopover.vue.js +4 -0
  102. package/dist/components/LpProgress.vue.js +4 -0
  103. package/dist/components/LpRadio.vue.js +4 -0
  104. package/dist/components/LpRadioGroup.vue.js +4 -0
  105. package/dist/components/LpScrollArea.vue.js +4 -0
  106. package/dist/components/LpSegmented.vue.js +4 -0
  107. package/dist/components/LpSelect.vue.d.ts +2 -0
  108. package/dist/components/LpSelect.vue.js +4 -0
  109. package/dist/components/LpServiceNode.vue.js +4 -0
  110. package/dist/components/LpSidebar.vue.js +4 -0
  111. package/dist/components/LpSidebarNav.vue.js +4 -0
  112. package/dist/components/LpSkeleton.vue.js +4 -0
  113. package/dist/components/LpSlider.vue.js +4 -0
  114. package/dist/components/LpStat.vue.js +4 -0
  115. package/dist/components/LpStepper.vue.js +4 -0
  116. package/dist/components/LpSwitch.vue.js +4 -0
  117. package/dist/components/LpTable.vue.js +4 -0
  118. package/dist/components/LpTableOfContents.vue.js +4 -0
  119. package/dist/components/LpTabs.vue.d.ts +4 -2
  120. package/dist/components/LpTabs.vue.js +4 -0
  121. package/dist/components/LpTextarea.vue.js +4 -0
  122. package/dist/components/LpThemeSwitcher.vue.js +4 -0
  123. package/dist/components/LpTilt.vue.d.ts +31 -0
  124. package/dist/components/LpTilt.vue.js +4 -0
  125. package/dist/components/LpToaster.vue.js +4 -0
  126. package/dist/components/LpTooltip.vue.js +4 -0
  127. package/dist/components/LpTopologyCanvas.vue.js +4 -0
  128. package/dist/components/LpUptimeBar.vue.js +4 -0
  129. package/dist/components/codeHighlight.js +230 -0
  130. package/dist/{countries-data-9rgqJWHo.js → components/countries-data.js} +2 -2
  131. package/dist/components/countries.js +38 -0
  132. package/dist/components/dropdown.js +14 -0
  133. package/dist/components/sidebar.js +1 -0
  134. package/dist/composables/useInputFilter.js +44 -0
  135. package/dist/composables/usePillTransition.js +11 -0
  136. package/dist/composables/useTilt.d.ts +23 -0
  137. package/dist/composables/useTilt.js +57 -0
  138. package/dist/composables/useToast.d.ts +2 -0
  139. package/dist/composables/useToast.js +49 -0
  140. package/dist/index.d.ts +3 -0
  141. package/dist/index.js +197 -17596
  142. package/dist/layout/registry.js +10 -0
  143. package/dist/layout/tree.js +192 -0
  144. package/dist/layout/useLayout.js +61 -0
  145. package/package.json +5 -3
  146. package/src/component-names.ts +1 -0
  147. package/src/components/LpContextMenu.vue +14 -4
  148. package/src/components/LpDrawer.vue +29 -1
  149. package/src/components/LpLogViewer.vue +161 -31
  150. package/src/components/LpSelect.vue +0 -0
  151. package/src/components/LpSidebar.vue +5 -2
  152. package/src/components/LpTabs.vue +19 -6
  153. package/src/components/LpThemeSwitcher.vue +13 -2
  154. package/src/components/LpTilt.vue +61 -0
  155. package/src/components/LpToaster.vue +11 -4
  156. package/src/composables/useTilt.ts +103 -0
  157. package/src/composables/useToast.ts +2 -0
  158. package/src/index.ts +3 -0
  159. package/src/nuxt.ts +10 -1
  160. package/dist/index.css +0 -1
@@ -0,0 +1,469 @@
1
+ import { defineComponent, computed, ref, watch, nextTick, onMounted, openBlock, createElementBlock, createVNode, normalizeStyle, withCtx, createElementVNode, toDisplayString, createCommentVNode, Fragment, createBlock, resolveDynamicComponent, TransitionGroup, mergeProps, renderList, normalizeClass, createTextVNode, Transition } from "vue";
2
+ import { _ as _sfc_main$3 } from "./LpContextMenu-D0mzXqA7.js";
3
+ import { _ as _sfc_main$2 } from "./LpIcon-CCnX5_2j.js";
4
+ import { _ as _sfc_main$1 } from "./LpScrollArea-CcHul_XS.js";
5
+ const _hoisted_1 = { class: "relative overflow-hidden rounded-card border border-line bg-surface font-mono text-xs leading-relaxed" };
6
+ const _hoisted_2 = {
7
+ key: 0,
8
+ class: "flex h-full flex-col items-center justify-center gap-1 px-4 py-10 text-center"
9
+ };
10
+ const _hoisted_3 = { class: "text-muted" };
11
+ const _hoisted_4 = {
12
+ key: 0,
13
+ class: "text-muted/70"
14
+ };
15
+ const _hoisted_5 = {
16
+ key: 0,
17
+ class: "flex items-center justify-center gap-2 py-2 text-muted"
18
+ };
19
+ const _hoisted_6 = ["data-log-row"];
20
+ const _hoisted_7 = {
21
+ key: 0,
22
+ class: "mr-3 w-10 shrink-0 select-none text-right tabular-nums text-muted/70"
23
+ };
24
+ const _hoisted_8 = {
25
+ key: 1,
26
+ class: "mr-3 shrink-0 tabular-nums text-muted"
27
+ };
28
+ const _hoisted_9 = {
29
+ key: 3,
30
+ class: "mr-2 shrink-0 text-muted-strong"
31
+ };
32
+ const _hoisted_10 = {
33
+ key: 0,
34
+ class: "rounded-xs bg-accent/30 text-ink"
35
+ };
36
+ const _hoisted_11 = ["title"];
37
+ const _hoisted_12 = {
38
+ key: 0,
39
+ class: "absolute inset-x-0 bottom-3 flex justify-center"
40
+ };
41
+ const ROW_HEIGHT = 20;
42
+ const VIRTUALISE_ABOVE = 200;
43
+ const OVERSCAN = 12;
44
+ const NEAR = 120;
45
+ const TOP_NEAR = 80;
46
+ const _sfc_main = /* @__PURE__ */ defineComponent({
47
+ __name: "LpLogViewer",
48
+ props: {
49
+ lines: {},
50
+ showTime: { type: Boolean, default: true },
51
+ showLevel: { type: Boolean, default: true },
52
+ lineNumbers: { type: Boolean, default: false },
53
+ wrap: { type: Boolean, default: false },
54
+ highlight: {},
55
+ filterMatches: { type: Boolean },
56
+ compact: { type: Boolean },
57
+ tail: { type: Boolean, default: true },
58
+ height: { default: "20rem" },
59
+ emptyLabel: { default: "No logs yet" },
60
+ loadingOlder: { type: Boolean },
61
+ loadOlder: { type: Boolean },
62
+ rowMenu: { type: Boolean, default: true },
63
+ extraRowItems: {},
64
+ parse: { type: Boolean, default: false }
65
+ },
66
+ emits: ["filter", "reach-top"],
67
+ setup(__props, { emit: __emit }) {
68
+ const props = __props;
69
+ const emit = __emit;
70
+ const levelMeta = {
71
+ trace: { rail: "bg-line-strong", text: "text-muted", chip: "text-muted", sel: "selection:bg-muted/40" },
72
+ debug: { rail: "bg-line-strong", text: "text-muted-strong", chip: "text-muted-strong", sel: "selection:bg-muted-strong/40" },
73
+ info: { rail: "bg-brand", text: "text-ink", chip: "text-brand", sel: "selection:bg-brand/40" },
74
+ success: { rail: "bg-action", text: "text-ink", chip: "text-action", sel: "selection:bg-action/40" },
75
+ warn: { rail: "bg-accent", text: "text-ink", chip: "text-accent", sel: "selection:bg-accent/40" },
76
+ error: { rail: "bg-danger", text: "text-ink", chip: "text-danger", sel: "selection:bg-danger/40" },
77
+ fatal: { rail: "bg-danger", text: "text-ink", chip: "rounded-xs bg-danger px-1 text-ink-inverse", sel: "selection:bg-danger/40" }
78
+ };
79
+ function metaFor(level) {
80
+ return levelMeta[level ?? "info"];
81
+ }
82
+ const LEVEL_WORDS = {
83
+ trace: "trace",
84
+ debug: "debug",
85
+ info: "info",
86
+ notice: "info",
87
+ warn: "warn",
88
+ warning: "warn",
89
+ error: "error",
90
+ err: "error",
91
+ fatal: "fatal",
92
+ crit: "fatal",
93
+ critical: "fatal",
94
+ panic: "fatal",
95
+ success: "success",
96
+ ok: "success"
97
+ };
98
+ const TS_RE = /^\[?(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[.,]\d{1,9})?(?:Z|[+-]\d{2}:?\d{2})?)\]?\s+/;
99
+ const LEVEL_RE = /^([A-Za-z]{2,8})\s*:?\s+/;
100
+ const SOURCE_RE = /^\[([^\]]{1,40})\]\s+/;
101
+ function parseLine(line) {
102
+ if (!props.parse) return line;
103
+ let msg = line.message;
104
+ let time = line.time;
105
+ let level = line.level;
106
+ let source = line.source;
107
+ if (time == null) {
108
+ const m = TS_RE.exec(msg);
109
+ if (m) {
110
+ time = m[1];
111
+ msg = msg.slice(m[0].length);
112
+ }
113
+ } else {
114
+ const m = TS_RE.exec(msg);
115
+ if (m) msg = msg.slice(m[0].length);
116
+ }
117
+ const lm = LEVEL_RE.exec(msg);
118
+ if (lm) {
119
+ const canon = LEVEL_WORDS[lm[1].toLowerCase()];
120
+ if (canon) {
121
+ if (level == null) level = canon;
122
+ msg = msg.slice(lm[0].length);
123
+ }
124
+ }
125
+ if (source == null) {
126
+ const sm = SOURCE_RE.exec(msg);
127
+ if (sm) {
128
+ source = sm[1];
129
+ msg = msg.slice(sm[0].length);
130
+ }
131
+ }
132
+ return msg === line.message && time === line.time && level === line.level && source === line.source ? line : { ...line, message: msg, time, level, source };
133
+ }
134
+ const parsedLines = computed(
135
+ () => props.parse ? props.lines.map(parseLine) : props.lines
136
+ );
137
+ const visibleLines = computed(() => {
138
+ const term = props.filterMatches ? props.highlight?.trim().toLowerCase() : "";
139
+ const filtered = parsedLines.value.map((line, n) => ({ line, n })).filter(({ line }) => !term || line.message.toLowerCase().includes(term));
140
+ if (!props.compact) return filtered.map((row) => ({ ...row, count: 1 }));
141
+ const folded = [];
142
+ for (const row of filtered) {
143
+ const prev = folded[folded.length - 1];
144
+ if (prev && prev.line.message === row.line.message && prev.line.level === row.line.level && prev.line.source === row.line.source) {
145
+ prev.count++;
146
+ } else {
147
+ folded.push({ ...row, count: 1 });
148
+ }
149
+ }
150
+ return folded;
151
+ });
152
+ const filteredEmpty = computed(
153
+ () => props.lines.length > 0 && visibleLines.value.length === 0
154
+ );
155
+ function fmtTime(time) {
156
+ if (time == null) return "";
157
+ if (typeof time === "number" || /^\d+$/.test(time)) {
158
+ const d = new Date(Number(time));
159
+ if (Number.isNaN(d.getTime())) return String(time);
160
+ return d.toLocaleTimeString(void 0, { hour12: false }) + "." + String(d.getMilliseconds()).padStart(3, "0");
161
+ }
162
+ return time;
163
+ }
164
+ async function copy(text) {
165
+ try {
166
+ await navigator.clipboard?.writeText(text);
167
+ } catch {
168
+ }
169
+ }
170
+ function lineText(line) {
171
+ return [fmtTime(line.time), line.level?.toUpperCase(), line.source && `[${line.source}]`, line.message].filter(Boolean).join(" ");
172
+ }
173
+ const menuRow = ref(null);
174
+ const menuItems = computed(
175
+ () => menuRow.value ? menuFor(menuRow.value.line, menuRow.value.n) : []
176
+ );
177
+ function onRowContext(e) {
178
+ if (!props.rowMenu) return;
179
+ const el = e.target?.closest("[data-log-row]");
180
+ const n = el ? Number(el.dataset.logRow) : NaN;
181
+ const row = Number.isNaN(n) ? void 0 : visibleLines.value.find((r) => r.n === n);
182
+ menuRow.value = row ? { line: row.line, n: row.n } : null;
183
+ }
184
+ function onRowPointerDown(e) {
185
+ if (e.button === 0) e.stopPropagation();
186
+ }
187
+ function menuFor(line, index) {
188
+ if (!props.rowMenu) return [];
189
+ const items = [
190
+ { label: "Copy message", icon: "lucide:copy", onSelect: () => copy(line.message) },
191
+ { label: "Copy line", icon: "lucide:clipboard-list", onSelect: () => copy(lineText(line)) }
192
+ ];
193
+ if (line.time != null) {
194
+ items.push({ label: "Copy timestamp", icon: "lucide:clock", onSelect: () => copy(fmtTime(line.time)) });
195
+ }
196
+ if (line.source) {
197
+ items.push({
198
+ label: `Filter by [${line.source}]`,
199
+ icon: "lucide:filter",
200
+ separatorBefore: true,
201
+ onSelect: () => emit("filter", { source: line.source })
202
+ });
203
+ }
204
+ if (line.level) {
205
+ items.push({
206
+ label: `Filter by ${line.level}`,
207
+ icon: "lucide:filter",
208
+ separatorBefore: !line.source,
209
+ onSelect: () => emit("filter", { level: line.level })
210
+ });
211
+ }
212
+ const extra = props.extraRowItems?.(line, index) ?? [];
213
+ if (extra.length) extra[0] = { ...extra[0], separatorBefore: true };
214
+ return [...items, ...extra];
215
+ }
216
+ function splitHighlight(message) {
217
+ const term = props.highlight?.trim();
218
+ if (!term) return [{ text: message, hit: false }];
219
+ const parts = [];
220
+ const lower = message.toLowerCase();
221
+ const needle = term.toLowerCase();
222
+ let i = 0;
223
+ while (i < message.length) {
224
+ const at = lower.indexOf(needle, i);
225
+ if (at === -1) {
226
+ parts.push({ text: message.slice(i), hit: false });
227
+ break;
228
+ }
229
+ if (at > i) parts.push({ text: message.slice(i, at), hit: false });
230
+ parts.push({ text: message.slice(at, at + needle.length), hit: true });
231
+ i = at + needle.length;
232
+ }
233
+ return parts;
234
+ }
235
+ const virtualise = computed(() => !props.wrap && visibleLines.value.length > VIRTUALISE_ABOVE);
236
+ const viewportHeight = ref(0);
237
+ const scrollTop = ref(0);
238
+ const virtualRange = computed(() => {
239
+ const total = visibleLines.value.length;
240
+ if (!virtualise.value) return { start: 0, end: total };
241
+ const visibleCount = Math.ceil(viewportHeight.value / ROW_HEIGHT) + 1;
242
+ const start = Math.max(0, Math.floor(scrollTop.value / ROW_HEIGHT) - OVERSCAN);
243
+ const end = Math.min(total, start + visibleCount + OVERSCAN * 2);
244
+ return { start, end };
245
+ });
246
+ const renderedLines = computed(() => {
247
+ if (!virtualise.value) return visibleLines.value;
248
+ const { start, end } = virtualRange.value;
249
+ return visibleLines.value.slice(start, end);
250
+ });
251
+ const padTop = computed(
252
+ () => virtualise.value ? virtualRange.value.start * ROW_HEIGHT : 0
253
+ );
254
+ const padBottom = computed(
255
+ () => virtualise.value ? (visibleLines.value.length - virtualRange.value.end) * ROW_HEIGHT : 0
256
+ );
257
+ const scrollRef = ref(null);
258
+ const pinned = ref(true);
259
+ function viewport() {
260
+ return scrollRef.value?.viewportEl ?? null;
261
+ }
262
+ function atBottom(el) {
263
+ return el.scrollHeight - el.scrollTop - el.clientHeight <= NEAR;
264
+ }
265
+ let topArmed = true;
266
+ function onScroll(e) {
267
+ const el = e.target;
268
+ pinned.value = atBottom(el);
269
+ scrollTop.value = el.scrollTop;
270
+ if (el.clientHeight !== viewportHeight.value) viewportHeight.value = el.clientHeight;
271
+ if (!props.loadOlder) return;
272
+ if (el.scrollTop <= TOP_NEAR) {
273
+ if (topArmed && !props.loadingOlder) {
274
+ topArmed = false;
275
+ emit("reach-top");
276
+ }
277
+ } else {
278
+ topArmed = true;
279
+ }
280
+ }
281
+ let prevScrollHeight = 0;
282
+ let prevScrollTop = 0;
283
+ function captureScrollAnchor() {
284
+ const el = viewport();
285
+ if (!el) return;
286
+ prevScrollHeight = el.scrollHeight;
287
+ prevScrollTop = el.scrollTop;
288
+ }
289
+ function restoreScrollAnchor() {
290
+ const el = viewport();
291
+ if (!el) return;
292
+ const added = el.scrollHeight - prevScrollHeight;
293
+ if (added > 0) el.scrollTop = prevScrollTop + added;
294
+ }
295
+ function rideToBottom(smooth = true, force = false) {
296
+ const el = viewport();
297
+ if (!el) return;
298
+ const reduce = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
299
+ const far = !force && el.scrollHeight - el.scrollTop - el.clientHeight > el.clientHeight;
300
+ el.scrollTo({ top: el.scrollHeight, behavior: smooth && !reduce && !far ? "smooth" : "auto" });
301
+ }
302
+ function scrollToBottom() {
303
+ rideToBottom(true, true);
304
+ pinned.value = true;
305
+ }
306
+ watch(
307
+ () => props.lines.length,
308
+ () => {
309
+ if (!pinned.value) captureScrollAnchor();
310
+ },
311
+ { flush: "pre" }
312
+ );
313
+ watch(
314
+ () => props.lines.length,
315
+ async () => {
316
+ await nextTick();
317
+ if (props.tail && pinned.value) {
318
+ requestAnimationFrame(() => rideToBottom(true));
319
+ } else {
320
+ restoreScrollAnchor();
321
+ }
322
+ }
323
+ );
324
+ const showJump = computed(() => props.tail && !pinned.value && props.lines.length > 0);
325
+ onMounted(() => {
326
+ const el = viewport();
327
+ if (el) viewportHeight.value = el.clientHeight;
328
+ });
329
+ const transitionProps = {
330
+ tag: "ol",
331
+ "enter-active-class": "transition duration-200 ease-[var(--ease-emphasized)]",
332
+ "enter-from-class": "-translate-x-1 opacity-0",
333
+ "leave-active-class": "overflow-hidden transition-all duration-200 ease-[var(--ease-emphasized)]",
334
+ "leave-from-class": "max-h-8 opacity-100",
335
+ "leave-to-class": "max-h-0 -translate-y-1 opacity-0",
336
+ "move-class": "transition-transform duration-200 ease-[var(--ease-emphasized)]"
337
+ };
338
+ return (_ctx, _cache) => {
339
+ return openBlock(), createElementBlock("div", _hoisted_1, [
340
+ createVNode(_sfc_main$1, {
341
+ ref_key: "scrollRef",
342
+ ref: scrollRef,
343
+ fade: "",
344
+ "content-class": __props.wrap ? "" : "min-w-max",
345
+ style: normalizeStyle({ height: __props.height }),
346
+ onScroll
347
+ }, {
348
+ default: withCtx(() => [
349
+ __props.lines.length === 0 || filteredEmpty.value ? (openBlock(), createElementBlock("div", _hoisted_2, [
350
+ createElementVNode("span", _hoisted_3, toDisplayString(filteredEmpty.value ? "No lines match" : __props.emptyLabel), 1),
351
+ filteredEmpty.value ? (openBlock(), createElementBlock("span", _hoisted_4, "“" + toDisplayString(__props.highlight) + "”", 1)) : createCommentVNode("", true)
352
+ ])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
353
+ __props.loadingOlder ? (openBlock(), createElementBlock("div", _hoisted_5, [
354
+ createVNode(_sfc_main$2, {
355
+ name: "lucide:loader-circle",
356
+ size: 14,
357
+ class: "animate-spin"
358
+ }),
359
+ _cache[0] || (_cache[0] = createElementVNode("span", { class: "text-[11px]" }, "loading older…", -1))
360
+ ])) : createCommentVNode("", true),
361
+ createVNode(_sfc_main$3, {
362
+ items: menuItems.value,
363
+ always: __props.rowMenu
364
+ }, {
365
+ default: withCtx(() => [
366
+ (openBlock(), createBlock(resolveDynamicComponent(virtualise.value ? "ol" : TransitionGroup), mergeProps(virtualise.value ? {} : transitionProps, {
367
+ class: "py-1",
368
+ onContextmenu: onRowContext,
369
+ onPointerdown: onRowPointerDown
370
+ }), {
371
+ default: withCtx(() => [
372
+ virtualise.value && padTop.value > 0 ? (openBlock(), createElementBlock("li", {
373
+ key: "pad-top",
374
+ style: normalizeStyle({ height: padTop.value + "px" }),
375
+ "aria-hidden": "true"
376
+ }, null, 4)) : createCommentVNode("", true),
377
+ (openBlock(true), createElementBlock(Fragment, null, renderList(renderedLines.value, ({ line, n, count }) => {
378
+ return openBlock(), createElementBlock("li", {
379
+ key: n,
380
+ "data-log-row": n,
381
+ class: normalizeClass(["group relative flex items-start gap-0 px-0 leading-5 transition-colors hover:bg-surface-soft/60", metaFor(line.level).sel])
382
+ }, [
383
+ createElementVNode("span", {
384
+ class: normalizeClass(["absolute inset-y-0 left-0 w-0.5", metaFor(line.level).rail]),
385
+ "aria-hidden": "true"
386
+ }, null, 2),
387
+ _cache[1] || (_cache[1] = createElementVNode("span", {
388
+ class: "mr-2 w-0.5 shrink-0",
389
+ "aria-hidden": "true"
390
+ }, null, -1)),
391
+ __props.lineNumbers ? (openBlock(), createElementBlock("span", _hoisted_7, toDisplayString(n + 1), 1)) : createCommentVNode("", true),
392
+ __props.showTime ? (openBlock(), createElementBlock("span", _hoisted_8, toDisplayString(fmtTime(line.time)), 1)) : createCommentVNode("", true),
393
+ __props.showLevel ? (openBlock(), createElementBlock("span", {
394
+ key: 2,
395
+ class: normalizeClass(["mr-3 w-12 shrink-0 text-[10px] font-semibold uppercase tracking-[0.06em]", metaFor(line.level).chip])
396
+ }, toDisplayString(line.level ?? "info"), 3)) : createCommentVNode("", true),
397
+ line.source ? (openBlock(), createElementBlock("span", _hoisted_9, "[" + toDisplayString(line.source) + "]", 1)) : createCommentVNode("", true),
398
+ createElementVNode("span", {
399
+ class: normalizeClass(["min-w-0 pr-3", [metaFor(line.level).text, __props.wrap ? "whitespace-pre-wrap break-words" : "whitespace-pre"]])
400
+ }, [
401
+ (openBlock(true), createElementBlock(Fragment, null, renderList(splitHighlight(line.message), (part, p) => {
402
+ return openBlock(), createElementBlock(Fragment, { key: p }, [
403
+ part.hit ? (openBlock(), createElementBlock("mark", _hoisted_10, toDisplayString(part.text), 1)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
404
+ createTextVNode(toDisplayString(part.text), 1)
405
+ ], 64))
406
+ ], 64);
407
+ }), 128)),
408
+ createVNode(Transition, {
409
+ "enter-active-class": "inline-block transition duration-200 ease-[var(--ease-emphasized)]",
410
+ "enter-from-class": "scale-50 opacity-0"
411
+ }, {
412
+ default: withCtx(() => [
413
+ count > 1 ? (openBlock(), createElementBlock("span", {
414
+ key: count,
415
+ class: "ml-2 select-none rounded-pill bg-surface-soft px-1.5 align-[1px] text-[10px] font-semibold tabular-nums text-muted-strong",
416
+ title: `${count} identical lines`
417
+ }, "×" + toDisplayString(count), 9, _hoisted_11)) : createCommentVNode("", true)
418
+ ]),
419
+ _: 2
420
+ }, 1024)
421
+ ], 2)
422
+ ], 10, _hoisted_6);
423
+ }), 128)),
424
+ virtualise.value && padBottom.value > 0 ? (openBlock(), createElementBlock("li", {
425
+ key: "pad-bottom",
426
+ style: normalizeStyle({ height: padBottom.value + "px" }),
427
+ "aria-hidden": "true"
428
+ }, null, 4)) : createCommentVNode("", true)
429
+ ]),
430
+ _: 1
431
+ }, 16))
432
+ ]),
433
+ _: 1
434
+ }, 8, ["items", "always"])
435
+ ], 64))
436
+ ]),
437
+ _: 1
438
+ }, 8, ["content-class", "style"]),
439
+ createVNode(Transition, {
440
+ "enter-active-class": "transition duration-200 ease-[var(--ease-emphasized)]",
441
+ "enter-from-class": "translate-y-3 scale-90 opacity-0",
442
+ "leave-active-class": "transition duration-150 ease-in",
443
+ "leave-to-class": "translate-y-3 scale-90 opacity-0"
444
+ }, {
445
+ default: withCtx(() => [
446
+ showJump.value ? (openBlock(), createElementBlock("div", _hoisted_12, [
447
+ createElementVNode("button", {
448
+ type: "button",
449
+ class: "group flex items-center gap-1.5 rounded-pill border border-line bg-surface-overlay px-3 py-1 text-xs font-medium text-ink shadow-[var(--shadow-panel)] backdrop-blur outline-none transition-colors hover:bg-surface-soft focus-visible:ring-2 focus-visible:ring-ring",
450
+ onClick: scrollToBottom
451
+ }, [
452
+ createVNode(_sfc_main$2, {
453
+ name: "lucide:arrow-down",
454
+ size: 14,
455
+ class: "transition-transform duration-200 ease-[var(--ease-emphasized)] group-hover:translate-y-0.5"
456
+ }),
457
+ _cache[2] || (_cache[2] = createTextVNode(" Jump to latest ", -1))
458
+ ])
459
+ ])) : createCommentVNode("", true)
460
+ ]),
461
+ _: 1
462
+ })
463
+ ]);
464
+ };
465
+ }
466
+ });
467
+ export {
468
+ _sfc_main as _
469
+ };
@@ -0,0 +1,128 @@
1
+ import { defineComponent, computed, useSlots, openBlock, createBlock, unref, withCtx, createVNode, normalizeStyle, normalizeClass, createElementBlock, createElementVNode, renderSlot, createTextVNode, toDisplayString, createCommentVNode } from "vue";
2
+ import { DialogRoot, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose } from "reka-ui";
3
+ import { CLOSE_ICON } from "../components/dropdown.js";
4
+ import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
5
+ import { _ as _sfc_main$2 } from "./LpScrollArea-CcHul_XS.js";
6
+ const _hoisted_1 = {
7
+ key: 0,
8
+ class: "flex shrink-0 items-start justify-between gap-4 p-5 pb-3"
9
+ };
10
+ const _hoisted_2 = { class: "flex flex-col gap-1" };
11
+ const _hoisted_3 = {
12
+ key: 3,
13
+ class: "flex shrink-0 justify-end gap-2 p-5 pt-4"
14
+ };
15
+ const _sfc_main = /* @__PURE__ */ defineComponent({
16
+ __name: "LpModal",
17
+ props: {
18
+ open: { type: Boolean },
19
+ title: {},
20
+ description: {},
21
+ size: { default: "md" },
22
+ width: {},
23
+ fillBody: { type: Boolean }
24
+ },
25
+ emits: ["update:open"],
26
+ setup(__props) {
27
+ const props = __props;
28
+ const widthClass = computed(() => {
29
+ if (props.width) return "";
30
+ return {
31
+ sm: "w-[min(92vw,24rem)]",
32
+ md: "w-[min(92vw,28rem)]",
33
+ lg: "w-[min(92vw,34rem)]",
34
+ xl: "w-[min(92vw,42rem)]",
35
+ "2xl": "w-[min(94vw,56rem)]",
36
+ "3xl": "w-[min(95vw,72rem)]",
37
+ full: "w-[96vw]"
38
+ }[props.size];
39
+ });
40
+ const slots = useSlots();
41
+ const bodyPad = computed(
42
+ () => [
43
+ "px-5",
44
+ props.title || slots.title ? "" : "pt-5",
45
+ slots.footer ? "" : "pb-5"
46
+ ].filter(Boolean).join(" ")
47
+ );
48
+ return (_ctx, _cache) => {
49
+ return openBlock(), createBlock(unref(DialogRoot), {
50
+ open: __props.open,
51
+ "onUpdate:open": _cache[0] || (_cache[0] = (v) => _ctx.$emit("update:open", v))
52
+ }, {
53
+ default: withCtx(() => [
54
+ createVNode(unref(DialogPortal), null, {
55
+ default: withCtx(() => [
56
+ createVNode(unref(DialogOverlay), { class: "fixed inset-0 z-(--z-overlay) bg-black/50 backdrop-blur-sm data-[state=open]:animate-[fade-in_150ms_ease] data-[state=closed]:animate-[fade-out_130ms_ease]" }),
57
+ createVNode(unref(DialogContent), {
58
+ class: normalizeClass(["fixed left-1/2 top-1/2 z-(--z-modal) flex max-h-[min(90vh,calc(100dvh-2rem))] -translate-x-1/2 -translate-y-1/2 flex-col rounded-card border border-line bg-surface-raised shadow-panel outline-none data-[state=open]:animate-[pop-in_160ms_var(--ease-emphasized)] data-[state=closed]:animate-[pop-out_130ms_ease]", widthClass.value]),
59
+ style: normalizeStyle(__props.width ? { width: __props.width } : void 0)
60
+ }, {
61
+ default: withCtx(() => [
62
+ __props.title || _ctx.$slots.title ? (openBlock(), createElementBlock("header", _hoisted_1, [
63
+ createElementVNode("div", _hoisted_2, [
64
+ createVNode(unref(DialogTitle), { class: "text-base font-semibold text-ink" }, {
65
+ default: withCtx(() => [
66
+ renderSlot(_ctx.$slots, "title", {}, () => [
67
+ createTextVNode(toDisplayString(__props.title), 1)
68
+ ])
69
+ ]),
70
+ _: 3
71
+ }),
72
+ __props.description ? (openBlock(), createBlock(unref(DialogDescription), {
73
+ key: 0,
74
+ class: "text-sm text-muted"
75
+ }, {
76
+ default: withCtx(() => [
77
+ createTextVNode(toDisplayString(__props.description), 1)
78
+ ]),
79
+ _: 1
80
+ })) : createCommentVNode("", true)
81
+ ]),
82
+ createVNode(unref(DialogClose), {
83
+ class: "group flex shrink-0 items-center rounded-md p-1 text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:ring-2 focus-visible:ring-ring",
84
+ "aria-label": "Close"
85
+ }, {
86
+ default: withCtx(() => [
87
+ createVNode(_sfc_main$1, {
88
+ name: "lucide:x",
89
+ size: 18,
90
+ class: normalizeClass(unref(CLOSE_ICON))
91
+ }, null, 8, ["class"])
92
+ ]),
93
+ _: 1
94
+ })
95
+ ])) : createCommentVNode("", true),
96
+ __props.fillBody ? (openBlock(), createElementBlock("div", {
97
+ key: 1,
98
+ class: normalizeClass(["flex min-h-0 flex-1 flex-col overflow-hidden text-sm text-ink/90", bodyPad.value])
99
+ }, [
100
+ renderSlot(_ctx.$slots, "default")
101
+ ], 2)) : (openBlock(), createBlock(_sfc_main$2, {
102
+ key: 2,
103
+ class: "min-h-0 flex-1 text-sm text-ink/90",
104
+ "content-class": bodyPad.value
105
+ }, {
106
+ default: withCtx(() => [
107
+ renderSlot(_ctx.$slots, "default")
108
+ ]),
109
+ _: 3
110
+ }, 8, ["content-class"])),
111
+ _ctx.$slots.footer ? (openBlock(), createElementBlock("footer", _hoisted_3, [
112
+ renderSlot(_ctx.$slots, "footer")
113
+ ])) : createCommentVNode("", true)
114
+ ]),
115
+ _: 3
116
+ }, 8, ["class", "style"])
117
+ ]),
118
+ _: 3
119
+ })
120
+ ]),
121
+ _: 3
122
+ }, 8, ["open"]);
123
+ };
124
+ }
125
+ });
126
+ export {
127
+ _sfc_main as _
128
+ };