@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.
- package/dist/assets/LpCommandPalette-D315B47-.css +16 -0
- package/dist/chunks/LayoutCanvas-Dd20boUG.js +50 -0
- package/dist/chunks/LayoutNode-DGpZA9LX.js +225 -0
- package/dist/chunks/LpAlert-BAYHDSS1.js +69 -0
- package/dist/chunks/LpAppShell-C22642JK.js +170 -0
- package/dist/chunks/LpAutocomplete-B9uoCs4t.js +196 -0
- package/dist/chunks/LpAvatar-B1Ez0ZAx.js +88 -0
- package/dist/chunks/LpBadge-CXzBPnwO.js +42 -0
- package/dist/chunks/LpBreadcrumbs-B7Aqatzw.js +42 -0
- package/dist/chunks/LpButton-5VXXCjwF.js +101 -0
- package/dist/chunks/LpCalendar-B7ZL0ty3.js +150 -0
- package/dist/chunks/LpCard-1yvWT9fx.js +66 -0
- package/dist/chunks/LpCheckbox-BO0zuuPh.js +55 -0
- package/dist/chunks/LpCodeBlock-CSiOSPs8.js +253 -0
- package/dist/chunks/LpConfirmDialog-CnHtHWHm.js +62 -0
- package/dist/chunks/LpContextMenu-D0mzXqA7.js +151 -0
- package/dist/chunks/LpDatePicker-dY2q4dr4.js +122 -0
- package/dist/chunks/LpDisclosure-DP6yfgfM.js +76 -0
- package/dist/chunks/LpDivider-CUX46KR0.js +43 -0
- package/dist/chunks/LpDrawer-CBNVhkg0.js +310 -0
- package/dist/chunks/LpDropdownMenu-DJpoqRCb.js +65 -0
- package/dist/chunks/LpEmptyState-CrfyRJUG.js +47 -0
- package/dist/chunks/LpFormField-Bn-ZwC6z.js +47 -0
- package/dist/chunks/LpIcon-CCnX5_2j.js +41 -0
- package/dist/chunks/LpInfraNode-C_PHcthC.js +99 -0
- package/dist/chunks/LpInput-BauZRyzm.js +136 -0
- package/dist/chunks/LpLaneNode-HtnQf2FF.js +33 -0
- package/dist/chunks/LpLink-DHwOEp4e.js +36 -0
- package/dist/chunks/LpLogViewer-CDBjhXl6.js +469 -0
- package/dist/chunks/LpModal-D17Bocq7.js +128 -0
- package/dist/chunks/LpNotificationBell-Bm51SkgV.js +232 -0
- package/dist/chunks/LpNumberField-C5ar-OwE.js +55 -0
- package/dist/chunks/LpOtpInput-DqaT0Z75.js +65 -0
- package/dist/chunks/LpPagination-CeGuNe_q.js +132 -0
- package/dist/chunks/LpPasswordInput-DIHFw4u0.js +64 -0
- package/dist/chunks/LpPhoneInput-LQvVFGMw.js +199 -0
- package/dist/chunks/LpPopover-8268UmlC.js +54 -0
- package/dist/chunks/LpProgress-BhI0IbjC.js +37 -0
- package/dist/chunks/LpRadio-CL-pI0_O.js +39 -0
- package/dist/chunks/LpRadioGroup-B4yDTmi6.js +52 -0
- package/dist/chunks/LpScrollArea-CcHul_XS.js +67 -0
- package/dist/chunks/LpSegmented-BmpanyAo.js +74 -0
- package/dist/chunks/LpSelect-DznhPEWX.js +222 -0
- package/dist/chunks/LpServiceNode-Dc-Ut5RS.js +51 -0
- package/dist/chunks/LpSidebar-CTUdtX7W.js +152 -0
- package/dist/chunks/LpSidebarNav-BRwovIc8.js +126 -0
- package/dist/chunks/LpSkeleton-eSFIo4kD.js +22 -0
- package/dist/chunks/LpSlider-B1SF3pRV.js +53 -0
- package/dist/chunks/LpStat-CdbEFOvn.js +88 -0
- package/dist/chunks/LpStepper-5dFTrW21.js +53 -0
- package/dist/chunks/LpSwitch-v6fnccSM.js +28 -0
- package/dist/chunks/LpTable-PHE6UKor.js +229 -0
- package/dist/chunks/LpTableOfContents-CjvxPMpU.js +155 -0
- package/dist/chunks/LpTabs-D7aTVPOb.js +115 -0
- package/dist/chunks/LpTextarea-CRAkhemZ.js +44 -0
- package/dist/chunks/LpThemeSwitcher-BgWwfkxs.js +601 -0
- package/dist/chunks/LpTilt-DuyKbpkA.js +49 -0
- package/dist/chunks/LpToaster-gDB1EaU3.js +196 -0
- package/dist/chunks/LpTooltip-CIwoRCag.js +49 -0
- package/dist/chunks/LpTopologyCanvas-DVwyQfKa.js +237 -0
- package/dist/chunks/LpUptimeBar-CB366k0O.js +127 -0
- package/dist/component-names.d.ts +1 -1
- package/dist/components/LayoutCanvas.vue.js +4 -0
- package/dist/components/LayoutNode.vue.js +4 -0
- package/dist/components/LpAlert.vue.js +4 -0
- package/dist/components/LpAppShell.vue.js +4 -0
- package/dist/components/LpAutocomplete.vue.js +4 -0
- package/dist/components/LpAvatar.vue.js +4 -0
- package/dist/components/LpBadge.vue.js +4 -0
- package/dist/components/LpBreadcrumbs.vue.js +4 -0
- package/dist/components/LpButton.vue.js +4 -0
- package/dist/components/LpCalendar.vue.js +4 -0
- package/dist/components/LpCard.vue.js +4 -0
- package/dist/components/LpCheckbox.vue.js +4 -0
- package/dist/components/LpCodeBlock.vue.js +4 -0
- package/dist/components/LpCommandPalette.vue.js +310 -0
- package/dist/components/LpConfirmDialog.vue.js +4 -0
- package/dist/components/LpContextMenu.vue.d.ts +7 -0
- package/dist/components/LpContextMenu.vue.js +4 -0
- package/dist/components/LpDatePicker.vue.js +4 -0
- package/dist/components/LpDisclosure.vue.js +4 -0
- package/dist/components/LpDivider.vue.js +4 -0
- package/dist/components/LpDrawer.vue.d.ts +8 -0
- package/dist/components/LpDrawer.vue.js +4 -0
- package/dist/components/LpDropdownMenu.vue.js +4 -0
- package/dist/components/LpEmptyState.vue.js +4 -0
- package/dist/components/LpFormField.vue.js +4 -0
- package/dist/components/LpIcon.vue.js +4 -0
- package/dist/components/LpInfraNode.vue.js +4 -0
- package/dist/components/LpInput.vue.js +4 -0
- package/dist/components/LpLaneNode.vue.js +4 -0
- package/dist/components/LpLink.vue.js +4 -0
- package/dist/components/LpLogViewer.vue.js +4 -0
- package/dist/components/LpModal.vue.js +4 -0
- package/dist/components/LpNotificationBell.vue.js +4 -0
- package/dist/components/LpNumberField.vue.js +4 -0
- package/dist/components/LpOtpInput.vue.js +4 -0
- package/dist/components/LpPagination.vue.js +4 -0
- package/dist/components/LpPasswordInput.vue.js +4 -0
- package/dist/components/LpPhoneInput.vue.js +4 -0
- package/dist/components/LpPopover.vue.js +4 -0
- package/dist/components/LpProgress.vue.js +4 -0
- package/dist/components/LpRadio.vue.js +4 -0
- package/dist/components/LpRadioGroup.vue.js +4 -0
- package/dist/components/LpScrollArea.vue.js +4 -0
- package/dist/components/LpSegmented.vue.js +4 -0
- package/dist/components/LpSelect.vue.d.ts +2 -0
- package/dist/components/LpSelect.vue.js +4 -0
- package/dist/components/LpServiceNode.vue.js +4 -0
- package/dist/components/LpSidebar.vue.js +4 -0
- package/dist/components/LpSidebarNav.vue.js +4 -0
- package/dist/components/LpSkeleton.vue.js +4 -0
- package/dist/components/LpSlider.vue.js +4 -0
- package/dist/components/LpStat.vue.js +4 -0
- package/dist/components/LpStepper.vue.js +4 -0
- package/dist/components/LpSwitch.vue.js +4 -0
- package/dist/components/LpTable.vue.js +4 -0
- package/dist/components/LpTableOfContents.vue.js +4 -0
- package/dist/components/LpTabs.vue.d.ts +4 -2
- package/dist/components/LpTabs.vue.js +4 -0
- package/dist/components/LpTextarea.vue.js +4 -0
- package/dist/components/LpThemeSwitcher.vue.js +4 -0
- package/dist/components/LpTilt.vue.d.ts +31 -0
- package/dist/components/LpTilt.vue.js +4 -0
- package/dist/components/LpToaster.vue.js +4 -0
- package/dist/components/LpTooltip.vue.js +4 -0
- package/dist/components/LpTopologyCanvas.vue.js +4 -0
- package/dist/components/LpUptimeBar.vue.js +4 -0
- package/dist/components/codeHighlight.js +230 -0
- package/dist/{countries-data-9rgqJWHo.js → components/countries-data.js} +2 -2
- package/dist/components/countries.js +38 -0
- package/dist/components/dropdown.js +14 -0
- package/dist/components/sidebar.js +1 -0
- package/dist/composables/useInputFilter.js +44 -0
- package/dist/composables/usePillTransition.js +11 -0
- package/dist/composables/useTilt.d.ts +23 -0
- package/dist/composables/useTilt.js +57 -0
- package/dist/composables/useToast.d.ts +2 -0
- package/dist/composables/useToast.js +49 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +197 -17596
- package/dist/layout/registry.js +10 -0
- package/dist/layout/tree.js +192 -0
- package/dist/layout/useLayout.js +61 -0
- package/package.json +5 -3
- package/src/component-names.ts +1 -0
- package/src/components/LpContextMenu.vue +14 -4
- package/src/components/LpDrawer.vue +29 -1
- package/src/components/LpLogViewer.vue +161 -31
- package/src/components/LpSelect.vue +0 -0
- package/src/components/LpSidebar.vue +5 -2
- package/src/components/LpTabs.vue +19 -6
- package/src/components/LpThemeSwitcher.vue +13 -2
- package/src/components/LpTilt.vue +61 -0
- package/src/components/LpToaster.vue +11 -4
- package/src/composables/useTilt.ts +103 -0
- package/src/composables/useToast.ts +2 -0
- package/src/index.ts +3 -0
- package/src/nuxt.ts +10 -1
- package/dist/index.css +0 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* viewport element and re-emits the native scroll event — exactly what tailing
|
|
8
8
|
* needs.
|
|
9
9
|
*/
|
|
10
|
-
import { computed, nextTick, ref, watch } from "vue"
|
|
10
|
+
import { computed, nextTick, onMounted, ref, TransitionGroup, watch } from "vue"
|
|
11
11
|
import LpContextMenu, { type ContextMenuItemDef } from "./LpContextMenu.vue"
|
|
12
12
|
import LpIcon from "./LpIcon.vue"
|
|
13
13
|
import LpScrollArea from "./LpScrollArea.vue"
|
|
@@ -108,14 +108,18 @@ const emit = defineEmits<{
|
|
|
108
108
|
// Level → gutter rail + text colour. Reads only semantic tokens so re-skinning
|
|
109
109
|
// just works. trace/debug stay muted; success is green; warn amber; error/fatal
|
|
110
110
|
// red (fatal gets a filled chip to stand apart from plain error).
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
111
|
+
// `sel` tints ::selection in the row's semantic colour (info→brand, warn→accent,
|
|
112
|
+
// error/fatal→danger) via Tailwind's `selection:` variant — the highlight then
|
|
113
|
+
// matches the level instead of the browser default. Static classes (not built
|
|
114
|
+
// from a var) so Tailwind emits them from the @source scan.
|
|
115
|
+
const levelMeta: Record<LogLevel, { rail: string; text: string; chip: string; sel: string }> = {
|
|
116
|
+
trace: { rail: "bg-line-strong", text: "text-muted", chip: "text-muted", sel: "selection:bg-muted/40" },
|
|
117
|
+
debug: { rail: "bg-line-strong", text: "text-muted-strong", chip: "text-muted-strong", sel: "selection:bg-muted-strong/40" },
|
|
118
|
+
info: { rail: "bg-brand", text: "text-ink", chip: "text-brand", sel: "selection:bg-brand/40" },
|
|
119
|
+
success: { rail: "bg-action", text: "text-ink", chip: "text-action", sel: "selection:bg-action/40" },
|
|
120
|
+
warn: { rail: "bg-accent", text: "text-ink", chip: "text-accent", sel: "selection:bg-accent/40" },
|
|
121
|
+
error: { rail: "bg-danger", text: "text-ink", chip: "text-danger", sel: "selection:bg-danger/40" },
|
|
122
|
+
fatal: { rail: "bg-danger", text: "text-ink", chip: "rounded-xs bg-danger px-1 text-ink-inverse", sel: "selection:bg-danger/40" },
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
function metaFor(level?: LogLevel) {
|
|
@@ -264,6 +268,34 @@ function lineText(line: LogLine): string {
|
|
|
264
268
|
.join(" ")
|
|
265
269
|
}
|
|
266
270
|
|
|
271
|
+
// One LpContextMenu wraps the whole list (not each row) so a mouse drag can
|
|
272
|
+
// select text across rows — a per-row reka trigger would bind pointerdown and
|
|
273
|
+
// pin the selection to one row. On right-click we resolve which row is under the
|
|
274
|
+
// cursor (data-log-row) and rebuild the menu items for it before reka opens.
|
|
275
|
+
const menuRow = ref<{ line: LogLine; n: number } | null>(null)
|
|
276
|
+
const menuItems = computed<ContextMenuItemDef[]>(() =>
|
|
277
|
+
menuRow.value ? menuFor(menuRow.value.line, menuRow.value.n) : [],
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
// Resolve the right-clicked row so menuItems reflects it before reka's trigger
|
|
281
|
+
// opens the menu (runs on the same contextmenu event, ahead of the open).
|
|
282
|
+
function onRowContext(e: MouseEvent) {
|
|
283
|
+
if (!props.rowMenu) return
|
|
284
|
+
const el = (e.target as HTMLElement | null)?.closest<HTMLElement>("[data-log-row]")
|
|
285
|
+
const n = el ? Number(el.dataset.logRow) : NaN
|
|
286
|
+
const row = Number.isNaN(n) ? undefined : visibleLines.value.find((r) => r.n === n)
|
|
287
|
+
menuRow.value = row ? { line: row.line, n: row.n } : null
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// vaul (the drawer lib) treats a left-button pointerdown on a side drawer as the
|
|
291
|
+
// start of a swipe-to-dismiss (`shouldDrag` returns true unconditionally for
|
|
292
|
+
// left/right drawers) and its preventDefault kills text selection. Stop that
|
|
293
|
+
// pointerdown from bubbling to vaul — left button only, so scroll and the
|
|
294
|
+
// right-click context menu still reach it.
|
|
295
|
+
function onRowPointerDown(e: PointerEvent) {
|
|
296
|
+
if (e.button === 0) e.stopPropagation()
|
|
297
|
+
}
|
|
298
|
+
|
|
267
299
|
// Built-in copy actions + optional "filter by" + consumer extras. Rebuilt per
|
|
268
300
|
// right-click so it reflects the line under the cursor.
|
|
269
301
|
function menuFor(line: LogLine, index: number): ContextMenuItemDef[] {
|
|
@@ -322,6 +354,52 @@ function splitHighlight(message: string): Part[] {
|
|
|
322
354
|
return parts
|
|
323
355
|
}
|
|
324
356
|
|
|
357
|
+
// ── virtualisation ───────────────────────────────────────────────────
|
|
358
|
+
// A live log stream can reach tens of thousands of rows; rendering them all
|
|
359
|
+
// (a v-for over the whole list, each row a flex box with a highlight-split and,
|
|
360
|
+
// worse, a TransitionGroup computing FLIP offsets for every node on each push)
|
|
361
|
+
// pins the main thread and the viewer stops responding. So past a threshold we
|
|
362
|
+
// render only the slice inside the viewport plus a small overscan, padding the
|
|
363
|
+
// rest with two spacer rows. This needs a uniform row height, which only holds
|
|
364
|
+
// when lines don't wrap — with `wrap` (or short lists) we fall back to the
|
|
365
|
+
// plain full render that keeps the fold/enter transitions.
|
|
366
|
+
const ROW_HEIGHT = 20 // px; matches leading-5 (1.25rem) monospace rows
|
|
367
|
+
const VIRTUALISE_ABOVE = 200 // rows; below this the full render is cheap enough
|
|
368
|
+
const OVERSCAN = 12 // rows rendered beyond each edge to cover fast scrolls
|
|
369
|
+
|
|
370
|
+
const virtualise = computed(() => !props.wrap && visibleLines.value.length > VIRTUALISE_ABOVE)
|
|
371
|
+
|
|
372
|
+
const viewportHeight = ref(0)
|
|
373
|
+
const scrollTop = ref(0)
|
|
374
|
+
|
|
375
|
+
// Window of rows to actually render when virtualising: [start, end) by index
|
|
376
|
+
// into visibleLines, widened by OVERSCAN on both sides and clamped.
|
|
377
|
+
const virtualRange = computed(() => {
|
|
378
|
+
const total = visibleLines.value.length
|
|
379
|
+
if (!virtualise.value) return { start: 0, end: total }
|
|
380
|
+
const visibleCount = Math.ceil(viewportHeight.value / ROW_HEIGHT) + 1
|
|
381
|
+
const start = Math.max(0, Math.floor(scrollTop.value / ROW_HEIGHT) - OVERSCAN)
|
|
382
|
+
const end = Math.min(total, start + visibleCount + OVERSCAN * 2)
|
|
383
|
+
return { start, end }
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
const renderedLines = computed(() => {
|
|
387
|
+
if (!virtualise.value) return visibleLines.value
|
|
388
|
+
const { start, end } = virtualRange.value
|
|
389
|
+
return visibleLines.value.slice(start, end)
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
// Spacer heights that stand in for the un-rendered rows above/below the window,
|
|
393
|
+
// so the scrollbar and scroll position stay honest.
|
|
394
|
+
const padTop = computed(() =>
|
|
395
|
+
virtualise.value ? virtualRange.value.start * ROW_HEIGHT : 0,
|
|
396
|
+
)
|
|
397
|
+
const padBottom = computed(() =>
|
|
398
|
+
virtualise.value
|
|
399
|
+
? (visibleLines.value.length - virtualRange.value.end) * ROW_HEIGHT
|
|
400
|
+
: 0,
|
|
401
|
+
)
|
|
402
|
+
|
|
325
403
|
// ── tail / auto-scroll ───────────────────────────────────────────────
|
|
326
404
|
const scrollRef = ref<{ viewportEl: HTMLElement | null } | null>(null)
|
|
327
405
|
const pinned = ref(true) // currently glued to the bottom
|
|
@@ -349,6 +427,10 @@ let topArmed = true
|
|
|
349
427
|
function onScroll(e: Event) {
|
|
350
428
|
const el = e.target as HTMLElement
|
|
351
429
|
pinned.value = atBottom(el)
|
|
430
|
+
// Feed the virtual window. Reading clientHeight here keeps it in sync with
|
|
431
|
+
// runtime resizes (drawer drag, window resize) without a ResizeObserver.
|
|
432
|
+
scrollTop.value = el.scrollTop
|
|
433
|
+
if (el.clientHeight !== viewportHeight.value) viewportHeight.value = el.clientHeight
|
|
352
434
|
if (!props.loadOlder) return
|
|
353
435
|
if (el.scrollTop <= TOP_NEAR) {
|
|
354
436
|
if (topArmed && !props.loadingOlder) {
|
|
@@ -422,6 +504,28 @@ watch(
|
|
|
422
504
|
)
|
|
423
505
|
|
|
424
506
|
const showJump = computed(() => props.tail && !pinned.value && props.lines.length > 0)
|
|
507
|
+
|
|
508
|
+
// Seed the viewport height before the first scroll so the initial virtual
|
|
509
|
+
// window is correct (otherwise a tall list would render a single overscan slice
|
|
510
|
+
// until the user scrolls).
|
|
511
|
+
onMounted(() => {
|
|
512
|
+
const el = viewport()
|
|
513
|
+
if (el) viewportHeight.value = el.clientHeight
|
|
514
|
+
})
|
|
515
|
+
|
|
516
|
+
// FLIP / enter / leave classes for the full-render TransitionGroup, bound via
|
|
517
|
+
// v-bind so the same list element can fall back to a plain <ol> when virtualised
|
|
518
|
+
// (a component :is can't carry TransitionGroup-only props inertly otherwise).
|
|
519
|
+
const transitionProps = {
|
|
520
|
+
tag: "ol",
|
|
521
|
+
"enter-active-class": "transition duration-200 ease-[var(--ease-emphasized)]",
|
|
522
|
+
"enter-from-class": "-translate-x-1 opacity-0",
|
|
523
|
+
"leave-active-class":
|
|
524
|
+
"overflow-hidden transition-all duration-200 ease-[var(--ease-emphasized)]",
|
|
525
|
+
"leave-from-class": "max-h-8 opacity-100",
|
|
526
|
+
"leave-to-class": "max-h-0 -translate-y-1 opacity-0",
|
|
527
|
+
"move-class": "transition-transform duration-200 ease-[var(--ease-emphasized)]",
|
|
528
|
+
}
|
|
425
529
|
</script>
|
|
426
530
|
|
|
427
531
|
<template>
|
|
@@ -455,33 +559,53 @@ const showJump = computed(() => props.tail && !pinned.value && props.lines.lengt
|
|
|
455
559
|
<span class="text-[11px]">loading older…</span>
|
|
456
560
|
</div>
|
|
457
561
|
|
|
458
|
-
<!--
|
|
562
|
+
<!-- ONE context menu wraps the whole list (not each row): @contextmenu
|
|
563
|
+
resolves which row was right-clicked so menuItems reflects it before
|
|
564
|
+
reka opens. @pointerdown stops a left-button drag from reaching vaul
|
|
565
|
+
(which would start a swipe-dismiss and cancel text selection); the
|
|
566
|
+
right button still bubbles so the menu opens. reka's own trigger
|
|
567
|
+
pointerdown is touch/pen-only, so it doesn't interfere with the mouse.
|
|
568
|
+
Leave collapses a folded-away duplicate (max-height + opacity + a small
|
|
459
569
|
lift) while the survivors FLIP-slide up via move-class — so toggling
|
|
460
|
-
`compact` reads as the dupes melting into the kept line
|
|
461
|
-
<
|
|
462
|
-
|
|
570
|
+
`compact` reads as the dupes melting into the kept line. -->
|
|
571
|
+
<!-- Component switches on `virtualise`: a plain <ol> when we render only a
|
|
572
|
+
windowed slice (FLIP/enter/leave transitions can't run over a moving
|
|
573
|
+
virtual window, and computing them for thousands of nodes is exactly
|
|
574
|
+
the cost we're avoiding), or a TransitionGroup for the full render on
|
|
575
|
+
small lists, where the fold/enter animations are affordable and nice.
|
|
576
|
+
Spacer rows stand in for the un-rendered slices above/below. -->
|
|
577
|
+
<LpContextMenu :items="menuItems" :always="rowMenu">
|
|
578
|
+
<component
|
|
579
|
+
:is="virtualise ? 'ol' : TransitionGroup"
|
|
580
|
+
v-bind="virtualise ? {} : transitionProps"
|
|
463
581
|
class="py-1"
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
leave-active-class="overflow-hidden transition-all duration-200 ease-[var(--ease-emphasized)]"
|
|
467
|
-
leave-from-class="max-h-8 opacity-100"
|
|
468
|
-
leave-to-class="max-h-0 -translate-y-1 opacity-0"
|
|
469
|
-
move-class="transition-transform duration-200 ease-[var(--ease-emphasized)]"
|
|
582
|
+
@contextmenu="onRowContext"
|
|
583
|
+
@pointerdown="onRowPointerDown"
|
|
470
584
|
>
|
|
471
|
-
<LpContextMenu
|
|
472
|
-
v-for="{ line, n, count } in visibleLines"
|
|
473
|
-
:key="n"
|
|
474
|
-
:items="menuFor(line, n)"
|
|
475
|
-
>
|
|
476
585
|
<li
|
|
477
|
-
|
|
586
|
+
v-if="virtualise && padTop > 0"
|
|
587
|
+
key="pad-top"
|
|
588
|
+
:style="{ height: padTop + 'px' }"
|
|
589
|
+
aria-hidden="true"
|
|
590
|
+
/>
|
|
591
|
+
<li
|
|
592
|
+
v-for="{ line, n, count } in renderedLines"
|
|
593
|
+
:key="n"
|
|
594
|
+
:data-log-row="n"
|
|
595
|
+
class="group relative flex items-start gap-0 px-0 leading-5 transition-colors hover:bg-surface-soft/60"
|
|
596
|
+
:class="metaFor(line.level).sel"
|
|
478
597
|
>
|
|
479
598
|
<!-- level rail -->
|
|
599
|
+
<!-- Level rail: absolutely positioned so it spans the FULL row height
|
|
600
|
+
regardless of content — self-stretch only reached the flex line box
|
|
601
|
+
(shorter than the relaxed line-height), which left visible gaps
|
|
602
|
+
between rows. A spacer keeps the text indented past it. -->
|
|
480
603
|
<span
|
|
481
|
-
class="
|
|
604
|
+
class="absolute inset-y-0 left-0 w-0.5"
|
|
482
605
|
:class="metaFor(line.level).rail"
|
|
483
606
|
aria-hidden="true"
|
|
484
607
|
/>
|
|
608
|
+
<span class="mr-2 w-0.5 shrink-0" aria-hidden="true" />
|
|
485
609
|
|
|
486
610
|
<!-- line number -->
|
|
487
611
|
<span
|
|
@@ -492,20 +616,20 @@ const showJump = computed(() => props.tail && !pinned.value && props.lines.lengt
|
|
|
492
616
|
<!-- timestamp -->
|
|
493
617
|
<span
|
|
494
618
|
v-if="showTime"
|
|
495
|
-
class="mr-3 shrink-0
|
|
619
|
+
class="mr-3 shrink-0 tabular-nums text-muted"
|
|
496
620
|
>{{ fmtTime(line.time) }}</span>
|
|
497
621
|
|
|
498
622
|
<!-- level chip -->
|
|
499
623
|
<span
|
|
500
624
|
v-if="showLevel"
|
|
501
|
-
class="mr-3 w-12 shrink-0
|
|
625
|
+
class="mr-3 w-12 shrink-0 text-[10px] font-semibold uppercase tracking-[0.06em]"
|
|
502
626
|
:class="metaFor(line.level).chip"
|
|
503
627
|
>{{ line.level ?? "info" }}</span>
|
|
504
628
|
|
|
505
629
|
<!-- source tag -->
|
|
506
630
|
<span
|
|
507
631
|
v-if="line.source"
|
|
508
|
-
class="mr-2 shrink-0
|
|
632
|
+
class="mr-2 shrink-0 text-muted-strong"
|
|
509
633
|
>[{{ line.source }}]</span>
|
|
510
634
|
|
|
511
635
|
<!-- message -->
|
|
@@ -536,8 +660,14 @@ const showJump = computed(() => props.tail && !pinned.value && props.lines.lengt
|
|
|
536
660
|
</Transition>
|
|
537
661
|
</span>
|
|
538
662
|
</li>
|
|
539
|
-
|
|
540
|
-
|
|
663
|
+
<li
|
|
664
|
+
v-if="virtualise && padBottom > 0"
|
|
665
|
+
key="pad-bottom"
|
|
666
|
+
:style="{ height: padBottom + 'px' }"
|
|
667
|
+
aria-hidden="true"
|
|
668
|
+
/>
|
|
669
|
+
</component>
|
|
670
|
+
</LpContextMenu>
|
|
541
671
|
</template>
|
|
542
672
|
</LpScrollArea>
|
|
543
673
|
|
|
Binary file
|
|
@@ -122,10 +122,12 @@ defineOptions({ inheritAttrs: false })
|
|
|
122
122
|
aria-label="Sidebar"
|
|
123
123
|
v-bind="$attrs"
|
|
124
124
|
>
|
|
125
|
+
<!-- Edge-to-edge (negative margins cancel the rail p-3) and h-16 to match the
|
|
126
|
+
app header, so its bottom hairline continues the header's border-b. -->
|
|
125
127
|
<div
|
|
126
128
|
v-if="$slots.header"
|
|
127
|
-
class="shrink-0"
|
|
128
|
-
:class="divider ? '
|
|
129
|
+
class="-mx-3 -mt-3 mb-3 flex h-16 shrink-0 items-center px-3"
|
|
130
|
+
:class="divider ? 'border-b border-line' : ''"
|
|
129
131
|
>
|
|
130
132
|
<slot name="header" />
|
|
131
133
|
</div>
|
|
@@ -163,6 +165,7 @@ defineOptions({ inheritAttrs: false })
|
|
|
163
165
|
direction="left"
|
|
164
166
|
size="sm"
|
|
165
167
|
:edge-open="edgeOpen"
|
|
168
|
+
:edge-breakpoint="mobileBreakpoint"
|
|
166
169
|
@update:open="(v) => emit('update:open', v)"
|
|
167
170
|
>
|
|
168
171
|
<div class="flex h-full flex-col gap-1">
|
|
@@ -20,8 +20,10 @@ withDefaults(
|
|
|
20
20
|
* "contained" (default) wraps the triggers in a bordered, filled bar.
|
|
21
21
|
* "plain" drops the container chrome — a flat row of tabs with only the
|
|
22
22
|
* sliding pill marking the active one (for nav bars over a custom surface).
|
|
23
|
+
* "underline" is a flat row over a hairline baseline, marking the active
|
|
24
|
+
* tab with a sliding bottom bar instead of a pill (page-section nav style).
|
|
23
25
|
*/
|
|
24
|
-
variant?: "contained" | "plain"
|
|
26
|
+
variant?: "contained" | "plain" | "underline"
|
|
25
27
|
/** Tint the active pill + label with the brand colour (nav-bar style). */
|
|
26
28
|
accent?: boolean
|
|
27
29
|
/** Stretch the bar to full width with equal-share triggers. */
|
|
@@ -67,6 +69,7 @@ function pillUnder(value: string, active?: string): boolean {
|
|
|
67
69
|
:class="[
|
|
68
70
|
block ? 'flex w-full' : 'inline-flex',
|
|
69
71
|
variant === 'contained' ? 'rounded-control border border-line bg-surface-soft p-1' : '',
|
|
72
|
+
variant === 'underline' ? 'border-b border-line' : '',
|
|
70
73
|
]"
|
|
71
74
|
@pointerleave="hovered = null"
|
|
72
75
|
>
|
|
@@ -74,22 +77,32 @@ function pillUnder(value: string, active?: string): boolean {
|
|
|
74
77
|
v-for="item in items"
|
|
75
78
|
:key="item.value"
|
|
76
79
|
:value="item.value"
|
|
77
|
-
class="relative inline-flex items-center justify-center gap-1.5
|
|
80
|
+
class="relative inline-flex items-center justify-center gap-1.5 text-sm outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring"
|
|
78
81
|
:class="[
|
|
79
82
|
block ? 'flex-1' : '',
|
|
83
|
+
variant === 'underline' ? 'rounded-t-md px-3 pt-1.5 pb-2' : 'rounded-md px-3 py-1.5',
|
|
80
84
|
accent ? 'data-[state=active]:text-brand' : 'data-[state=active]:text-ink',
|
|
81
85
|
hovered === item.value ? 'text-ink' : 'text-muted',
|
|
82
86
|
]"
|
|
83
87
|
@pointerenter="hovered = item.value"
|
|
84
88
|
>
|
|
89
|
+
<!-- Active marker: a bottom bar in "underline", a filled pill otherwise.
|
|
90
|
+
Both slide between tabs via the shared motion layoutId. -->
|
|
85
91
|
<Motion
|
|
86
92
|
v-if="pillUnder(item.value, modelValue)"
|
|
87
93
|
:layout-id="pillId"
|
|
88
94
|
:transition="pillTransition"
|
|
89
|
-
class="
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
:class="variant === 'underline'
|
|
96
|
+
? [
|
|
97
|
+
'absolute inset-x-0 -bottom-px z-0 h-0.5 rounded-full',
|
|
98
|
+
accent ? 'bg-brand' : 'bg-ink',
|
|
99
|
+
]
|
|
100
|
+
: [
|
|
101
|
+
'absolute inset-0 z-0 rounded-md shadow-sm',
|
|
102
|
+
accent
|
|
103
|
+
? 'border border-brand/35 bg-brand/12'
|
|
104
|
+
: 'border border-line bg-surface-raised',
|
|
105
|
+
]"
|
|
93
106
|
/>
|
|
94
107
|
<LpIcon v-if="item.icon" :name="item.icon" :size="14" class="relative z-10" />
|
|
95
108
|
<span class="relative z-10">{{ item.label }}</span>
|
|
@@ -90,6 +90,10 @@ const ICON_SIZE = { sm: 15, md: 16, lg: 18 } as const
|
|
|
90
90
|
|
|
91
91
|
const wantsLabel = computed(() => props.showLabel || props.variant === "pill")
|
|
92
92
|
|
|
93
|
+
// Labelless icon variant renders as a plain square icon button (no border/fill),
|
|
94
|
+
// so it sits inline with other toolbar icon buttons. Other variants stay framed.
|
|
95
|
+
const bareIcon = computed(() => props.variant === "icon" && !wantsLabel.value)
|
|
96
|
+
|
|
93
97
|
// Apply a theme through the kit's self-managed setter (it persists + updates
|
|
94
98
|
// the shared `current`), then notify. The reveal animates from the click point.
|
|
95
99
|
function applyTheme(t: TokenSet, ev?: MouseEvent) {
|
|
@@ -138,8 +142,15 @@ const triggerLabel = computed(
|
|
|
138
142
|
type="button"
|
|
139
143
|
:aria-label="triggerLabel"
|
|
140
144
|
:title="triggerLabel"
|
|
141
|
-
class="inline-flex items-center gap-2 rounded-control
|
|
142
|
-
:class="
|
|
145
|
+
class="inline-flex items-center justify-center gap-2 rounded-control outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring"
|
|
146
|
+
:class="
|
|
147
|
+
bareIcon
|
|
148
|
+
? [SIZES[size], 'text-muted hover:bg-surface-soft hover:text-ink']
|
|
149
|
+
: [
|
|
150
|
+
'border border-line bg-surface-raised px-1.5 text-ink hover:border-line-strong',
|
|
151
|
+
wantsLabel ? 'py-1 pr-2.5' : '',
|
|
152
|
+
]
|
|
153
|
+
"
|
|
143
154
|
@click="cycle"
|
|
144
155
|
>
|
|
145
156
|
<!-- swatch: brand over surface, hinting the theme at a glance -->
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/*
|
|
3
|
+
* LpTilt — a tactile 3D-tilt wrapper. Its slot content rotates toward the
|
|
4
|
+
* pointer and lifts on hover (see useTilt), so any element — a card, a stat
|
|
5
|
+
* tile, a floating island illustration — gains hand-held depth by wrapping it.
|
|
6
|
+
*
|
|
7
|
+
* <LpTilt :max="12" :lift="14" glare>
|
|
8
|
+
* <LpCard>…</LpCard>
|
|
9
|
+
* </LpTilt>
|
|
10
|
+
*
|
|
11
|
+
* The wrapper owns the `perspective` stage; the inner element is what actually
|
|
12
|
+
* tilts, so sibling content outside the wrapper is unaffected. Honors reduced
|
|
13
|
+
* motion (falls back to a flat, still element).
|
|
14
|
+
*/
|
|
15
|
+
import { computed } from "vue"
|
|
16
|
+
import { useTilt } from "../composables/useTilt"
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(
|
|
19
|
+
defineProps<{
|
|
20
|
+
/** Max rotation toward the pointer, in degrees. */
|
|
21
|
+
max?: number
|
|
22
|
+
/** Upward lift on hover, in px. */
|
|
23
|
+
lift?: number
|
|
24
|
+
/** Scale on hover. */
|
|
25
|
+
scale?: number
|
|
26
|
+
/** Render a pointer-tracking glare highlight over the content. */
|
|
27
|
+
glare?: boolean
|
|
28
|
+
/** Perspective depth for the 3D stage, in px. Lower = stronger. */
|
|
29
|
+
perspective?: number
|
|
30
|
+
}>(),
|
|
31
|
+
{ max: 10, lift: 8, scale: 1.03, glare: false, perspective: 900 },
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
const tilt = useTilt({
|
|
35
|
+
max: props.max,
|
|
36
|
+
lift: props.lift,
|
|
37
|
+
scale: props.scale,
|
|
38
|
+
glare: props.glare,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const stageStyle = computed(() => ({ perspective: `${props.perspective}px` }))
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<div :style="stageStyle" class="lp-tilt-stage">
|
|
46
|
+
<div
|
|
47
|
+
class="lp-tilt-inner relative"
|
|
48
|
+
:style="tilt.style.value"
|
|
49
|
+
@pointermove="tilt.onPointerMove"
|
|
50
|
+
@pointerenter="tilt.onPointerEnter"
|
|
51
|
+
@pointerleave="tilt.onPointerLeave"
|
|
52
|
+
>
|
|
53
|
+
<slot />
|
|
54
|
+
<div
|
|
55
|
+
v-if="glare"
|
|
56
|
+
class="pointer-events-none absolute inset-0 rounded-[inherit] mix-blend-overlay"
|
|
57
|
+
:style="tilt.glareStyle.value"
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</template>
|
|
@@ -178,9 +178,10 @@ async function runAction(t: ToastItem, action: NonNullable<ToastItem["actions"]>
|
|
|
178
178
|
v-for="(action, i) in t.actions"
|
|
179
179
|
:key="i"
|
|
180
180
|
type="button"
|
|
181
|
-
class="rounded-control border border-line bg-surface-soft px-2.5 py-1 text-xs font-medium text-ink outline-none transition-[border-color,background-color,scale] duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] hover:border-line-strong hover:bg-surface-soft/70 active:scale-95 motion-reduce:active:scale-100 focus-visible:ring-2 focus-visible:ring-ring"
|
|
181
|
+
class="inline-flex items-center gap-1.5 rounded-control border border-line bg-surface-soft px-2.5 py-1 text-xs font-medium text-ink outline-none transition-[border-color,background-color,scale] duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] hover:border-line-strong hover:bg-surface-soft/70 active:scale-95 motion-reduce:active:scale-100 focus-visible:ring-2 focus-visible:ring-ring"
|
|
182
182
|
@click="runAction(t, action)"
|
|
183
183
|
>
|
|
184
|
+
<LpIcon v-if="action.icon" :name="action.icon" :size="14" />
|
|
184
185
|
{{ action.label }}
|
|
185
186
|
</button>
|
|
186
187
|
</div>
|
|
@@ -209,8 +210,14 @@ async function runAction(t: ToastItem, action: NonNullable<ToastItem["actions"]>
|
|
|
209
210
|
/>
|
|
210
211
|
</ToastRoot>
|
|
211
212
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
213
|
+
<!-- Teleported to body so the viewport escapes any stacking context the
|
|
214
|
+
consumer's app root creates (e.g. an opaque `#app { z-index }` wrapper).
|
|
215
|
+
Modals portal to body too, so both compare at the same root level and
|
|
216
|
+
--z-toast (200) > --z-modal (110) wins as intended. -->
|
|
217
|
+
<Teleport to="body">
|
|
218
|
+
<ToastViewport
|
|
219
|
+
class="fixed bottom-0 right-0 z-(--z-toast) flex w-[min(92vw,22rem)] flex-col gap-2 p-4 outline-none"
|
|
220
|
+
/>
|
|
221
|
+
</Teleport>
|
|
215
222
|
</ToastProvider>
|
|
216
223
|
</template>
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { useReducedMotion } from "motion-v"
|
|
2
|
+
import { computed, ref, type ComputedRef, type CSSProperties, type Ref } from "vue"
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* useTilt — pointer-reactive 3D tilt for a "tactile" surface (a card, a tile, a
|
|
6
|
+
* floating island). The element rotates toward the cursor and lifts on hover,
|
|
7
|
+
* giving physical, hand-held depth without any per-component motion wiring.
|
|
8
|
+
*
|
|
9
|
+
* Bind the returned handlers to the element and the returned `style` to it (it
|
|
10
|
+
* needs a shared `perspective` on an ANCESTOR — see LpTilt, which supplies one).
|
|
11
|
+
* Rotation eases to rest via CSS transition; the handlers only write the target
|
|
12
|
+
* transform, so there's no rAF loop. SSR-safe (no window access at setup) and
|
|
13
|
+
* collapses to a flat, no-tilt style when the OS prefers reduced motion.
|
|
14
|
+
*/
|
|
15
|
+
export interface UseTiltOptions {
|
|
16
|
+
/** Max rotation toward the pointer, in degrees (per axis). Default 10. */
|
|
17
|
+
max?: number
|
|
18
|
+
/** Upward lift on hover, in px. Default 8. */
|
|
19
|
+
lift?: number
|
|
20
|
+
/** Scale on hover. Default 1.03. */
|
|
21
|
+
scale?: number
|
|
22
|
+
/** Glare highlight following the pointer. Default false. */
|
|
23
|
+
glare?: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface UseTilt {
|
|
27
|
+
/** Bind to the tilting element's style. */
|
|
28
|
+
style: ComputedRef<CSSProperties>
|
|
29
|
+
/** Radial-gradient background for an optional glare overlay (0 when idle). */
|
|
30
|
+
glareStyle: ComputedRef<CSSProperties>
|
|
31
|
+
onPointerMove: (e: PointerEvent) => void
|
|
32
|
+
onPointerEnter: () => void
|
|
33
|
+
onPointerLeave: () => void
|
|
34
|
+
/** True while the pointer is over the element (drives lift/scale). */
|
|
35
|
+
active: Ref<boolean>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function useTilt(options: UseTiltOptions = {}): UseTilt {
|
|
39
|
+
const max = options.max ?? 10
|
|
40
|
+
const lift = options.lift ?? 8
|
|
41
|
+
const scale = options.scale ?? 1.03
|
|
42
|
+
const reduceMotion = useReducedMotion()
|
|
43
|
+
|
|
44
|
+
const active = ref(false)
|
|
45
|
+
// Pointer position within the element, normalized to [-1, 1] from center.
|
|
46
|
+
const nx = ref(0)
|
|
47
|
+
const ny = ref(0)
|
|
48
|
+
|
|
49
|
+
function onPointerMove(e: PointerEvent): void {
|
|
50
|
+
if (reduceMotion.value) return
|
|
51
|
+
const el = e.currentTarget as HTMLElement | null
|
|
52
|
+
if (!el) return
|
|
53
|
+
const r = el.getBoundingClientRect()
|
|
54
|
+
nx.value = ((e.clientX - r.left) / r.width) * 2 - 1
|
|
55
|
+
ny.value = ((e.clientY - r.top) / r.height) * 2 - 1
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function onPointerEnter(): void {
|
|
59
|
+
active.value = true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function onPointerLeave(): void {
|
|
63
|
+
active.value = false
|
|
64
|
+
nx.value = 0
|
|
65
|
+
ny.value = 0
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const style = computed<CSSProperties>(() => {
|
|
69
|
+
if (reduceMotion.value) {
|
|
70
|
+
return { transition: "transform 0.2s ease" }
|
|
71
|
+
}
|
|
72
|
+
// rotateX tips away from the pointer's vertical position; rotateY toward its
|
|
73
|
+
// horizontal one. Only tilt while active so it rests flat when the pointer
|
|
74
|
+
// leaves (nx/ny are already zeroed on leave).
|
|
75
|
+
const rx = active.value ? -ny.value * max : 0
|
|
76
|
+
const ry = active.value ? nx.value * max : 0
|
|
77
|
+
const z = active.value ? lift : 0
|
|
78
|
+
const s = active.value ? scale : 1
|
|
79
|
+
return {
|
|
80
|
+
transform: `translate3d(0, ${-z}px, 0) rotateX(${rx}deg) rotateY(${ry}deg) scale(${s})`,
|
|
81
|
+
transformStyle: "preserve-3d",
|
|
82
|
+
transition: active.value
|
|
83
|
+
? "transform 0.12s ease-out"
|
|
84
|
+
: "transform 0.5s cubic-bezier(0.22, 1, 0.36, 1)",
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const glareStyle = computed<CSSProperties>(() => {
|
|
89
|
+
if (!options.glare || reduceMotion.value || !active.value) {
|
|
90
|
+
return { opacity: 0 }
|
|
91
|
+
}
|
|
92
|
+
// Bright spot tracks the pointer; map [-1,1] → [0%,100%].
|
|
93
|
+
const px = (nx.value + 1) * 50
|
|
94
|
+
const py = (ny.value + 1) * 50
|
|
95
|
+
return {
|
|
96
|
+
opacity: 1,
|
|
97
|
+
background: `radial-gradient(circle at ${px}% ${py}%, rgba(255,255,255,0.35), transparent 55%)`,
|
|
98
|
+
transition: "opacity 0.2s ease",
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
return { style, glareStyle, onPointerMove, onPointerEnter, onPointerLeave, active }
|
|
103
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -72,6 +72,7 @@ export { default as LpTabs } from "./components/LpTabs.vue"
|
|
|
72
72
|
export type { TabItem } from "./components/LpTabs.vue"
|
|
73
73
|
export { default as LpTextarea } from "./components/LpTextarea.vue"
|
|
74
74
|
export { default as LpThemeSwitcher } from "./components/LpThemeSwitcher.vue"
|
|
75
|
+
export { default as LpTilt } from "./components/LpTilt.vue"
|
|
75
76
|
export { default as LpToaster } from "./components/LpToaster.vue"
|
|
76
77
|
export { default as LpTooltip } from "./components/LpTooltip.vue"
|
|
77
78
|
export { default as LpTopologyCanvas } from "./components/LpTopologyCanvas.vue"
|
|
@@ -137,6 +138,8 @@ export type { UiConfig } from "./theme/config"
|
|
|
137
138
|
|
|
138
139
|
export { useInputFilter } from "./composables/useInputFilter"
|
|
139
140
|
export type { UseInputFilterOptions } from "./composables/useInputFilter"
|
|
141
|
+
export { useTilt } from "./composables/useTilt"
|
|
142
|
+
export type { UseTilt, UseTiltOptions } from "./composables/useTilt"
|
|
140
143
|
export { useToast } from "./composables/useToast"
|
|
141
144
|
export type {
|
|
142
145
|
ToastAction,
|
package/src/nuxt.ts
CHANGED
|
@@ -16,7 +16,16 @@ export default defineNuxtModule({
|
|
|
16
16
|
meta: { name: "@leavepulse/ui", configKey: "leavepulseUi" },
|
|
17
17
|
setup() {
|
|
18
18
|
for (const name of COMPONENT_NAMES) {
|
|
19
|
-
|
|
19
|
+
// Point each component at its own dist chunk rather than the barrel
|
|
20
|
+
// ("@leavepulse/ui"). Importing through the barrel makes the bundler pull
|
|
21
|
+
// the whole kit; a per-component path lets it ship only the components an
|
|
22
|
+
// app actually uses (plus their real deps — e.g. @vue-flow only when a
|
|
23
|
+
// canvas component is used).
|
|
24
|
+
addComponent({
|
|
25
|
+
name,
|
|
26
|
+
export: "default",
|
|
27
|
+
filePath: `@leavepulse/ui/dist/components/${name}.vue.js`,
|
|
28
|
+
})
|
|
20
29
|
}
|
|
21
30
|
},
|
|
22
31
|
})
|
package/dist/index.css
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.lp-cmd-item[data-v-d845063e]{animation:lp-cmd-in-d845063e .14s ease-out both}@keyframes lp-cmd-in-d845063e{0%{opacity:0;transform:translateY(3px)}}@media(prefers-reduced-motion:reduce){.lp-cmd-item[data-v-d845063e]{animation:none}}
|