npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pinooxhq/luma

v0.4.14

Published

Luma — A modern Vue 3 dashboard framework built on PrimeVue. Themeable, customizable, and ready for production.

Readme

@pinooxhq/luma

Vue 3 dashboard framework for Pinoox apps — routing, Pinia, auth, layouts, theming, and a PrimeVue 4 UI layer in one package.

Luma is more than a component kit. It is the front-end foundation of a Pinoox admin app: boot the shell with createApp(), drop in PageLayout, and ship pages with PrimeVue + Luma wrappers. Every layer (tokens, layouts, router, auth, components) can be replaced without forking.


Table of contents

  1. Requirements
  2. Install
  3. Vite plugin
  4. Quick start
  5. createApp options
  6. Direction (RTL / LTR)
  7. Customization
  8. Authentication
  9. Usage examples
  10. Components
  11. Subpath exports
  12. Upgrading
  13. License

Full prop / slot reference: docs/ui-reference.md · docs index: docs/README.md · page kit: docs/page-kit.md · entity forms: docs/guides/entity-form.md · release notes: CHANGELOG.md


Requirements

| Package | Version | Role | |---------|---------|------| | vue | ^3.5 | Runtime | | primevue | ^4.5 | UI components | | @primeuix/themes | ^2 | Aura-based theming (Luma Console preset) | | pinia | ^4 | State | | @vue/devtools-api | ^8 | Required by Pinia 4 | | vue-router | ^5 | Routing | | axios | ^1 | HTTP (used by auth / http) | | lucide-vue-next | ^1 | Icons for <LIcon> | | sass | ^1 | Compile @pinooxhq/luma/styles (devDependency) | | @pinooxhq/auth | ^0.1 | Optional — bundled auth helpers | | @pinooxhq/slug | ^0.1 | Persian → Finglish slugs (slugify, LSlugField) |

Node ≥ 18. Luma targets PrimeVue 4.x (latest 4.5 line) with @primeuix/themes 2.x.


Install

npm install @pinooxhq/luma

Peers (and Sass for styles):

npm install primevue@^4 @primeuix/themes@^2 pinia@^4 @vue/devtools-api@^8 vue-router axios lucide-vue-next
npm install -D sass

Optional auth package (needed for useAuthStore / configureAuth flows):

npm install @pinooxhq/auth

Without @pinooxhq/auth, createApp still boots and public pages work; requiresAuth routes redirect.

Import styles once in your app entry SCSS:

@use '@pinooxhq/luma/styles';

Vite plugin

Use the official plugin so peer packages are deduped, path aliases are configured, performance is optimized, and file:-linked Luma hot-reloads correctly:

// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import luma from '@pinooxhq/luma/vite';

export default defineConfig({
  plugins: [luma(), vue()],
});

Or use createLumaViteConfig for a zero-boilerplate setup:

// vite.config.js
import vue from '@vitejs/plugin-vue';
import { createLumaViteConfig } from '@pinooxhq/luma/vite';

export default createLumaViteConfig({
  plugins: [vue()],
});

What luma() does:

  1. Dedupes & pre-bundles peers (primevue, pinia, vue-router, yup, …) against the consumer node_modules — avoids duplicate inject keys and optimizes cold-start dev speed.
  2. Auto-detects entry & warmup — resolves theme entry from frontend.config.php, index.html, or src/main.js and configures server warmup.
  3. App aliases — automatically registers standard Pinoox theme aliases (@/, @views, @pages, @stores, @components, @composables, @config, @utils, @api, @assets, @layouts).
  4. Vendor chunk splitting — splits production builds into clean vendor chunks (vendor-vue, vendor-luma, vendor-prime, vendor-tiptap, vendor-icons, vendor-date, vendor-http).
  5. Config & Environment — automatically reads luma.config.js and loads .env / .env.local files.
  6. Diagnostics (lumaDoctor) — checks consumer node_modules during vite dev and warns if required dependencies or local checkout paths are missing.
  7. Allows the Luma package root through server.fs.allow and enables watch polling for file: / symlink installs.
  8. Optional local checkout — set LUMA_LOCAL=/path/to/luma-ui or luma({ local: '/path/to/luma-ui' }). Aliases every @pinooxhq/luma entry (JS, Sass, Vazir fonts) to that tree. Leave unset to use npm.

Typography: Luma’s default stack is Vazir ($px-font-sans + @font-face in styles, and DEFAULT_THEME_CONFIG.font). Override with applyThemeConfig({ font: { sans, mono } }) when needed.

Plugin options

luma({
  perf: true,                          // Dev pre-bundling & build chunk splitting (default: true)
  entry: 'src/main.js',                // Custom entry file (or auto-detected)
  warmup: ['src/main.js'],             // Dev server warmup files
  configFile: 'luma.config.js',        // Theme config file (or set appConfig: false)
  doctor: true,                        // Run dependency health-checks in dev (default: true)
  dedupe: ['my-shared-lib'],           // Additional packages to dedupe
  alias: { '@custom': 'src/custom' },  // Custom aliases
  extraOptimizeDeps: ['my-lib'],       // Additional optimizeDeps
  extraChunkGroups: [],                // Custom Rollup/Rolldown vendor chunk groups
  fsAllow: ['/custom/peer-dir'],       // Additional server.fs.allow paths
  watchPolling: false,                 // Polling options for file links
  local: '/path/to/luma',              // Local Luma source override
})

Local ↔ npm switch

# .env.local (app) — use checkout
LUMA_LOCAL={PATH_TO_LOCAL_LUMA}/luma-ui

# unset / comment out — use published @pinooxhq/luma from node_modules

Prefer loading the checkout’s vite.js when LUMA_LOCAL is set so plugin fixes apply before publish:

import { defineConfig, loadEnv } from 'vite';
import { pathToFileURL } from 'node:url';
import path from 'node:path';

export default defineConfig(async ({ mode }) => {
  const env = loadEnv(mode, process.cwd(), '');
  const local = (env.LUMA_LOCAL || '').trim();
  const { default: luma } = local
    ? await import(pathToFileURL(path.join(path.resolve(local), 'vite.js')).href)
    : await import('@pinooxhq/luma/vite');

  return {
    plugins: [luma(local ? { local } : {})],
  };
});

Quick start

Minimal layout:

your-app/
├── package.json
├── vite.config.js
├── theme.config.js          ← visual tokens (colors, fonts, layout)
└── src/
    ├── main.js              ← createApp(...)
    └── config/
        ├── theme.js         ← brand, nav, pageMeta
        └── routes.js

theme.config.js — visual tokens

Written to :root as CSS variables at boot (no Sass rebuild to rebrand):

export default {
  brand: {
    primary:       '#0E73FD',
    primaryHover:  '#0858D4',
    primaryActive: '#0644A8',
    primarySoft:   '#DBE7FE',
    bgLight:       '#F4F4FE',
    bgDark:        '#060912',
  },
  font: {
    sans: 'Vazir, Vazirmatn, Inter, system-ui, sans-serif',
    mono: 'JetBrains Mono, monospace',
  },
  layout: {
    sidebarWidth:          '280px',
    sidebarCollapsedWidth: '76px',
    topbarHeight:          '64px',
    pageMaxWidth:          '1280px',
    radius:                'lg',
  },
  // Optional:
  // direction: 'rtl',
  // auth: { endpoints: { me, login, logout }, skipMe, autoLoginFromUrl },
};

src/config/theme.js — brand, nav, page meta

import logo from '@/assets/images/brand.svg';

export const themeConfig = {
  brand: {
    title: 'Acme Shop',
    subtitle: 'Sales admin',
    logo,
  },
  nav: {
    sections: [
      {
        key: 'overview',
        label: 'Overview', // static heading; set collapsible: true for accordion
        items: [
          { key: 'dashboard', label: 'Dashboard', icon: 'layout-dashboard', route: 'shop.dashboard' },
          { key: 'orders',    label: 'Orders',    icon: 'shopping-cart',    route: 'shop.orders' },
          {
            key: 'catalog',
            label: 'Catalog',
            icon: 'layers',
            children: [
              { key: 'categories', label: 'Categories', icon: 'folder-tree', route: 'shop.products.categories' },
              { key: 'brands',     label: 'Brands',     icon: 'award',       route: 'shop.products.brands' },
            ],
          },
        ],
      },
    ],
  },
  pageMeta: {
    'shop.dashboard': { title: 'Dashboard', lead: 'Sales overview', badge: 'Live' },
    'shop.orders':    { title: 'Orders',    lead: 'Open and completed', badge: 'Sales' },
  },
  user: { roleLabel: 'Store manager' },
};

export default themeConfig;

src/config/routes.js

import { PageLayout } from '@pinooxhq/luma/layouts';

export const routes = [
  {
    path: '/',
    component: PageLayout,
    meta: { requiresAuth: true },
    children: [
      {
        path: '',
        name: 'shop.dashboard',
        component: () => import('@pages/dashboard/page-dashboard.vue'),
      },
      {
        path: 'orders',
        name: 'shop.orders',
        component: () => import('@pages/orders/page-orders.vue'),
      },
    ],
  },
];

src/main.js

import { createApp } from '@pinooxhq/luma';
import { RootShell } from '@pinooxhq/luma/layouts';
import pinia from '@stores/index.js';
import themeConfig from '../theme.config.js';
import { themeConfig as appThemeConfig } from '@/config/theme.js';
import { routes } from '@/config/routes.js';

createApp({
  AppRoot: RootShell,
  themeConfig: {
    ...appThemeConfig,
    brand:  { ...appThemeConfig.brand, ...themeConfig.brand },
    font:   themeConfig.font,
    layout: themeConfig.layout,
    direction: themeConfig.direction,
    auth: themeConfig.auth,
  },
  routes,
  pinia,
});

In pages:

import { usePage } from '@pinooxhq/luma';
const { pageTitle, pageLead, pageBadge } = usePage();

createApp options

| Option | Required | Description | |--------|----------|-------------| | AppRoot | yes | Top-level Vue component — usually RootShell from @pinooxhq/luma/layouts | | themeConfig | no | Brand, nav, pageMeta, tokens, direction, auth | | routes | no | Vue Router route table (default []) | | pinia | no | Pinia instance; installed and set active for guards | | mount | no | Mount selector (default #app) | | auth | no | Options passed to configureAuth() (overrides themeConfig.auth) | | verifyAuth | no | Async hook — full control over session checks (skips built-in me()) | | IconComponent | no | Global <LIcon> registration (e.g. pass Luma’s LIcon) |

AppRoot is required so the Node-safe root barrel never pulls .vue SFCs into smoke tests / tooling.


Direction (RTL / LTR)

createApp resolves direction and syncs <html dir> + PrimeVue rtl:

  1. themeConfig.direction ('rtl' | 'ltr')
  2. Existing <html dir> / <body dir>
  3. window.__PINOOX__.direction (boot payload / VITE_DIRECTION)
  4. Fallback: 'ltr'

Teleported overlays (Select, DatePicker, menus, confirm) inherit direction via Luma SCSS — no per-app overlay CSS required for the common cases.

// theme.config.js
export default {
  direction: 'rtl',
  // …
};

Customization

Visual tokens

| Group | Keys | |-------|------| | brand | primary, primaryHover, primaryActive, primarySoft, bgLight, bgDark | | font | sans, mono | | layout | sidebarWidth, sidebarCollapsedWidth, topbarHeight, pageMaxWidth, radius |

createApp calls applyThemeConfig() for you. You can also call it at runtime (white-label / multi-tenant):

import { applyThemeConfig, useTheme } from '@pinooxhq/luma';

applyThemeConfig({ brand: { primary: '#10b981' } });
const { toggleTheme, isDark } = useTheme();

Sass overrides

@use '@pinooxhq/luma/styles';

:root {
  --px-primary-soft: rgba(14, 115, 253, 0.08);
}

[data-theme='dark'] {
  --px-bg: #0a0a14;
}

themeConfig shape

{
  brand:    { title, subtitle?, logo?, primary?, /* visual brand keys */ },
  nav:      { sections: Array<{ key, label, items: Array<{ key, label, icon, route?, children? }>, collapsible?, defaultCollapsed? }> },
  pageMeta: Record<string, { title, lead?, badge? }>,
  user?:    { roleLabel? },
  font?:    { sans?, mono? },
  layout?:  { sidebarWidth?, sidebarCollapsedWidth?, topbarHeight?, pageMaxWidth?, radius? },
  direction?: 'rtl' | 'ltr',
  auth?:    { endpoints?, skipMe?, autoLoginFromUrl? },
  loading?: { enabled?, delay?, exclude?, label? },
}

Omitted fields fall back to Luma defaults. Nav section label is a static heading; set collapsible: true for accordion sections. Nav items may include children for a nested submenu.

Layouts

Typical pattern: RootShell as AppRoot, PageLayout as the authenticated route layout (sidebar, topbar, mobile nav, drawer).

import { RootShell, PageLayout } from '@pinooxhq/luma/layouts';
import { LSidebar, LTopbar, LMobileNav } from '@pinooxhq/luma/ds';
import { LView, LHeader, LIcon } from '@pinooxhq/luma/ui';

Compose DS / UI pieces into a custom layout when PageLayout is too opinionated — RootShell only provides the outer wrapper, global Toast, confirm dialog, HTTP loading overlay, and theme CSS variables.

Global HTTP loading

RootShell mounts <LLoading />. Any request through Luma http is counted; the overlay appears after delay ms so fast calls do not flash.

// theme.config.js
export default {
  loading: {
    enabled: true,
    delay: 220,
    exclude: ['/pricing/fx/quotes'],
    label: 'Loading',
  },
};
http.get('/pricing/fx/quotes', { skipLoading: true });

configureHttpLoading() updates the same options at runtime. { skipLoading: true } always wins for that request.

While a local loading surface is active (table skeleton, custom spinner), the overlay stays hidden even if HTTP is in flight. LDataTable :loading and useTableRows() register automatically. Anywhere else:

import { useLocalLoading } from '@pinooxhq/luma';

useLocalLoading(loading); // ref or computed

Table skeleton

Keep the table visible while the first fetch runs. Pass :loading to LDataTable and wrap cells with LColumnBody:

<LDataTable :value="rows" :loading="loading" paginator data-key="id">
  <Column field="name" header="Name">
    <template #body="{ data }">
      <LColumnBody :data="data" part="entity">
        {{ data.name }}
      </LColumnBody>
    </template>
  </Column>
  <Column header="">
    <template #body="{ data }">
      <LColumnBody :data="data" part="actions" :skel-props="{ count: 2 }">
        …
      </LColumnBody>
    </template>
  </Column>
</LDataTable>

For a custom PrimeVue DataTable, use useTableRows(items, loading) and LTableSkel / isSkelRow().


Authentication

Defaults match Pinoox account APIs. Override only what you need.

Default guard flow

On first navigation, authGuard:

  1. Optionally adopts ?__manager_token=… when themeConfig.auth.autoLoginFromUrl: true
  2. Calls auth.me() once (remote strategy → /account/api/v1/auth/{login,logout,get} by default)
  3. Redirects to /account/login when requiresAuth cannot proceed

Custom endpoints

// theme.config.js
auth: {
  endpoints: {
    me:     '/api/v1/auth/me',
    login:  '/api/v1/auth/login',
    logout: '/api/v1/auth/logout',
  },
},

Replace the strategy

import { createApp, configureAuth } from '@pinooxhq/luma';
import { RootShell } from '@pinooxhq/luma/layouts';

configureAuth({
  endpoints: { me: '/api/auth/me', login: '/api/auth/login' },
});

createApp({ AppRoot: RootShell, themeConfig, routes, pinia });

Skip me()

auth: { skipMe: true, autoLoginFromUrl: true },

Trusts token presence and skips the HTTP round-trip.

Full control — verifyAuth

createApp({
  AppRoot: RootShell,
  themeConfig,
  routes,
  pinia,
  verifyAuth: async ({ store, route, adoptedFromUrl }) => {
    if (!store.token) return false;
    const profile = await fetchMyProfile();
    store.user = profile;
    return profile.active === true;
  },
});

| Return | Effect | |--------|--------| | true | Allow navigation | | false / throw | Redirect to login |

Helpers

import { auth, useAuthStore, http, configureAuth, getActiveAuth } from '@pinooxhq/luma';

http is an axios client with auth headers wired — prefer it over a fresh axios instance.

Theme-context panels mount under __PINOOX__.url.BASE (/panel) and set auth.loginUrl to the browser path (/panel/auth/login). Vue Router still uses unprefixed records (/auth/login). Do not rewrite url.BASE in the app. Use:

import {
  resolveHistoryBase,
  bindAuthRedirect,
  resolveGuestExit,
} from '@pinooxhq/luma';

const router = createRouter({
  history: createWebHistory(resolveHistoryBase()),
  routes,
});
bindAuthRedirect(router);

Usage examples

DataTable page

<script setup>
import { ref } from 'vue';
import DataTable from 'primevue/datatable';
import Column from 'primevue/column';
import Tag from 'primevue/tag';
import Button from 'primevue/button';
import InputText from 'primevue/inputtext';
import IconField from 'primevue/iconfield';
import InputIcon from 'primevue/inputicon';
import { usePage } from '@pinooxhq/luma';
import { LView, LHeader, LCard } from '@pinooxhq/luma/ui';

const { pageTitle, pageLead } = usePage();
const orders = ref([
  { id: 1001, customer: 'Alex Rivera', total: 1250, status: 'paid' },
  { id: 1002, customer: 'Sam Chen',    total:  890, status: 'pending' },
]);
const filters = ref({ global: '' });
const statusSeverity = { paid: 'success', pending: 'warn', shipped: 'info' };
</script>

<template>
  <LView>
    <LHeader :title="pageTitle" :lead="pageLead">
      <Button label="New order" severity="primary" />
    </LHeader>
    <LCard>
      <IconField>
        <InputIcon class="pi pi-search" />
        <InputText v-model="filters.global" placeholder="Search…" />
      </IconField>
      <DataTable
        :value="orders"
        v-model:filters="filters"
        :global-filter-fields="['customer', 'status']"
        paginator
        :rows="10"
        striped-rows
      >
        <Column field="id" header="ID" sortable />
        <Column field="customer" header="Customer" sortable />
        <Column field="status" header="Status">
          <template #body="{ data }">
            <Tag :value="data.status" :severity="statusSeverity[data.status]" />
          </template>
        </Column>
      </DataTable>
    </LCard>
  </LView>
</template>

Docs: primevue.org/datatable

Form + LField

For entity CRUD with Yup defaults / toForm / toPayload, see Entity Form (index: docs/README.md).

<script setup>
import { reactive, ref } from 'vue';
import InputText from 'primevue/inputtext';
import Select from 'primevue/select';
import Button from 'primevue/button';
import { http } from '@pinooxhq/luma';
import { LView, LHeader, LCard, LField } from '@pinooxhq/luma/ui';

const form = reactive({ name: '', category: null });
const errors = ref({});
const submitting = ref(false);
const categories = [
  { label: 'Apparel', value: 'apparel' },
  { label: 'Shoes', value: 'shoes' },
];

async function submit() {
  submitting.value = true;
  errors.value = {};
  try {
    await http.post('/api/v1/products', form);
  } catch (err) {
    errors.value = err.response?.data?.errors ?? {};
  } finally {
    submitting.value = false;
  }
}
</script>

<template>
  <LView>
    <LHeader title="New product" lead="Add an item to the catalog" />
    <LCard>
      <form class="product-form" @submit.prevent="submit">
        <LField label="Name" :error="errors.name?.[0]">
          <InputText v-model="form.name" :invalid="!!errors.name" />
        </LField>
        <LField label="Category">
          <Select
            v-model="form.category"
            :options="categories"
            option-label="label"
            option-value="value"
            placeholder="Select…"
          />
        </LField>
        <Button type="submit" label="Save" :loading="submitting" />
      </form>
    </LCard>
  </LView>
</template>

Toast / Dialog

Global Toast is mounted by RootShell. Use useToast() anywhere:

<script setup>
import Button from 'primevue/button';
import Dialog from 'primevue/dialog';
import { useToast } from 'primevue/usetoast';
import { ref } from 'vue';

const toast = useToast();
const open = ref(false);

function notify() {
  toast.add({
    severity: 'success',
    summary: 'Saved',
    detail: 'Changes were stored.',
    life: 3000,
  });
}
</script>

<template>
  <Button label="Notify" @click="notify" />
  <Button label="Delete" severity="danger" @click="open = true" />
  <Dialog v-model:visible="open" header="Confirm" modal>
    <p>Delete this item?</p>
    <template #footer>
      <Button label="Cancel" text @click="open = false" />
      <Button label="Delete" severity="danger" @click="open = false" />
    </template>
  </Dialog>
</template>

Dark mode

<script setup>
import { useTheme } from '@pinooxhq/luma';
import { LThemeToggle } from '@pinooxhq/luma/ds';

const { isDark, toggleTheme } = useTheme();
</script>

<template>
  <LThemeToggle :model-value="isDark" @update:model-value="toggleTheme" />
</template>

Components

Rule of thumb: use PrimeVue directly for forms, tables, overlays. Reach for an L* wrapper when you want Luma’s themed chrome on top.

Luma UI (@pinooxhq/luma/ui)

| Component | Purpose | |-----------|---------| | LView / LPage | Page shells with consistent padding | | LHeader | Classic page header (title, lead, badge, actions slot) | | LPageHeader | Modern header (eyebrow, title, lead, icon) | | LPageToolbar | Sticky page actions | | LPageContainer | Honors pageMaxWidth + gutters | | LPanel | Content panel (incl. flush table chrome) | | LCard | Themed card (PrimeVue Card) | | LStatCard | KPI / metric card | | LEmptyPanel | Empty state + CTA | | LBadge | Status tag | | LButton | Variant / severity / size / shape system | | LField | Label, hint, error + input slot | | LDatePicker | Themed date picker | | LTabs | Tab strip helpers | | LToolbar | Filter / action row | | LSpinner | Loading spinner | | LToast | In-page toast host (global Toast still in RootShell) | | LConfirmDialog | Confirm surface with RTL-aware layout | | LRichEditor | TipTap rich text | | LIcon | Lucide wrapper (<LIcon name="shopping-cart" />) |

Design-system chrome (@pinooxhq/luma/ds)

| Component | Purpose | |-----------|---------| | LSidebar | Nav sidebar (PageLayout) | | LTopbar | Top bar | | LMobileNav | Mobile bottom nav | | LThemeToggle | Light / dark control | | LEmptyState | Deprecated alias of LEmptyPanel |

PrimeVue 4 (wired via Console preset)

Import any core component in pages — preset, ripple, RTL, and dark mode are already installed by setupPrimeVue:

| Area | Components | |------|------------| | Form | InputText, Textarea, Password, InputMask, InputNumber, InputOtp, Select, MultiSelect, AutoComplete, Checkbox, RadioButton, ToggleSwitch, SelectButton, Slider, Rating, DatePicker, … | | Data | DataTable, DataView, Tree, TreeTable, Timeline, Paginator | | Panels | Card, Panel, Accordion, Tabs, Stepper, Toolbar | | Overlay | Dialog, Drawer, Popover, Tooltip, ConfirmDialog, ConfirmPopup | | Feedback | Toast, Message, Tag, Badge, Chip, ProgressBar, ProgressSpinner, Skeleton | | Menu | Menu, Menubar, MegaMenu, PanelMenu, TieredMenu, Breadcrumb | | Media | Image, Carousel, FileUpload | | Misc | Avatar, Divider, Splitter, ScrollPanel, Knob, Button, ToggleButton |

See primevue.org for the full API. Theme CSS comes from @primeuix/themes via Luma’s preset — you do not need a separate PrimeVue CSS import for the Console look.


Subpath exports

| Path | Contents | |------|----------| | @pinooxhq/luma | Node-safe barrel (createApp, composables, auth, theme helpers — no .vue SFCs) | | @pinooxhq/luma/ui | UI primitives listed above | | @pinooxhq/luma/ds | Shell chrome + legacy LEmptyState | | @pinooxhq/luma/layouts | RootShell, PageLayout | | @pinooxhq/luma/composables | usePage, … | | @pinooxhq/luma/router | createAppRouter, authGuard, bindAuthRedirect, redirectToLogin, route prefetching, BASE/login helpers | | @pinooxhq/luma/core | auth, http, useAuthStore, configureAuth, env, dates, media helpers | | @pinooxhq/luma/plugins | setupPrimeVue, ConsolePreset | | @pinooxhq/luma/styles | Main SCSS bundle | | @pinooxhq/luma/tokens | SCSS design tokens | | @pinooxhq/luma/fonts | Vazir registration | | @pinooxhq/luma/theme-config | Nav / pageMeta / theme resolution helpers | | @pinooxhq/luma/createApp | Factory only | | @pinooxhq/luma/vite | Vite plugin, presets, and context loader | | @pinooxhq/luma/vite/perf | Vite pre-bundling and vendor chunk splitting presets | | @pinooxhq/luma/vite/config | createLumaViteConfig, createAppAliases, lumaDoctor, config loaders | | @pinooxhq/luma/vite/theme-auto | Automatic theme entry detection and icon aliases | | @pinooxhq/luma/preset | Console preset (default export) |


Upgrading

npm update @pinooxhq/luma

Semantic versioning (see CHANGELOG.md):

  • Patch — internal fixes, no public API break
  • Minor — new components / helpers, backwards compatible
  • Major — breaking changes, documented in the changelog

Pin to ^0.4.0 (or stricter) for predictable upgrades.

Migrating from 0.3.x → 0.4.x

| Package | 0.3.x | 0.4.x | |---------|-------|-------| | primevue | ^4 | ^4.5 | | @primeuix/themes | ^1 | ^2 | | pinia | ^3 | ^4 | | @vue/devtools-api | — | ^8 |

  1. Install the peer set from Install.
  2. Prefer LEmptyPanel over LEmptyState.
  3. Prefer the luma() Vite plugin when developing against a file: link.
  4. Set themeConfig.direction when you need explicit RTL.

License

MIT © Pinoox