@enfyra/mcp-server 0.0.111 → 0.0.113
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/package.json
CHANGED
package/src/lib/mcp-examples.js
CHANGED
|
@@ -1531,7 +1531,7 @@ ensure_page_extension({
|
|
|
1531
1531
|
name: "ReportsPage",
|
|
1532
1532
|
description: "Reports dashboard",
|
|
1533
1533
|
menuId: "<created-menu-id>",
|
|
1534
|
-
code: "<template><section class=\\"min-h-full w-full space-y-4\\"><div class=\\"grid gap-4 md:grid-cols-3\\"><
|
|
1534
|
+
code: "<template><section class=\\"min-h-full w-full space-y-4\\"><div class=\\"grid gap-4 md:grid-cols-2 xl:grid-cols-3\\"><article class=\\"eapp-identity-surface rounded-[var(--radius-panel)] border p-4\\"><div class=\\"flex items-start justify-between gap-3\\"><div><p class=\\"text-sm font-medium text-[var(--text-tertiary)]\\">Total</p><p class=\\"mt-2 text-2xl font-semibold text-[var(--text-primary)]\\">0</p></div><span class=\\"eapp-identity-soft rounded-[var(--radius-pill)] px-2 py-0.5 text-xs font-semibold\\">Current</span></div></article></div></section></template><script setup>const { registerPageHeader } = usePageHeaderRegistry(); const { register: registerHeaderActions } = useHeaderActionRegistry(); registerPageHeader({ title: 'Reports', description: 'Operational report overview.', leadingIcon: 'lucide:bar-chart-3', gradient: 'none', variant: 'minimal' }); registerHeaderActions([{ id: 'refresh-reports', label: 'Refresh', icon: 'lucide:refresh-cw', color: 'neutral', variant: 'outline', onClick: () => {}, order: 80 }])</script>",
|
|
1535
1535
|
isEnabled: true
|
|
1536
1536
|
})`,
|
|
1537
1537
|
notes: [
|
|
@@ -1546,7 +1546,12 @@ ensure_page_extension({
|
|
|
1546
1546
|
'Put page-level actions in useHeaderActionRegistry or useSubHeaderActionRegistry, destructure register first, then call it with one action or an array.',
|
|
1547
1547
|
'Page extensions should be full-bleed by default and responsive from the first version.',
|
|
1548
1548
|
'The extension root is already inside Enfyra admin page main; do not add root-level page padding.',
|
|
1549
|
-
'Use existing eApp theme variables for panels, rows, badges, borders, and text. Pair border/divide utilities with border-[var(--border-default)] or divide-[var(--border-default)] so light and dark themes stay consistent.',
|
|
1549
|
+
'Use existing eApp theme variables for panels, rows, badges, borders, controls, radius, and text. Pair border/divide utilities with border-[var(--border-default)] or divide-[var(--border-default)] so light and dark themes stay consistent.',
|
|
1550
|
+
'Treat primary color as runtime-configurable by the app color picker. For Nuxt UI components, choose color="primary" by semantic intent; for custom extension blocks, choose eapp-identity-surface for larger tinted blocks, eapp-identity-soft for compact chips, eapp-identity-solid for fills, or eapp-identity-text for icons by intent instead of choosing Tailwind color utilities.',
|
|
1551
|
+
'Use PageHeader gradient: "none" for generated operational pages unless the user explicitly asks for a decorative page accent; do not hardcode cyan/violet/purple/blue/green gradients.',
|
|
1552
|
+
'For general card grids inside the shell, use md:grid-cols-2 xl:grid-cols-3 instead of lg:grid-cols-3 because the desktop sidebar leaves tablet-width content at 1024px.',
|
|
1553
|
+
'Do not use Nuxt UI neutral semantic classes such as bg-default, text-muted, text-dimmed, border-default, or divide-default inside extension code; use eApp tokens/classes.',
|
|
1554
|
+
'Do not pass ui.content: "surface-card" to UModal/CommonModal; modal content uses the app modal surface and caller content classes should only append z-index or width.',
|
|
1550
1555
|
'Do not inject global CSS, create theme guards, redefine the app palette, or solve one extension by overriding the whole app shell.',
|
|
1551
1556
|
'Keep list selection local and fetch detail rows only; do not refetch the whole list after a row click unless the list data changed.',
|
|
1552
1557
|
'Page extension paths are admin app UI routes. Do not verify them with test_rest_endpoint against ENFYRA_API_URL unless inspect_route shows an API route with the same path.',
|
|
@@ -1558,16 +1563,19 @@ ensure_page_extension({
|
|
|
1558
1563
|
code: `// Create reusable/bulky sections as widget extension records first.
|
|
1559
1564
|
const reportStatusWidgetCode = \`
|
|
1560
1565
|
<template>
|
|
1561
|
-
<section class="
|
|
1566
|
+
<section class="surface-card p-4">
|
|
1562
1567
|
<div class="flex items-start justify-between gap-3">
|
|
1563
1568
|
<div>
|
|
1564
|
-
<p class="text-sm text-
|
|
1565
|
-
<p class="mt-2 text-2xl font-semibold">{{ total }}</p>
|
|
1566
|
-
<p class="mt-1 text-xs text-
|
|
1569
|
+
<p class="text-sm font-medium text-[var(--text-tertiary)]">Total reports</p>
|
|
1570
|
+
<p class="mt-2 text-2xl font-semibold text-[var(--text-primary)]">{{ total }}</p>
|
|
1571
|
+
<p class="mt-1 text-xs text-[var(--text-tertiary)]">{{ latestLabel }}</p>
|
|
1567
1572
|
</div>
|
|
1568
1573
|
<UButton type="button" color="neutral" variant="outline" @click.stop.prevent="emit('refresh')">Refresh</UButton>
|
|
1569
1574
|
</div>
|
|
1570
|
-
<
|
|
1575
|
+
<div class="mt-3 h-1.5 overflow-hidden rounded-[var(--radius-pill)] bg-[var(--surface-muted)]">
|
|
1576
|
+
<div class="eapp-identity-solid h-full" :style="{ width: progressWidth }"></div>
|
|
1577
|
+
</div>
|
|
1578
|
+
<UButton v-if="hasLatest" type="button" class="mt-3" color="primary" variant="solid" @click.stop.prevent="openLatest">Open latest</UButton>
|
|
1571
1579
|
</section>
|
|
1572
1580
|
</template>
|
|
1573
1581
|
|
|
@@ -1580,6 +1588,7 @@ const props = defineProps({
|
|
|
1580
1588
|
const emit = defineEmits(['refresh'])
|
|
1581
1589
|
const hasLatest = computed(() => props.rows.length > 0)
|
|
1582
1590
|
const latestLabel = computed(() => hasLatest.value ? 'Latest: ' + (props.rows[0]?.title || props.rows[0]?.id || 'Untitled') : 'No reports yet')
|
|
1591
|
+
const progressWidth = computed(() => hasLatest.value ? '100%' : '0%')
|
|
1583
1592
|
function openLatest() {
|
|
1584
1593
|
if (typeof props.openDetails === 'function' && props.rows[0]) props.openDetails(props.rows[0])
|
|
1585
1594
|
}
|
|
@@ -1597,7 +1606,7 @@ ensure_widget_extension({
|
|
|
1597
1606
|
ensure_page_extension({
|
|
1598
1607
|
name: "ReportsPage",
|
|
1599
1608
|
menuId: "<reports-menu-id>",
|
|
1600
|
-
code: "<template><section class=\\"min-h-full w-full space-y-4\\"><Widget :id=\\"<report-status-widget-id>\\" :total=\\"totalReports\\" :rows=\\"reportRows\\" :open-details=\\"openReportDetails\\" @refresh=\\"refresh\\" /><Widget :id=\\"<report-table-widget-id>\\" :rows=\\"reportRows\\" @refresh=\\"refresh\\" /></section></template><script setup>const { registerPageHeader } = usePageHeaderRegistry(); registerPageHeader({ title: 'Reports', description: 'Operational report overview.', leadingIcon: 'lucide:bar-chart-3', gradient: '
|
|
1609
|
+
code: "<template><section class=\\"min-h-full w-full space-y-4\\"><Widget :id=\\"<report-status-widget-id>\\" :total=\\"totalReports\\" :rows=\\"reportRows\\" :open-details=\\"openReportDetails\\" @refresh=\\"refresh\\" /><Widget :id=\\"<report-table-widget-id>\\" :rows=\\"reportRows\\" @refresh=\\"refresh\\" /></section></template><script setup>const { registerPageHeader } = usePageHeaderRegistry(); registerPageHeader({ title: 'Reports', description: 'Operational report overview.', leadingIcon: 'lucide:bar-chart-3', gradient: 'none', variant: 'minimal' }); const totalReports = ref(0); const reportRows = ref([]); function refresh() {} function openReportDetails(row) { navigateTo('/data/report?filter=' + encodeURIComponent(JSON.stringify({ id: { _eq: row.id } }))) }</script>",
|
|
1601
1610
|
isEnabled: true
|
|
1602
1611
|
})`,
|
|
1603
1612
|
notes: [
|
|
@@ -1738,7 +1747,7 @@ registerPageHeader({
|
|
|
1738
1747
|
title: 'Report detail',
|
|
1739
1748
|
description: 'Review status, schedule, and delivery history.',
|
|
1740
1749
|
leadingIcon: 'lucide:file-text',
|
|
1741
|
-
gradient: '
|
|
1750
|
+
gradient: 'none',
|
|
1742
1751
|
variant: 'minimal'
|
|
1743
1752
|
})
|
|
1744
1753
|
|
|
@@ -1766,8 +1775,8 @@ registerHeaderActions([
|
|
|
1766
1775
|
id: 'refresh-report',
|
|
1767
1776
|
label: 'Refresh',
|
|
1768
1777
|
icon: 'lucide:refresh-cw',
|
|
1769
|
-
color: '
|
|
1770
|
-
variant: '
|
|
1778
|
+
color: 'neutral',
|
|
1779
|
+
variant: 'outline',
|
|
1771
1780
|
order: 2,
|
|
1772
1781
|
onClick: refresh
|
|
1773
1782
|
}
|
|
@@ -1775,9 +1784,10 @@ registerHeaderActions([
|
|
|
1775
1784
|
</script>`,
|
|
1776
1785
|
notes: [
|
|
1777
1786
|
'Use PageHeader for the title strip; do not render a duplicate header inside extension body.',
|
|
1787
|
+
'Use gradient: "none" for generated operational pages; hardcoded named gradients are decorative and should be explicit user intent.',
|
|
1778
1788
|
'Back/navigation actions should be neutral ghost so they read as navigation, not a primary operation.',
|
|
1779
1789
|
'Visible secondary operations should be neutral outline; soft is only for low-emphasis chrome actions.',
|
|
1780
|
-
'The main page action should be primary solid.',
|
|
1790
|
+
'The main page mutation action should be primary solid; refresh is neutral outline unless refresh is the actual primary workflow.',
|
|
1781
1791
|
'Do not choose soft only because it looks acceptable in dark mode; light mode must remain clear too.',
|
|
1782
1792
|
],
|
|
1783
1793
|
},
|
|
@@ -297,18 +297,41 @@ function getExtensionThemeContract() {
|
|
|
297
297
|
'The extension is already mounted inside the Enfyra app shell. Do not add a duplicate page header, centered page wrapper, or root-level page padding.',
|
|
298
298
|
'Page extensions should be full-bleed, responsive, and split large operations into focused pages or UTabs.',
|
|
299
299
|
'Use usePageHeaderRegistry for the shell title and useHeaderActionRegistry/useSubHeaderActionRegistry for page actions.',
|
|
300
|
+
'For detail/form workflows that should stay left-aligned with empty space on the right, wrap the body in eapp-page-constrained; use eapp-page-constrained-wide only when the workflow genuinely needs more width.',
|
|
301
|
+
'Card/list grids inside the default shell must account for the 280px desktop sidebar. Do not switch general card grids to three columns at lg; use md:grid-cols-2 xl:grid-cols-3 unless a local container proves three columns have enough width.',
|
|
300
302
|
],
|
|
301
303
|
theme: [
|
|
302
|
-
'Use existing eApp theme variables/classes, not hardcoded light or dark colors.',
|
|
304
|
+
'Use existing eApp theme variables/classes, not hardcoded light or dark colors. The app theme source of truth is CSS variables, including --brand-*, --surface-*, --card-*, --control-*, --action-*, --badge-*, --radius-*, --border-*, and --text-*.',
|
|
305
|
+
'Primary color is runtime-configurable through the app color picker. For Nuxt UI components, choose color="primary" by semantic intent and let the app map it through the primary contract; do not choose a concrete palette. For custom extension blocks, choose semantic intent classes, not colors: use eapp-identity-surface for larger entity/feature blocks, tiles, or cards that need a tinted/dark surface matching the shell; add eapp-identity-surface-hover when that block is clickable. Use eapp-identity-soft for compact selected entity chips, pills, small icon tiles, and identity callouts; add eapp-identity-soft-hover when compact surfaces are clickable; use eapp-identity-solid only for primary identity fills; use eapp-identity-text for identity icons or inline text. The eapp-accent-* aliases are still available for the same runtime-primary intent, but eapp-identity-* is preferred when the UI element represents an entity or feature identity.',
|
|
306
|
+
'Nuxt UI secondary is still a valid semantic color when the product intentionally wants a secondary action or state. Do not use color="secondary", from-secondary-*, bg-secondary-*, text-secondary-*, or cyan/purple/green palette utilities merely to approximate an entity accent; use eapp-identity-* and let the app decide the color.',
|
|
307
|
+
'Do not make custom extension blocks with Tailwind palette utilities or raw primary utilities such as text-primary, bg-primary/10, border-primary, ring-primary/20, from-cyan-*, text-violet-*, bg-green-*, or dark:bg-zinc-950. The app owns how eapp-identity-* and eapp-accent-* map to the active color picker value.',
|
|
308
|
+
'Use UButton color="primary" only for the single main action for the current scope. Refresh, back, navigation, filters, and secondary actions should be neutral variants unless they are the main mutation.',
|
|
309
|
+
'PageHeader gradient must be "none" for generated operational extensions unless the user explicitly asks for a decorative page accent. Do not hardcode cyan, violet, purple, blue, or green PageHeader gradients to force color variety.',
|
|
303
310
|
'Do not inject global CSS, create theme guards, redefine the app palette, or solve one extension by overriding the whole app shell.',
|
|
304
|
-
'For
|
|
305
|
-
'Never use
|
|
306
|
-
'
|
|
311
|
+
'For panels/cards, prefer surface-card, surface-card-hover, surface-muted, or explicit token classes such as bg-[var(--card-bg)] border-[var(--card-border)] shadow-[var(--card-shadow)]. Use text-[var(--text-primary|secondary|tertiary)] for copy.',
|
|
312
|
+
'Never use Nuxt UI neutral semantic classes such as bg-default, bg-muted, border-default, divide-default, text-muted, text-dimmed, or hardcoded dark palettes such as dark:bg-zinc-950, bg-slate-*, text-gray-*, border-black, or black.',
|
|
313
|
+
'Never use bare border/divide-y for panels or rows: pair them with border-[var(--border-default)] or divide-[var(--border-default)].',
|
|
314
|
+
'Use radius tokens or mapped rounded utilities consistently: --radius-card for cards, --radius-panel for nested panels, --radius-control for buttons/inputs, --radius-subcontrol for compact inner controls, and --radius-pill for pills.',
|
|
315
|
+
'Status colors must remain readable in both themes. Use UBadge or badge tokens for non-primary statuses such as --badge-success-soft-*, --badge-warning-soft-*, --badge-danger-soft-*, --badge-info-soft-*, and --badge-neutral-soft-* instead of neon translucent colors.',
|
|
316
|
+
'Keep dark and light contrast comparable. Do not make dark mode more neon or lower-contrast than light mode; prefer muted soft backgrounds with clear text and visible borders.',
|
|
317
|
+
],
|
|
318
|
+
components: [
|
|
319
|
+
'Use Nuxt UI/eApp components for normal controls: UButton, UInput, UTextarea, USelectMenu/USelect, USwitch, UCheckbox, UTabs, UBadge, UModal, and CommonDrawer when available.',
|
|
320
|
+
'Buttons should have stable geometry: hover may change color, border, or shadow but must not move the button or resize its content. Disabled buttons keep disabled cursor/visual state.',
|
|
321
|
+
'Inputs and textareas should not add hover movement or decorative hover states; focus, invalid, disabled, and loading states must be explicit.',
|
|
322
|
+
'Dynamic extensions resolve UModal to the app CommonModal. Do not pass ui.content: "surface-card" to UModal/CommonModal; modal content uses the app modal surface and caller ui.content should only append z-index, width, or max-width classes.',
|
|
323
|
+
'Use CommonDrawer for side-panel editing. Open drawers immediately on user action and render loading/error/content inside the drawer instead of waiting for fetch before opening.',
|
|
324
|
+
'Use UBadge or token-backed badge spans for status. Keep badges legible in both themes with tokenized background, text, and border.',
|
|
325
|
+
],
|
|
326
|
+
loadingAndLists: [
|
|
327
|
+
'For first load of card/list pages, render calm skeleton cards with a slow pulse. For subsequent pagination/filter refreshes, keep the card shells mounted and skeletonize card content until the new list is ready.',
|
|
328
|
+
'Keep pagination inside the same transition/loading branch as the list. Do not show pagination before the list content has left loading.',
|
|
329
|
+
'Use bounded pagination for operational lists. Do not replace pagination with arbitrary fixed caps such as 30 or 50.',
|
|
330
|
+
'Empty states should use an app-matched card surface with compact icon tile, title, and description; do not use huge blank white panels or naked UEmpty chrome on page surfaces.',
|
|
307
331
|
],
|
|
308
332
|
interaction: [
|
|
309
333
|
'Every mutating button needs pending/disabled state, success/error feedback, and must close or update its modal when the operation completes.',
|
|
310
334
|
'Do not refetch broad lists after selecting one row. Keep local selection state and fetch only the detail or mutation result needed.',
|
|
311
|
-
'Use bounded pagination for operational lists. Do not replace pagination with arbitrary fixed caps such as 30 or 50.',
|
|
312
335
|
'Customer-facing toasts must describe the operation. Do not surface raw job ids, flow ids, or worker ids.',
|
|
313
336
|
],
|
|
314
337
|
security: [
|
|
@@ -316,7 +339,7 @@ function getExtensionThemeContract() {
|
|
|
316
339
|
'UI checks are only guidance; handlers/hooks must independently enforce owner/root-admin authorization.',
|
|
317
340
|
'Use the most specific business route or MCP tool. Do not write directly to raw tables when a domain route exists.',
|
|
318
341
|
],
|
|
319
|
-
compactExample: '<template><section class="min-h-full w-full space-y-4"><div class="
|
|
342
|
+
compactExample: '<template><section class="min-h-full w-full space-y-4"><div class="surface-card"><div class="border-b border-[var(--border-default)] px-4 py-3"><h2 class="text-base font-semibold text-[var(--text-primary)]">Title</h2><p class="text-sm text-[var(--text-tertiary)]">Short operational context.</p></div><div class="p-4"><article class="eapp-identity-surface rounded-[var(--radius-panel)] border p-4"><div class="flex items-center gap-3"><span class="eapp-identity-soft rounded-[var(--radius-control)] p-2"><span class="eapp-identity-text">◆</span></span><span class="text-sm font-medium text-[var(--text-primary)]">Identity block</span></div></article></div><div class="divide-y divide-[var(--border-default)]"><button class="flex w-full cursor-pointer items-center justify-between px-4 py-3 text-left transition-colors hover:bg-[var(--surface-muted)] disabled:cursor-not-allowed disabled:opacity-60"><span class="text-sm font-medium text-[var(--text-primary)]">Row</span><span class="eapp-identity-soft rounded-[var(--radius-pill)] px-2 py-0.5 text-xs font-semibold">Open</span></button></div><div class="h-1.5 overflow-hidden rounded-[var(--radius-pill)] bg-[var(--surface-muted)]"><div class="eapp-identity-solid h-full w-1/2"></div></div></div></section></template>',
|
|
320
343
|
};
|
|
321
344
|
}
|
|
322
345
|
|