From bf1401fe34106277cf1caecedaced460911b4e12 Mon Sep 17 00:00:00 2001 From: JohnDoe6345789 Date: Tue, 30 Dec 2025 10:21:33 +0000 Subject: [PATCH] Enhance module definitions and add type annotations across various packages - Added type annotations and class definitions in the dashboard layout, stats, and data table modules for improved type safety and documentation. - Introduced new classes for UI components, props, and configuration in the form builder, navigation menu, notification center, and UI dialogs packages. - Implemented detailed type definitions for actions, fields, and pagination components to streamline usage and enhance clarity. - Updated initialization functions in multiple packages to include versioning and installation context. - Improved structure and readability of the codebase by organizing and documenting component properties and methods. --- DEPENDENCY_CLEANUP.md | 348 +++++++++++++++ FAKEMUI_STRATEGY.md | 397 ++++++++++++++++++ MUI_ELIMINATION_PLAN.md | 29 +- fakemui/icons/README.md | 293 +++++++++++++ packages/admin_dialog/seed/scripts/init.lua | 10 + .../admin_dialog/seed/scripts/settings.lua | 9 + .../seed/scripts/settings/general.lua | 13 + .../seed/scripts/settings/init.lua | 4 + .../seed/scripts/settings/security.lua | 13 + packages/admin_dialog/seed/scripts/user.lua | 16 + packages/dashboard/seed/scripts/init.lua | 12 + packages/dashboard/seed/scripts/layout.lua | 17 + .../dashboard/seed/scripts/layout/flex.lua | 11 + .../dashboard/seed/scripts/layout/grid.lua | 11 + .../dashboard/seed/scripts/layout/init.lua | 7 + .../dashboard/seed/scripts/layout/section.lua | 9 + packages/dashboard/seed/scripts/stats.lua | 17 + .../dashboard/seed/scripts/stats/card.lua | 13 + .../dashboard/seed/scripts/stats/init.lua | 7 + packages/dashboard/seed/scripts/stats/row.lua | 9 + packages/data_table/seed/scripts/columns.lua | 56 +++ .../data_table/seed/scripts/columns/text.lua | 13 + packages/data_table/seed/scripts/init.lua | 11 + .../data_table/seed/scripts/pagination.lua | 30 ++ packages/data_table/seed/scripts/rows.lua | 34 ++ packages/form_builder/seed/scripts/fields.lua | 42 ++ packages/form_builder/seed/scripts/init.lua | 12 + packages/nav_menu/seed/scripts/init.lua | 16 +- .../nav_menu/seed/scripts/items/divider.lua | 5 + .../nav_menu/seed/scripts/items/group.lua | 11 + packages/nav_menu/seed/scripts/items/init.lua | 5 + packages/nav_menu/seed/scripts/items/item.lua | 11 + packages/nav_menu/seed/scripts/menu.lua | 29 ++ packages/nav_menu/seed/scripts/sidebar.lua | 22 + .../notification_center/seed/scripts/init.lua | 10 + .../notification_center/seed/scripts/list.lua | 23 + .../seed/scripts/list/init.lua | 4 + .../seed/scripts/summary.lua | 44 +- .../seed/scripts/toast.lua | 21 + .../seed/scripts/lua/audience_pulse.lua | 12 + .../stream_cast/seed/scripts/lua/init.lua | 14 + .../seed/scripts/lua/scene_router.lua | 4 + .../stream_cast/seed/scripts/lua/schedule.lua | 7 + packages/ui_auth/seed/scripts/denied.lua | 14 + packages/ui_auth/seed/scripts/gate.lua | 24 ++ packages/ui_auth/seed/scripts/init.lua | 12 + packages/ui_dialogs/seed/scripts/alert.lua | 14 + packages/ui_dialogs/seed/scripts/confirm.lua | 15 + packages/ui_dialogs/seed/scripts/init.lua | 12 + packages/ui_footer/seed/scripts/init.lua | 10 + packages/ui_footer/seed/scripts/render.lua | 11 + packages/ui_header/seed/scripts/init.lua | 10 + packages/ui_home/seed/scripts/init.lua | 14 + packages/ui_intro/seed/scripts/init.lua | 10 + packages/ui_level2/seed/scripts/init.lua | 12 + packages/ui_level3/seed/scripts/init.lua | 12 + packages/ui_level4/seed/scripts/init.lua | 12 + packages/ui_level5/seed/scripts/init.lua | 12 + packages/ui_level6/seed/scripts/init.lua | 14 + packages/ui_login/seed/scripts/actions.lua | 34 ++ packages/ui_login/seed/scripts/init.lua | 14 + packages/ui_permissions/seed/scripts/init.lua | 14 + 62 files changed, 1940 insertions(+), 11 deletions(-) create mode 100644 DEPENDENCY_CLEANUP.md create mode 100644 FAKEMUI_STRATEGY.md create mode 100644 fakemui/icons/README.md diff --git a/DEPENDENCY_CLEANUP.md b/DEPENDENCY_CLEANUP.md new file mode 100644 index 000000000..68cef8ce9 --- /dev/null +++ b/DEPENDENCY_CLEANUP.md @@ -0,0 +1,348 @@ +# Dependency Cleanup Plan + +**Created:** 2025-12-30 +**Status:** Planning Phase +**Goal:** Remove unnecessary dependencies from package.json + +--- + +## Current Dependencies Analysis + +### MUI Dependencies (TO REMOVE) + +**Location:** `frontends/nextjs/package.json` + +```json +"@emotion/react": "^11.14.0", // MUI peer dependency +"@emotion/styled": "^11.14.1", // MUI peer dependency +"@mui/icons-material": "^7.3.6", // ~500KB - Replace with fakemui icons +"@mui/material": "^7.3.6", // ~1MB - Replace with fakemui +"@mui/x-data-grid": "^8.23.0", // Replace with custom table +"@mui/x-date-pickers": "^8.23.0", // Replace with native date input +``` + +**Total Size:** ~2-3 MB +**Status:** ❌ Ready to remove after migration complete + +--- + +## Dependencies to Keep + +### Essential Next.js & React +```json +"next": "16.1.1", // ✅ Core framework +"react": "19.2.3", // ✅ Core library +"react-dom": "19.2.3", // ✅ Core library +``` + +### Icons +```json +"@phosphor-icons/react": "^2.1.10", // ❌ Remove - Using fakemui custom icons +``` + +**✅ DECISION:** Full fakemui custom SVG icon system +**Action:** Remove @phosphor-icons/react after icon migration complete + +### Form Handling +```json +"@hookform/resolvers": "^5.2.2", // ✅ Keep - Zod integration +"react-hook-form": "^7.69.0", // ✅ Keep - Form state management +"zod": "^4.2.1", // ✅ Keep - Schema validation +``` + +### Data Fetching & State +```json +"@tanstack/react-query": "^5.90.12", // ✅ Keep - Server state +``` + +### Database +```json +"@prisma/client": "^7.2.0", // ✅ Keep - Database ORM +"@prisma/adapter-better-sqlite3": "^7.2.0", // ✅ Keep - SQLite adapter +"better-sqlite3": "^12.5.0", // ✅ Keep - SQLite driver +``` + +### Code Editor +```json +"@monaco-editor/react": "^4.7.0", // ✅ Keep - Code editor +``` + +### Utilities +```json +"date-fns": "^4.1.0", // ✅ Keep - Date utilities +"marked": "^17.0.1", // ✅ Keep - Markdown parsing +"uuid": "^13.0.0", // ✅ Keep - UUID generation +"jszip": "^3.10.1", // ✅ Keep - ZIP handling +``` + +### Animation & Visualization +```json +"motion": "^12.6.2", // ✅ Keep - Animation library +"recharts": "^3.6.0", // ✅ Keep - Charts +"d3": "^7.9.0", // ✅ Keep - Data visualization +"three": "^0.182.0", // ✅ Keep - 3D graphics +``` + +### Lua Integration +```json +"fengari-web": "^0.1.4", // ✅ Keep - Lua in browser +"fengari-interop": "^0.1.4", // ✅ Keep - Lua/JS interop +``` + +### AWS S3 +```json +"@aws-sdk/client-s3": "^3.958.0", // ✅ Keep - S3 client +"@aws-sdk/lib-storage": "^3.958.0", // ✅ Keep - Multipart upload +"@aws-sdk/s3-request-presigner": "^3.958.0", // ✅ Keep - Presigned URLs +``` + +### GitHub Integration +```json +"@octokit/core": "^7.0.6", // ✅ Keep - GitHub API +"octokit": "^5.0.5", // ✅ Keep - GitHub SDK +"@github/spark": ">=0.43.1 <1", // ⚠️ Evaluate - What is this? +``` + +**Action Needed:** Verify @github/spark usage + +### Error Handling & Notifications +```json +"react-error-boundary": "^6.0.0", // ✅ Keep - Error boundaries +"sonner": "^2.0.7", // ✅ Keep - Toast notifications +``` + +### Misc +```json +"@next/third-parties": "^16.1.1", // ⚠️ Evaluate - Scripts loader +"server-only": "^0.0.1", // ✅ Keep - Server-only marker +"sharp": "^0.34.5", // ✅ Keep - Image optimization +``` + +--- + +## Removal Plan + +### Phase 1: Immediate Removal (After fakemui complete) + +**Remove:** +- `@mui/material` ^7.3.6 +- `@mui/icons-material` ^7.3.6 +- `@emotion/react` ^11.14.0 +- `@emotion/styled` ^11.14.1 + +**Bundle Size Savings:** ~1.5-2 MB + +**Prerequisites:** +- ✅ All MUI components migrated to fakemui +- ✅ All MUI icons replaced with fakemui icons +- ✅ Theme system migrated to CSS variables +- ✅ CssBaseline replaced with fakemui reset + +### Phase 2: Advanced Components (After Lua packages) + +**Remove:** +- `@mui/x-data-grid` ^8.23.0 +- `@mui/x-date-pickers` ^8.23.0 + +**Bundle Size Savings:** ~500 KB + +**Prerequisites:** +- ✅ DataGrid replaced with fakemui Table or Lua package +- ✅ DatePickers replaced with native HTML5 or Lua package + +### Phase 3: Icon Consolidation ✅ APPROVED + +**Remove:** +- `@phosphor-icons/react` ^2.1.10 + +**Bundle Size Savings:** ~200 KB + +**Decision:** Using fakemui custom SVG icons only (no third-party icon library) + +**Prerequisites:** +- ✅ All icons migrated to fakemui/icons +- ✅ Icon inventory complete (50-100 icons) +- ✅ Pattern established for adding new icons as needed + +--- + +## Dependencies to Evaluate + +### @github/spark +**Current Version:** >=0.43.1 <1 +**Purpose:** Unknown - needs investigation +**Action:** Search codebase for usage +**Decision:** Keep or remove based on usage + +### @next/third-parties +**Current Version:** ^16.1.1 +**Purpose:** Third-party script loading (Google Analytics, etc.) +**Action:** Check if actively used +**Decision:** Keep if used, remove if not + +--- + +## Post-Migration package.json + +**Estimated Dependencies After Cleanup:** 35-40 packages (down from 45) +**Estimated Bundle Size Reduction:** 2-3 MB (15-25%) + +### Final State +```json +{ + "dependencies": { + // Core + "next": "16.1.1", + "react": "19.2.3", + "react-dom": "19.2.3", + + // Forms & Validation + "@hookform/resolvers": "^5.2.2", + "react-hook-form": "^7.69.0", + "zod": "^4.2.1", + + // Data & State + "@tanstack/react-query": "^5.90.12", + "@prisma/client": "^7.2.0", + "@prisma/adapter-better-sqlite3": "^7.2.0", + "better-sqlite3": "^12.5.0", + + // Editor & Code + "@monaco-editor/react": "^4.7.0", + + // Utilities + "date-fns": "^4.1.0", + "marked": "^17.0.1", + "uuid": "^13.0.0", + "jszip": "^3.10.1", + + // Visualization + "motion": "^12.6.2", + "recharts": "^3.6.0", + "d3": "^7.9.0", + "three": "^0.182.0", + + // Lua + "fengari-web": "^0.1.4", + "fengari-interop": "^0.1.4", + + // AWS + "@aws-sdk/client-s3": "^3.958.0", + "@aws-sdk/lib-storage": "^3.958.0", + "@aws-sdk/s3-request-presigner": "^3.958.0", + + // GitHub + "@octokit/core": "^7.0.6", + "octokit": "^5.0.5", + + // UI & Feedback + "react-error-boundary": "^6.0.0", + "sonner": "^2.0.7", + + // Misc + "server-only": "^0.0.1", + "sharp": "^0.34.5" + } +} +``` + +--- + +## Removal Commands + +### Phase 1: MUI Removal +```bash +cd frontends/nextjs +npm uninstall @mui/material @mui/icons-material @emotion/react @emotion/styled +``` + +### Phase 2: MUI X Components +```bash +npm uninstall @mui/x-data-grid @mui/x-date-pickers +``` + +### Phase 3: Phosphor Icons +```bash +npm uninstall @phosphor-icons/react +``` + +--- + +## Verification Steps + +After each removal phase: + +1. **Build Test** + ```bash + npm run build + ``` + +2. **Type Check** + ```bash + npm run typecheck + ``` + +3. **Unit Tests** + ```bash + npm run test:unit + ``` + +4. **E2E Tests** + ```bash + npm run test:e2e + ``` + +5. **Bundle Analysis** + ```bash + # Check bundle size + npm run build + # Compare .next/static/chunks sizes + ``` + +--- + +## Risk Mitigation + +### Backup Strategy +```bash +# Before removal +git checkout -b mui-removal-phase-1 +cp package.json package.json.backup +npm list > installed-packages.txt +``` + +### Rollback Plan +```bash +# If issues arise +git checkout main +npm install +``` + +### Staged Rollout +1. Remove MUI deps but keep code +2. Test thoroughly +3. If successful, proceed +4. If issues, restore deps and investigate + +--- + +## Success Metrics + +- ✅ Zero MUI imports in codebase +- ✅ All tests passing +- ✅ No build errors +- ✅ Bundle size reduced 15-25% +- ✅ No visual regressions +- ✅ Performance metrics maintained or improved + +--- + +**Next Actions:** +1. Complete MUI migration (see MUI_ELIMINATION_PLAN.md) +2. Search for @github/spark usage +3. Check @next/third-parties usage +4. Execute Phase 1 removal +5. Verify and test +6. Proceed to Phase 2 + +**Last Updated:** 2025-12-30 diff --git a/FAKEMUI_STRATEGY.md b/FAKEMUI_STRATEGY.md new file mode 100644 index 000000000..4300a2f63 --- /dev/null +++ b/FAKEMUI_STRATEGY.md @@ -0,0 +1,397 @@ +# Fakemui Strategy - Zero Dependency UI System + +**Created:** 2025-12-30 +**Status:** Active Development +**Goal:** Complete UI independence with zero third-party UI dependencies + +--- + +## Executive Summary + +**Decision:** ✅ Full fakemui UI system +- Zero MUI dependencies +- Zero Phosphor Icons dependency +- Zero Emotion/styled-components +- Custom SVG icon system +- SCSS-based styling with CSS variables + +**Bundle Size Target:** Reduce by 2-3 MB (15-25%) + +--- + +## Architecture + +``` +fakemui/ +├── icons/ # Custom SVG icon system (~1KB per icon) +│ ├── Icon.tsx # Base icon component +│ ├── Plus.tsx # Individual icon components +│ ├── Trash.tsx +│ └── ... +├── fakemui/ # React/TypeScript components +│ ├── inputs/ # Form inputs, buttons +│ ├── surfaces/ # Cards, papers, dialogs +│ ├── layout/ # Box, Stack, Grid, Container +│ ├── data-display/ # Typography, tables, lists +│ ├── feedback/ # Alerts, progress, skeletons +│ ├── navigation/ # Tabs, breadcrumbs, menus +│ ├── utils/ # Modals, portals, transitions +│ ├── atoms/ # Text, Label, Panel +│ ├── lab/ # Experimental components +│ └── x/ # Advanced components +├── styles/ # SCSS styling system +│ ├── _variables.scss # CSS custom properties +│ ├── base.scss # Base element styles +│ ├── components.scss # Component styles +│ ├── atoms/ # Atomic component styles +│ ├── global/ # Global styles, reset +│ ├── mixins/ # Reusable SCSS mixins +│ └── themes/ # Theme variants +└── index.ts # Barrel export + +Total: ~150 components, ~80 SCSS files +``` + +--- + +## Icon Strategy + +### ✅ APPROVED: Custom SVG System + +**Approach:** +- Create SVG components as needed +- Source from Heroicons, Lucide, Phosphor +- ~1KB per icon (gzipped) +- Zero runtime dependencies +- Full TypeScript support + +**Current Icons:** 7 +**Target Phase 2:** 27 (core set) +**Target Phase 3:** 50-100 (full migration) + +**See:** [fakemui/icons/README.md](fakemui/icons/README.md) + +--- + +## Component Migration Path + +### From MUI → Fakemui + +| MUI Component | Fakemui Replacement | Status | +|---------------|---------------------|--------| +| `@mui/material/Button` | `fakemui/inputs/Button` | ✅ Ready | +| `@mui/material/TextField` | `fakemui/inputs/TextField` | ✅ Ready | +| `@mui/material/Card` | `fakemui/surfaces/Card` | ✅ Ready | +| `@mui/material/Box` | `fakemui/layout/Box` | ✅ Ready | +| `@mui/material/Stack` | `fakemui/layout/Stack` | ✅ Ready | +| `@mui/material/Typography` | `fakemui/data-display/Typography` | ✅ Ready | +| `@mui/icons-material/*` | `fakemui/icons/*` | 🚧 7/100 | +| `@mui/x-data-grid` | Lua package or custom | ⏳ Planned | +| `@mui/x-date-pickers` | Native HTML5 or Lua | ⏳ Planned | + +### From Phosphor → Fakemui + +| Phosphor Icon | Fakemui Icon | Status | +|---------------|--------------|--------| +| `` | `` | ✅ Done | +| `` | `` | ✅ Done | +| `` | `` | ✅ Done | +| `` | `` | ✅ Done | +| `` | `` | ✅ Done | +| `` | `` | ✅ Done | +| `` | `` | ✅ Done | +| Others (100+) | Add as needed | ⏳ Ongoing | + +--- + +## Styling Strategy + +### CSS Variables (Theme System) + +**Current:** `fakemui/styles/_variables.scss` +- 70+ CSS custom properties +- 5 theme variants (light, dark, midnight, forest, ocean) +- Runtime theme switching +- No JavaScript required + +**Example:** +```scss +:root { + --color-primary: #1976d2; + --color-bg: #ffffff; + --color-text: #000000; + --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto; + --spacing-md: 16px; + --radius-md: 8px; + --shadow-sm: 0 2px 4px rgba(0,0,0,0.1); +} +``` + +### SCSS Modules + +**Pattern:** +```tsx +// Component.tsx +import styles from './Component.module.scss' + +export const Component = () => ( +
+

Title

+
+) + +// Component.module.scss +.root { + padding: var(--spacing-md); + background: var(--color-bg-paper); +} + +.title { + color: var(--color-text); + font-size: var(--font-size-lg); +} +``` + +### Reset (Replaces CssBaseline) + +**Location:** `fakemui/styles/global/_reset.scss` +- Modern CSS reset +- Typography defaults +- Form element normalization +- Focus visible styles +- Accessibility defaults + +--- + +## Import Strategy + +### Before (MUI) +```tsx +import { Button, Card, Stack, Box } from '@mui/material' +import { Add, Delete, Edit } from '@mui/icons-material' +import { DataGrid } from '@mui/x-data-grid' +``` + +### After (Fakemui) +```tsx +import { Button, Card, Stack, Box, Plus, Trash, Edit } from '@/fakemui' +// DataGrid becomes Lua package +``` + +**Benefits:** +- Single import path +- No version conflicts +- Full control over API +- Tree-shakeable +- TypeScript native + +--- + +## Lua Package Strategy + +### Components → Lua Packages + +**Workflow Editor:** +- Package: `@packages/workflow_editor` +- Components: WorkflowEditor, WorkflowNodeCard, WorkflowSidebar, etc. +- UI: Uses fakemui for basic components + +**GitHub Viewer:** +- Package: `@packages/github_actions_viewer` +- Components: GitHubActionsFetcher, RunList, AnalysisPanel, etc. +- UI: Custom table with fakemui primitives + +**Data Grid:** +- Package: `@packages/data_grid` (TBD) +- Replaces: `@mui/x-data-grid` +- Features: Sorting, filtering, pagination, virtual scrolling + +**Date Picker:** +- Option 1: Native HTML5 `` +- Option 2: Lua package `@packages/date_picker` +- Decision: Start with native, create package if needed + +--- + +## Migration Phases + +### Phase 1: Foundation ✅ 60% Complete +- [x] Create fakemui icon system +- [x] Migrate LuaBlocksEditor +- [x] Enhance CSS reset +- [x] Document strategy +- [ ] Update providers (remove MUI) +- [ ] Finalize theme system + +### Phase 2: Atomic Components 🚧 Ready +Target: 20 components +- Button, IconButton, Fab +- Input, Textarea, Select +- Checkbox, Radio, Switch, Slider +- Avatar, Badge, Icon, Label, Link, Text +- Skeleton, Spinner, Progress, Tooltip, Separator + +### Phase 3: Molecules ⏳ Planned +Target: 18 components +- Card, Alert, Accordion +- Breadcrumb, NavGroup, NavItem, NavLink +- Dialog, DropdownMenu, Popover +- RadioGroup, Select, ToggleGroup +- Tabs components + +### Phase 4: Organisms ⏳ Planned +Target: 22 components +- Navigation, Sidebar, Pagination +- AlertDialog, Command, Sheet +- Form, Table + +### Phase 5: Lua Packages ⏳ Planned +- workflow_editor +- github_actions_viewer +- (others as needed) + +### Phase 6: Cleanup ⏳ Planned +- Remove all MUI dependencies +- Remove Phosphor dependency +- Final verification + +--- + +## Performance Targets + +### Bundle Size +| Phase | Current | Target | Savings | +|-------|---------|--------|---------| +| Before | ~12 MB | - | - | +| After Phase 1 | ~11.5 MB | ~11.5 MB | 500 KB | +| After Phase 2 | ~11 MB | ~10.5 MB | 1.5 MB | +| After Phase 3 | ~10 MB | ~9.5 MB | 2.5 MB | + +### Load Time +- First Contentful Paint (FCP): < 1.5s +- Largest Contentful Paint (LCP): < 2.5s +- Time to Interactive (TTI): < 3.5s + +### Runtime Performance +- Zero styled-components runtime overhead +- No Emotion runtime +- Pure CSS for styling +- Minimal JavaScript + +--- + +## Developer Experience + +### Type Safety +```tsx +import { Button, ButtonProps } from '@/fakemui' + +// Full TypeScript support +const MyButton: React.FC = (props) => ( + + ) +} +``` + +--- + +## Available Icons + +Current icon count: **7 icons** + +### Actions +- `Plus` - Add, create, new +- `Trash` - Delete, remove +- `Copy` - Duplicate, copy + +### Navigation +- `ArrowUp` - Move up, scroll up +- `ArrowDown` - Move down, scroll down +- `ArrowClockwise` - Refresh, reload, retry + +### Files +- `FloppyDisk` - Save, export + +--- + +## Icon Props + +All icons support the following props: + +```tsx +interface IconProps extends React.SVGAttributes { + size?: number | string // Default: 24 + weight?: 'thin' | 'light' | 'regular' | 'bold' // Default: 'regular' + // Plus all standard SVG attributes (className, onClick, etc.) +} +``` + +### Examples + +```tsx +// Size variants + // Small + // Default + // Large + +// Weight variants + // strokeWidth: 1 + // strokeWidth: 1.5 + // strokeWidth: 2 (default) + // strokeWidth: 2.5 + +// Custom styling + + + console.log('clicked')} /> +``` + +--- + +## Adding New Icons + +### Step 1: Find the SVG + +**Recommended Sources:** +- [Heroicons](https://heroicons.com/) - Tailwind's icon set +- [Lucide](https://lucide.dev/) - Beautiful, consistent icons +- [Phosphor Icons](https://phosphoricons.com/) - Flexible icon family + +### Step 2: Create the Component + +Create a new file in `fakemui/icons/IconName.tsx`: + +```tsx +import React from 'react' +import { Icon, IconProps } from './Icon' + +export const IconName = (props: IconProps) => ( + + {/* Paste SVG paths here */} + + + +) +``` + +**Important:** +- Use viewBox="0 0 256 256" coordinate system (Phosphor standard) +- Icons should be stroke-based, not fill-based +- Remove any fill/stroke attributes from paths (Icon component handles this) + +### Step 3: Export from index.ts + +Add to `fakemui/icons/index.ts`: + +```ts +export { IconName } from './IconName' +``` + +### Step 4: Export from main barrel + +Add to `fakemui/index.ts`: + +```ts +// Icons +export { + Icon, + Plus, + Trash, + Copy, + // ... existing icons + IconName, // Add your new icon +} from './icons' +``` + +### Step 5: Update this README + +Add your icon to the "Available Icons" section above. + +--- + +## Icon Conversion Guide + +### From Phosphor Icons + +Phosphor uses `viewBox="0 0 256 256"` - perfect for our system! + +```tsx +// Source: https://phosphoricons.com/ + + + + + +// fakemui component: +export const Plus = (props: IconProps) => ( + + + + +) +``` + +### From Heroicons + +Heroicons uses `viewBox="0 0 24 24"` - needs scaling! + +```tsx +// Source: https://heroicons.com/ + + + + +// Scale by 10.67 (256/24): +export const Plus = (props: IconProps) => ( + + + +) +``` + +### From Lucide + +Lucide uses `viewBox="0 0 24 24"` - same scaling as Heroicons. + +```tsx +// Source: https://lucide.dev/ + + + + + +// Scale coordinates: +export const Plus = (props: IconProps) => ( + + + + +) +``` + +--- + +## Icon Naming Conventions + +- **PascalCase** for component names: `ArrowUp`, `CheckCircle`, `UserPlus` +- **Descriptive** names: prefer `Trash` over `Delete`, `Copy` over `Duplicate` +- **Consistent** with existing patterns: `ArrowUp/Down/Left/Right` +- **Action-oriented**: `Plus` (not `Add`), `Trash` (not `Remove`) + +--- + +## Migration Progress + +### Phase 1: Core Actions (7/7) ✅ +- [x] Plus +- [x] Trash +- [x] Copy +- [x] ArrowUp +- [x] ArrowDown +- [x] ArrowClockwise +- [x] FloppyDisk + +### Phase 2: Common Icons (0/20) 🚧 +- [ ] Check +- [ ] X (Close) +- [ ] ChevronUp/Down/Left/Right (4 icons) +- [ ] Search +- [ ] Settings +- [ ] User +- [ ] Menu +- [ ] Eye/EyeSlash +- [ ] Edit (Pencil) +- [ ] Calendar +- [ ] Clock +- [ ] Mail +- [ ] Bell +- [ ] Star +- [ ] Heart +- [ ] Share + +### Phase 3: Specialized Icons (0/30+) ⏳ +Add as needed based on component migration + +--- + +## Icon Size Guidelines + +| Size | Use Case | Example | +|------|----------|---------| +| 12px | Tiny badges, indicators | Status dots | +| 16px | Small UI elements | Inline icons, table cells | +| 20px | Default UI | Buttons, inputs, cards | +| 24px | Standard size | Menu items, toolbars | +| 32px | Large UI | Headers, hero sections | +| 48px+ | Feature icons | Landing pages, empty states | + +--- + +## Performance Notes + +- Each icon is ~1KB gzipped +- Tree-shakeable (only bundle icons you use) +- No runtime dependencies +- SSR-friendly +- TypeScript definitions included + +--- + +## Comparison to Icon Libraries + +| Library | Size (full) | Size (10 icons) | Dependencies | +|---------|-------------|-----------------|--------------| +| **fakemui** | ~10KB | ~10KB | Zero | +| Phosphor Icons | 500KB | ~50KB | React | +| Lucide React | 300KB | ~30KB | React | +| Heroicons | 200KB | ~20KB | React | +| MUI Icons | 1MB+ | ~100KB | MUI, Emotion | + +**Winner:** fakemui - smallest bundle, zero deps, full control + +--- + +## Future Enhancements + +- [ ] Animated icon variants (spin, pulse, etc.) +- [ ] Icon sprite system for even better performance +- [ ] Auto-generate icons from Figma +- [ ] Visual icon gallery/documentation +- [ ] Dark mode variants (if needed) + +--- + +**Last Updated:** 2025-12-30 +**Maintainer:** Metabuilder Team diff --git a/packages/admin_dialog/seed/scripts/init.lua b/packages/admin_dialog/seed/scripts/init.lua index 8b63ad3ec..1fa6daeb4 100644 --- a/packages/admin_dialog/seed/scripts/init.lua +++ b/packages/admin_dialog/seed/scripts/init.lua @@ -1,9 +1,19 @@ -- Admin Dialog initialization + +---@class AdminDialog +---@field name string +---@field version string local M = {} M.name = "admin_dialog" M.version = "1.0.0" +---@class InitResult +---@field name string +---@field version string +---@field loaded boolean + +---@return InitResult function M.init() return { name = M.name, diff --git a/packages/admin_dialog/seed/scripts/settings.lua b/packages/admin_dialog/seed/scripts/settings.lua index bd8fb1c8f..a99679e26 100644 --- a/packages/admin_dialog/seed/scripts/settings.lua +++ b/packages/admin_dialog/seed/scripts/settings.lua @@ -1,6 +1,14 @@ -- Admin settings dialog + +---@class SettingsDialog local M = {} +---@class UIComponent +---@field type string +---@field props? table +---@field children? table + +---@return UIComponent function M.render_general() return { type = "dialog", @@ -17,6 +25,7 @@ function M.render_general() } end +---@return UIComponent function M.render_security() return { type = "dialog", diff --git a/packages/admin_dialog/seed/scripts/settings/general.lua b/packages/admin_dialog/seed/scripts/settings/general.lua index 599ee3b6b..418d403b8 100644 --- a/packages/admin_dialog/seed/scripts/settings/general.lua +++ b/packages/admin_dialog/seed/scripts/settings/general.lua @@ -1,4 +1,17 @@ -- Admin settings general section + +---@class SettingsSection +---@field type string +---@field id string +---@field title string +---@field fields SettingsField[] + +---@class SettingsField +---@field id string +---@field type string +---@field label string + +---@return SettingsSection local function general_settings() return { type = "settings_section", diff --git a/packages/admin_dialog/seed/scripts/settings/init.lua b/packages/admin_dialog/seed/scripts/settings/init.lua index 957607a20..9f68cdbf0 100644 --- a/packages/admin_dialog/seed/scripts/settings/init.lua +++ b/packages/admin_dialog/seed/scripts/settings/init.lua @@ -1,4 +1,8 @@ -- Admin settings module + +---@class SettingsModule +---@field general function +---@field security function local settings = { general = require("settings.general"), security = require("settings.security") diff --git a/packages/admin_dialog/seed/scripts/settings/security.lua b/packages/admin_dialog/seed/scripts/settings/security.lua index be699850c..6445cb702 100644 --- a/packages/admin_dialog/seed/scripts/settings/security.lua +++ b/packages/admin_dialog/seed/scripts/settings/security.lua @@ -1,4 +1,17 @@ -- Admin settings security section + +---@class SettingsSection +---@field type string +---@field id string +---@field title string +---@field fields SettingsField[] + +---@class SettingsField +---@field id string +---@field type string +---@field label string + +---@return SettingsSection local function security_settings() return { type = "settings_section", diff --git a/packages/admin_dialog/seed/scripts/user.lua b/packages/admin_dialog/seed/scripts/user.lua index 694cb7547..0ad06c2a9 100644 --- a/packages/admin_dialog/seed/scripts/user.lua +++ b/packages/admin_dialog/seed/scripts/user.lua @@ -1,6 +1,20 @@ -- User management dialog + +---@class UserDialog local M = {} +---@class User +---@field username string +---@field email string +---@field role string +---@field active boolean + +---@class UIComponent +---@field type string +---@field props? table +---@field children? table + +---@return UIComponent function M.render_create() return { type = "dialog", @@ -17,6 +31,8 @@ function M.render_create() } end +---@param user User +---@return UIComponent function M.render_edit(user) return { type = "dialog", diff --git a/packages/dashboard/seed/scripts/init.lua b/packages/dashboard/seed/scripts/init.lua index 64b9a9b18..7f9f7e88b 100644 --- a/packages/dashboard/seed/scripts/init.lua +++ b/packages/dashboard/seed/scripts/init.lua @@ -1,3 +1,15 @@ +---@class DashboardScripts local M = {} + +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@param ctx InstallContext +---@return InstallResult function M.on_install(ctx) return { message = "Dashboard installed", version = ctx.version } end + return M diff --git a/packages/dashboard/seed/scripts/layout.lua b/packages/dashboard/seed/scripts/layout.lua index cec555eb6..1b8b543e6 100644 --- a/packages/dashboard/seed/scripts/layout.lua +++ b/packages/dashboard/seed/scripts/layout.lua @@ -1,5 +1,20 @@ +---@class Layout local M = {} +---@class UIComponent +---@field type string +---@field props? table +---@field children? table + +---@class LayoutProps +---@field header? table +---@field footer? table +---@field sidebar? table +---@field children? table +---@field className? string + +---@param props LayoutProps +---@return UIComponent function M.standard(props) return { type = "Box", @@ -16,6 +31,8 @@ function M.standard(props) } end +---@param props LayoutProps +---@return UIComponent function M.with_sidebar(props) return { type = "Flex", diff --git a/packages/dashboard/seed/scripts/layout/flex.lua b/packages/dashboard/seed/scripts/layout/flex.lua index a82499e20..24a9a1ffe 100644 --- a/packages/dashboard/seed/scripts/layout/flex.lua +++ b/packages/dashboard/seed/scripts/layout/flex.lua @@ -1,4 +1,15 @@ -- Dashboard flex layout + +---@class FlexLayoutConfig +---@field type string +---@field direction string +---@field gap number +---@field align string + +---@param direction? string Direction of flex layout (default: "row") +---@param gap? number Gap between items in pixels (default: 16) +---@param align? string Alignment of items (default: "stretch") +---@return FlexLayoutConfig local function flex(direction, gap, align) return { type = "flex", diff --git a/packages/dashboard/seed/scripts/layout/grid.lua b/packages/dashboard/seed/scripts/layout/grid.lua index 020a2efc3..ef4130709 100644 --- a/packages/dashboard/seed/scripts/layout/grid.lua +++ b/packages/dashboard/seed/scripts/layout/grid.lua @@ -1,4 +1,15 @@ -- Dashboard grid layout + +---@class GridLayoutConfig +---@field type string +---@field columns number +---@field rows? number +---@field gap number + +---@param columns? number Number of columns (default: 3) +---@param rows? number Number of rows (optional) +---@param gap? number Gap between items in pixels (default: 16) +---@return GridLayoutConfig local function grid(columns, rows, gap) return { type = "grid", diff --git a/packages/dashboard/seed/scripts/layout/init.lua b/packages/dashboard/seed/scripts/layout/init.lua index 337c2c3cb..7d69bac2a 100644 --- a/packages/dashboard/seed/scripts/layout/init.lua +++ b/packages/dashboard/seed/scripts/layout/init.lua @@ -1,4 +1,11 @@ -- Dashboard layout module + +---@class LayoutModule +---@field grid fun(columns: number?, rows: number?, gap: number?): LayoutConfig +---@field flex fun(direction: string?, gap: number?, align: string?): LayoutConfig +---@field section fun(title: string, children: table?): LayoutConfig + +---@type LayoutModule local layout = { grid = require("layout.grid"), flex = require("layout.flex"), diff --git a/packages/dashboard/seed/scripts/layout/section.lua b/packages/dashboard/seed/scripts/layout/section.lua index 33d5f9f8d..5a0395b2b 100644 --- a/packages/dashboard/seed/scripts/layout/section.lua +++ b/packages/dashboard/seed/scripts/layout/section.lua @@ -1,4 +1,13 @@ -- Dashboard section layout + +---@class SectionLayoutConfig +---@field type string +---@field title string +---@field children table + +---@param title string Section title +---@param children? table Child elements (default: {}) +---@return SectionLayoutConfig local function section(title, children) return { type = "section", diff --git a/packages/dashboard/seed/scripts/stats.lua b/packages/dashboard/seed/scripts/stats.lua index 943c0324b..1d05d3967 100644 --- a/packages/dashboard/seed/scripts/stats.lua +++ b/packages/dashboard/seed/scripts/stats.lua @@ -1,5 +1,20 @@ +---@class Stats local M = {} +---@class UIComponent +---@field type string +---@field props? table +---@field children? table + +---@class StatCardProps +---@field label string +---@field value number|string +---@field change? string +---@field positive? boolean +---@field className? string + +---@param props StatCardProps +---@return UIComponent function M.card(props) return { type = "Card", @@ -21,6 +36,8 @@ function M.card(props) } end +---@param stats StatCardProps[] +---@return UIComponent function M.grid(stats) local items = {} for _, s in ipairs(stats) do diff --git a/packages/dashboard/seed/scripts/stats/card.lua b/packages/dashboard/seed/scripts/stats/card.lua index 6bae658bc..c0dbc1c3f 100644 --- a/packages/dashboard/seed/scripts/stats/card.lua +++ b/packages/dashboard/seed/scripts/stats/card.lua @@ -1,4 +1,17 @@ -- Dashboard stat card component + +---@class StatCardConfig +---@field type string +---@field title string +---@field value string|number +---@field icon? string +---@field trend? table + +---@param title string Card title +---@param value string|number Card value to display +---@param icon? string Optional icon identifier +---@param trend? table Optional trend indicator data +---@return StatCardConfig local function stat_card(title, value, icon, trend) return { type = "stat_card", diff --git a/packages/dashboard/seed/scripts/stats/init.lua b/packages/dashboard/seed/scripts/stats/init.lua index 6960028e8..002557ffd 100644 --- a/packages/dashboard/seed/scripts/stats/init.lua +++ b/packages/dashboard/seed/scripts/stats/init.lua @@ -1,4 +1,11 @@ -- Dashboard stats module + +---@class StatsModule +---@field card fun(title: string, value: string|number, icon: string?, trend: table?): StatCardConfig +---@field row fun(stats: table): StatRowConfig +---@field trend fun(direction: string, value: string): TrendIndicatorConfig + +---@type StatsModule local stats = { card = require("stats.card"), row = require("stats.row"), diff --git a/packages/dashboard/seed/scripts/stats/row.lua b/packages/dashboard/seed/scripts/stats/row.lua index 8f06daa79..2658426f3 100644 --- a/packages/dashboard/seed/scripts/stats/row.lua +++ b/packages/dashboard/seed/scripts/stats/row.lua @@ -1,4 +1,13 @@ -- Dashboard stat row layout + +---@class StatRowConfig +---@field type string +---@field layout string +---@field gap number +---@field children table + +---@param stats table Array of stat components to display in a row +---@return StatRowConfig local function stat_row(stats) return { type = "row", diff --git a/packages/data_table/seed/scripts/columns.lua b/packages/data_table/seed/scripts/columns.lua index fbf1a98cc..1ee210464 100644 --- a/packages/data_table/seed/scripts/columns.lua +++ b/packages/data_table/seed/scripts/columns.lua @@ -1,6 +1,48 @@ -- Column definition utilities + +---@class Columns local M = {} +---@class TextColumn +---@field type "text" +---@field id string Column identifier +---@field label string Column header label +---@field width string Column width (e.g., "auto", "100px") +---@field sortable boolean Whether the column is sortable + +---@class NumberColumn +---@field type "number" +---@field id string Column identifier +---@field label string Column header label +---@field width string Column width (e.g., "100px") +---@field sortable boolean Whether the column is sortable +---@field align "right" Text alignment + +---@class DateColumn +---@field type "date" +---@field id string Column identifier +---@field label string Column header label +---@field format string Date format string (e.g., "YYYY-MM-DD") +---@field sortable boolean Whether the column is sortable + +---@class Action +---@field label string Action button label +---@field handler string Action handler name + +---@class ActionColumn +---@field type "actions" +---@field id string Column identifier +---@field label string Column header label (typically empty) +---@field width string Column width (e.g., "120px") +---@field actions Action[] Array of actions + +---@alias Column TextColumn | NumberColumn | DateColumn | ActionColumn + +---Create a text column definition +---@param id string Column identifier +---@param label string Column header label +---@param width? string Column width (default: "auto") +---@return TextColumn function M.text_column(id, label, width) return { type = "text", @@ -11,6 +53,11 @@ function M.text_column(id, label, width) } end +---Create a number column definition +---@param id string Column identifier +---@param label string Column header label +---@param width? string Column width (default: "100px") +---@return NumberColumn function M.number_column(id, label, width) return { type = "number", @@ -22,6 +69,11 @@ function M.number_column(id, label, width) } end +---Create a date column definition +---@param id string Column identifier +---@param label string Column header label +---@param format? string Date format string (default: "YYYY-MM-DD") +---@return DateColumn function M.date_column(id, label, format) return { type = "date", @@ -32,6 +84,10 @@ function M.date_column(id, label, format) } end +---Create an action column definition +---@param id string Column identifier +---@param actions? Action[] Array of actions (default: {}) +---@return ActionColumn function M.action_column(id, actions) return { type = "actions", diff --git a/packages/data_table/seed/scripts/columns/text.lua b/packages/data_table/seed/scripts/columns/text.lua index bdf8fa080..7ac0a4ea3 100644 --- a/packages/data_table/seed/scripts/columns/text.lua +++ b/packages/data_table/seed/scripts/columns/text.lua @@ -1,4 +1,17 @@ -- Text column definition + +---@class TextColumn +---@field type "text" +---@field id string Column identifier +---@field label string Column header label +---@field width string Column width (e.g., "auto", "100px") +---@field sortable boolean Whether the column is sortable + +---Create a text column definition +---@param id string Column identifier +---@param label string Column header label +---@param width? string Column width (default: "auto") +---@return TextColumn local function text_column(id, label, width) return { type = "text", diff --git a/packages/data_table/seed/scripts/init.lua b/packages/data_table/seed/scripts/init.lua index 2baeda206..5513ec7ab 100644 --- a/packages/data_table/seed/scripts/init.lua +++ b/packages/data_table/seed/scripts/init.lua @@ -1,9 +1,20 @@ -- Data Table initialization + +---@class DataTable +---@field name string +---@field version string local M = {} M.name = "data_table" M.version = "1.0.0" +---@class DataTableInitResult +---@field name string +---@field version string +---@field loaded boolean + +---Initialize the data table module +---@return DataTableInitResult function M.init() return { name = M.name, diff --git a/packages/data_table/seed/scripts/pagination.lua b/packages/data_table/seed/scripts/pagination.lua index e592bd79d..d25c62f63 100644 --- a/packages/data_table/seed/scripts/pagination.lua +++ b/packages/data_table/seed/scripts/pagination.lua @@ -1,6 +1,33 @@ -- Pagination utilities + +---@class Pagination local M = {} +---@class PaginationState +---@field total integer Total number of items +---@field page integer Current page number (1-indexed) +---@field per_page integer Items per page +---@field pages integer Total number of pages +---@field has_prev boolean Whether there is a previous page +---@field has_next boolean Whether there is a next page + +---@class PaginationProps +---@field current integer Current page number +---@field total integer Total number of pages +---@field show_prev boolean Whether to show previous button +---@field show_next boolean Whether to show next button +---@field on_prev string Event handler name for previous button +---@field on_next string Event handler name for next button + +---@class PaginationComponent +---@field type "pagination" +---@field props PaginationProps + +---Calculate pagination state +---@param total integer Total number of items +---@param page integer Current page number (1-indexed) +---@param per_page integer Items per page +---@return PaginationState function M.calculate(total, page, per_page) local pages = math.ceil(total / per_page) return { @@ -13,6 +40,9 @@ function M.calculate(total, page, per_page) } end +---Render pagination component +---@param pagination PaginationState +---@return PaginationComponent function M.render(pagination) return { type = "pagination", diff --git a/packages/data_table/seed/scripts/rows.lua b/packages/data_table/seed/scripts/rows.lua index 16f8bbda2..5549239ff 100644 --- a/packages/data_table/seed/scripts/rows.lua +++ b/packages/data_table/seed/scripts/rows.lua @@ -1,6 +1,31 @@ -- Row rendering utilities + +---@class Rows local M = {} +---@class TextCell +---@field type "text" +---@field content string Cell content +---@field align? "right" Text alignment (optional) + +---@class ActionsCell +---@field type "actions" +---@field buttons Action[] Action buttons for the cell + +---@alias Cell TextCell | ActionsCell + +---@class Row +---@field index integer Row index (1-indexed) +---@field cells Cell[] Array of rendered cells +---@field selected boolean Whether the row is selected + +---@class RowData +---@field [string] any Key-value pairs representing row data + +---Render a single cell based on column type +---@param column Column Column definition +---@param value any Cell value +---@return Cell function M.render_cell(column, value) if column.type == "date" then return { type = "text", content = value } @@ -13,6 +38,11 @@ function M.render_cell(column, value) end end +---Render a single row with all its cells +---@param columns Column[] Array of column definitions +---@param data RowData Row data object +---@param index integer Row index (1-indexed) +---@return Row function M.render_row(columns, data, index) local cells = {} for _, col in ipairs(columns) do @@ -25,6 +55,10 @@ function M.render_row(columns, data, index) } end +---Render multiple rows from data list +---@param columns Column[] Array of column definitions +---@param data_list RowData[] Array of row data objects +---@return Row[] function M.render_rows(columns, data_list) local rows = {} for i, data in ipairs(data_list) do diff --git a/packages/form_builder/seed/scripts/fields.lua b/packages/form_builder/seed/scripts/fields.lua index 988bff6df..d857368c0 100644 --- a/packages/form_builder/seed/scripts/fields.lua +++ b/packages/form_builder/seed/scripts/fields.lua @@ -1,5 +1,39 @@ +---@class Fields local M = {} +---@class UIComponent +---@field type string +---@field props? table +---@field children? UIComponent[] + +---@class TextFieldProps +---@field name string +---@field label? string +---@field placeholder? string +---@field required? boolean + +---@class EmailFieldProps +---@field name string +---@field label? string + +---@class PasswordFieldProps +---@field name string +---@field label? string + +---@class NumberFieldProps +---@field name string +---@field label? string +---@field min? number +---@field max? number + +---@class TextAreaFieldProps +---@field name string +---@field label? string +---@field placeholder? string +---@field rows? number + +---@param props TextFieldProps +---@return UIComponent function M.text(props) return { type = "Box", @@ -10,6 +44,8 @@ function M.text(props) } end +---@param props EmailFieldProps +---@return UIComponent function M.email(props) return { type = "Box", @@ -20,6 +56,8 @@ function M.email(props) } end +---@param props PasswordFieldProps +---@return UIComponent function M.password(props) return { type = "Box", @@ -30,6 +68,8 @@ function M.password(props) } end +---@param props NumberFieldProps +---@return UIComponent function M.number(props) return { type = "Box", @@ -40,6 +80,8 @@ function M.number(props) } end +---@param props TextAreaFieldProps +---@return UIComponent function M.textarea(props) return { type = "Box", diff --git a/packages/form_builder/seed/scripts/init.lua b/packages/form_builder/seed/scripts/init.lua index 129974c10..917e9f5d4 100644 --- a/packages/form_builder/seed/scripts/init.lua +++ b/packages/form_builder/seed/scripts/init.lua @@ -1,3 +1,15 @@ +---@class FormBuilder local M = {} + +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@param ctx InstallContext +---@return InstallResult function M.on_install(ctx) return { message = "Form Builder installed", version = ctx.version } end + return M diff --git a/packages/nav_menu/seed/scripts/init.lua b/packages/nav_menu/seed/scripts/init.lua index 7a9bfe874..4b96bf593 100644 --- a/packages/nav_menu/seed/scripts/init.lua +++ b/packages/nav_menu/seed/scripts/init.lua @@ -1,3 +1,17 @@ +---@class NavMenuModule local M = {} -function M.on_install(ctx) return { message = "Nav Menu installed", version = ctx.version } end + +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@param ctx InstallContext +---@return InstallResult +function M.on_install(ctx) + return { message = "Nav Menu installed", version = ctx.version } +end + return M diff --git a/packages/nav_menu/seed/scripts/items/divider.lua b/packages/nav_menu/seed/scripts/items/divider.lua index e3fc71ba4..3454e1034 100644 --- a/packages/nav_menu/seed/scripts/items/divider.lua +++ b/packages/nav_menu/seed/scripts/items/divider.lua @@ -1,4 +1,9 @@ -- Nav menu divider component + +---@class MenuDivider +---@field type string + +---@return MenuDivider local function menu_divider() return { type = "divider" diff --git a/packages/nav_menu/seed/scripts/items/group.lua b/packages/nav_menu/seed/scripts/items/group.lua index c31bec9c3..060a66e7c 100644 --- a/packages/nav_menu/seed/scripts/items/group.lua +++ b/packages/nav_menu/seed/scripts/items/group.lua @@ -1,4 +1,15 @@ -- Nav menu group component + +---@class MenuGroup +---@field type string +---@field label string +---@field icon? string +---@field children table + +---@param label string +---@param children? table +---@param icon? string +---@return MenuGroup local function menu_group(label, children, icon) return { type = "menu_group", diff --git a/packages/nav_menu/seed/scripts/items/init.lua b/packages/nav_menu/seed/scripts/items/init.lua index c3200eb50..c805d9cd4 100644 --- a/packages/nav_menu/seed/scripts/items/init.lua +++ b/packages/nav_menu/seed/scripts/items/init.lua @@ -1,4 +1,9 @@ -- Nav menu items module + +---@class MenuItemsModule +---@field item fun(label: string, path: string, icon?: string): MenuItemData +---@field group fun(label: string, children?: table, icon?: string): MenuGroup +---@field divider fun(): MenuDivider local items = { item = require("items.item"), group = require("items.group"), diff --git a/packages/nav_menu/seed/scripts/items/item.lua b/packages/nav_menu/seed/scripts/items/item.lua index 961dc5a25..1fc60a82b 100644 --- a/packages/nav_menu/seed/scripts/items/item.lua +++ b/packages/nav_menu/seed/scripts/items/item.lua @@ -1,4 +1,15 @@ -- Nav menu item component + +---@class MenuItemData +---@field type string +---@field label string +---@field path string +---@field icon? string + +---@param label string +---@param path string +---@param icon? string +---@return MenuItemData local function menu_item(label, path, icon) return { type = "menu_item", diff --git a/packages/nav_menu/seed/scripts/menu.lua b/packages/nav_menu/seed/scripts/menu.lua index 2e46a8f1e..799f0497d 100644 --- a/packages/nav_menu/seed/scripts/menu.lua +++ b/packages/nav_menu/seed/scripts/menu.lua @@ -1,6 +1,28 @@ local check = require("check") + +---@class MenuModule local M = {} +---@class UIComponent +---@field type string +---@field props? table +---@field children? table + +---@class MenuItem +---@field label string +---@field path? string +---@field minLevel? number +---@field children? MenuItem[] + +---@class User +---@field level? number + +---@class MenuProps +---@field items MenuItem[] +---@field user User + +---@param props MenuProps +---@return UIComponent function M.render(props) local items = {} for _, item in ipairs(props.items or {}) do @@ -15,11 +37,16 @@ function M.render(props) } end +---@param user User +---@param item MenuItem +---@return boolean function M.can_show(user, item) if not item.minLevel then return true end return check.can_access(user, item.minLevel) end +---@param item MenuItem +---@return UIComponent function M.item(item) if item.children then return { @@ -33,6 +60,8 @@ function M.item(item) return { type = "Button", props = { variant = "ghost", text = item.label, onClick = "navigate", data = item.path } } end +---@param children MenuItem[] +---@return UIComponent[] function M.sub_items(children) local items = {} for _, c in ipairs(children) do diff --git a/packages/nav_menu/seed/scripts/sidebar.lua b/packages/nav_menu/seed/scripts/sidebar.lua index 62e5fa01f..d32833715 100644 --- a/packages/nav_menu/seed/scripts/sidebar.lua +++ b/packages/nav_menu/seed/scripts/sidebar.lua @@ -1,5 +1,22 @@ +---@class SidebarModule local M = {} +---@class UIComponent +---@field type string +---@field props? table +---@field children? table + +---@class SidebarItem +---@field label string +---@field path string + +---@class SidebarProps +---@field items SidebarItem[] +---@field currentPath? string +---@field title? string + +---@param props SidebarProps +---@return UIComponent function M.render(props) local items = {} for _, item in ipairs(props.items or {}) do @@ -15,6 +32,8 @@ function M.render(props) } end +---@param props SidebarProps +---@return UIComponent function M.header(props) return { type = "Box", @@ -25,6 +44,9 @@ function M.header(props) } end +---@param item SidebarItem +---@param currentPath? string +---@return UIComponent function M.item(item, currentPath) local active = currentPath == item.path return { diff --git a/packages/notification_center/seed/scripts/init.lua b/packages/notification_center/seed/scripts/init.lua index a182cfb20..e3b67c0a9 100644 --- a/packages/notification_center/seed/scripts/init.lua +++ b/packages/notification_center/seed/scripts/init.lua @@ -1,9 +1,19 @@ -- Notification Center initialization + +---@class NotificationCenter +---@field name string +---@field version string local M = {} M.name = "notification_center" M.version = "1.0.0" +---@class NotificationCenterInfo +---@field name string +---@field version string +---@field loaded boolean + +---@return NotificationCenterInfo function M.init() return { name = M.name, diff --git a/packages/notification_center/seed/scripts/list.lua b/packages/notification_center/seed/scripts/list.lua index af9f7bf0d..fdd7e6218 100644 --- a/packages/notification_center/seed/scripts/list.lua +++ b/packages/notification_center/seed/scripts/list.lua @@ -1,6 +1,25 @@ -- Notification list utilities + +---@class NotificationListUtils local M = {} +---@class Notification +---@field id string|number +---@field title string +---@field message string +---@field created_at string|number +---@field read? boolean +---@field icon? string + +---@class UIComponent +---@field type string +---@field props? table +---@field children? table +---@field content? string +---@field variant? string + +---@param notification Notification +---@return UIComponent function M.render_item(notification) return { type = "notification_item", @@ -15,6 +34,8 @@ function M.render_item(notification) } end +---@param notifications Notification[] +---@return UIComponent function M.render_list(notifications) local items = {} for _, n in ipairs(notifications) do @@ -26,6 +47,8 @@ function M.render_list(notifications) } end +---@param count number +---@return UIComponent|nil function M.render_badge(count) if count > 0 then return { diff --git a/packages/notification_center/seed/scripts/list/init.lua b/packages/notification_center/seed/scripts/list/init.lua index e0a745c2a..8e76c2bff 100644 --- a/packages/notification_center/seed/scripts/list/init.lua +++ b/packages/notification_center/seed/scripts/list/init.lua @@ -1,4 +1,8 @@ -- Notification list module + +---@class NotificationListModule +---@field item fun(id: string|number, title: string, message: string, timestamp: string|number, read?: boolean): UIComponent +---@field container fun(notifications?: table[]): UIComponent local list = { item = require("list.item"), container = require("list.container") diff --git a/packages/notification_center/seed/scripts/summary.lua b/packages/notification_center/seed/scripts/summary.lua index 086b00229..ead7b328d 100644 --- a/packages/notification_center/seed/scripts/summary.lua +++ b/packages/notification_center/seed/scripts/summary.lua @@ -1,5 +1,9 @@ -- Notification summary card component logic local json = require("json") + +---@class NotificationSummary +---@field severityClasses table +---@field defaultItems SummaryItem[] local M = {} -- Load config from JSON file @@ -7,7 +11,35 @@ local config = json.load("summary.json") M.severityClasses = config.severityClasses M.defaultItems = config.defaultItems +---@class SummaryItem +---@field label string +---@field count? number +---@field severity? "info"|"success"|"warning"|"error" +---@field hint? string + +---@class EnrichedSummaryItem +---@field label string +---@field count number +---@field severity "info"|"success"|"warning"|"error" +---@field hint? string +---@field classes string + +---@class SummaryProps +---@field title? string +---@field subtitle? string +---@field totalLabel? string +---@field items? SummaryItem[] + +---@class SummaryData +---@field title string +---@field subtitle? string +---@field totalLabel string +---@field total number +---@field items EnrichedSummaryItem[] + -- Calculate total from items +---@param items SummaryItem[] +---@return number function M.calculateTotal(items) local total = 0 for _, item in ipairs(items) do @@ -20,23 +52,27 @@ function M.calculateTotal(items) end -- Get severity class for an item +---@param severity? "info"|"success"|"warning"|"error" +---@return string function M.getSeverityClass(severity) return M.severityClasses[severity or "info"] or M.severityClasses.info end -- Prepare summary data +---@param props SummaryProps +---@return SummaryData function M.prepareSummary(props) local title = props.title or "Notification Summary" local subtitle = props.subtitle local totalLabel = props.totalLabel or "Total" local items = props.items - + if not items or #items == 0 then items = M.defaultItems end - + local total = M.calculateTotal(items) - + -- Enrich items with severity classes local enrichedItems = {} for i, item in ipairs(items) do @@ -48,7 +84,7 @@ function M.prepareSummary(props) classes = M.getSeverityClass(item.severity) } end - + return { title = title, subtitle = subtitle, diff --git a/packages/notification_center/seed/scripts/toast.lua b/packages/notification_center/seed/scripts/toast.lua index fd4bfe393..545a97cb3 100644 --- a/packages/notification_center/seed/scripts/toast.lua +++ b/packages/notification_center/seed/scripts/toast.lua @@ -1,6 +1,18 @@ -- Toast notification utilities + +---@class ToastUtils local M = {} +---@class Toast +---@field type string +---@field variant "info"|"success"|"warning"|"error" +---@field message string +---@field duration number +---@field icon string + +---@param message string +---@param duration? number +---@return Toast function M.success(message, duration) return { type = "toast", @@ -11,6 +23,9 @@ function M.success(message, duration) } end +---@param message string +---@param duration? number +---@return Toast function M.error(message, duration) return { type = "toast", @@ -21,6 +36,9 @@ function M.error(message, duration) } end +---@param message string +---@param duration? number +---@return Toast function M.warning(message, duration) return { type = "toast", @@ -31,6 +49,9 @@ function M.warning(message, duration) } end +---@param message string +---@param duration? number +---@return Toast function M.info(message, duration) return { type = "toast", diff --git a/packages/stream_cast/seed/scripts/lua/audience_pulse.lua b/packages/stream_cast/seed/scripts/lua/audience_pulse.lua index 3bec8cbf1..521865951 100644 --- a/packages/stream_cast/seed/scripts/lua/audience_pulse.lua +++ b/packages/stream_cast/seed/scripts/lua/audience_pulse.lua @@ -1,5 +1,17 @@ +---@class AudiencePulse local M = {} +---@class Metrics +---@field messagesPerMinute? number +---@field reactionsPerMinute? number +---@field viewers? number + +---@class PulseResult +---@field score number +---@field tier "surging"|"steady"|"cooling" + +---@param metrics Metrics +---@return PulseResult function M.score(metrics) local messages = metrics.messagesPerMinute or 0 local reactions = metrics.reactionsPerMinute or 0 diff --git a/packages/stream_cast/seed/scripts/lua/init.lua b/packages/stream_cast/seed/scripts/lua/init.lua index 9d1afc233..f563b8d17 100644 --- a/packages/stream_cast/seed/scripts/lua/init.lua +++ b/packages/stream_cast/seed/scripts/lua/init.lua @@ -1,9 +1,23 @@ +---@class StreamCast local M = {} +---@class InstallContext +---@field version? string + +---@class InstallResult +---@field message string +---@field version? string + +---@class UninstallResult +---@field message string + +---@param context InstallContext +---@return InstallResult function M.on_install(context) return { message = "Stream Cast installed", version = context.version } end +---@return UninstallResult function M.on_uninstall() return { message = "Stream Cast removed" } end diff --git a/packages/stream_cast/seed/scripts/lua/scene_router.lua b/packages/stream_cast/seed/scripts/lua/scene_router.lua index 1e121e4d4..5470fe90c 100644 --- a/packages/stream_cast/seed/scripts/lua/scene_router.lua +++ b/packages/stream_cast/seed/scripts/lua/scene_router.lua @@ -1,11 +1,15 @@ +---@class SceneRouter local M = {} +---@type table local routes = { intro = "studio_a", main = "studio_b", qa = "studio_c" } +---@param scene string +---@return string function M.route(scene) return routes[scene] or "studio_b" end diff --git a/packages/stream_cast/seed/scripts/lua/schedule.lua b/packages/stream_cast/seed/scripts/lua/schedule.lua index f6592e012..df2cf340b 100644 --- a/packages/stream_cast/seed/scripts/lua/schedule.lua +++ b/packages/stream_cast/seed/scripts/lua/schedule.lua @@ -1,5 +1,12 @@ +---@class Schedule local M = {} +---@class ScheduleEntry +---@field scene string + +---@param schedule ScheduleEntry[] +---@param current_index? number +---@return string? function M.next_scene(schedule, current_index) local index = current_index or 1 local entry = schedule[index] diff --git a/packages/ui_auth/seed/scripts/denied.lua b/packages/ui_auth/seed/scripts/denied.lua index 3a71b3113..6e5f65b9a 100644 --- a/packages/ui_auth/seed/scripts/denied.lua +++ b/packages/ui_auth/seed/scripts/denied.lua @@ -1,5 +1,19 @@ +---@class AccessDenied local M = {} +---@class DeniedProps +---@field title? string +---@field description? string +---@field actionLabel? string +---@field onAction? string + +---@class UIComponent +---@field type string +---@field props? table +---@field children? table + +---@param props DeniedProps +---@return UIComponent function M.render(props) return { type = "Stack", diff --git a/packages/ui_auth/seed/scripts/gate.lua b/packages/ui_auth/seed/scripts/gate.lua index febf8549c..a17194882 100644 --- a/packages/ui_auth/seed/scripts/gate.lua +++ b/packages/ui_auth/seed/scripts/gate.lua @@ -1,6 +1,28 @@ local check = require("check") + +---@class AuthGate local M = {} +---@class User +---@field id string +---@field level? number + +---@class GateContext +---@field user? User +---@field requiredLevel? number +---@field children? table + +---@class CheckResult +---@field allowed boolean +---@field reason? string +---@field redirect? string + +---@class UIComponent +---@field type string +---@field props? table + +---@param ctx GateContext +---@return CheckResult function M.check(ctx) if not ctx.user then return { allowed = false, reason = "not_authenticated", redirect = "/login" } @@ -11,6 +33,8 @@ function M.check(ctx) return { allowed = true } end +---@param ctx GateContext +---@return UIComponent|table function M.wrap(ctx) local result = M.check(ctx) if not result.allowed then diff --git a/packages/ui_auth/seed/scripts/init.lua b/packages/ui_auth/seed/scripts/init.lua index ea0f850a5..75536fd34 100644 --- a/packages/ui_auth/seed/scripts/init.lua +++ b/packages/ui_auth/seed/scripts/init.lua @@ -1,3 +1,15 @@ +---@class UIAuth local M = {} + +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@param ctx InstallContext +---@return InstallResult function M.on_install(ctx) return { message = "Auth UI installed", version = ctx.version } end + return M diff --git a/packages/ui_dialogs/seed/scripts/alert.lua b/packages/ui_dialogs/seed/scripts/alert.lua index 211652af7..2e3ed77a7 100644 --- a/packages/ui_dialogs/seed/scripts/alert.lua +++ b/packages/ui_dialogs/seed/scripts/alert.lua @@ -1,5 +1,19 @@ +---@class Alert local M = {} +---@class AlertProps +---@field open boolean +---@field variant? "info"|"warning"|"error" +---@field title string +---@field message string + +---@class UIComponent +---@field type string +---@field props? table +---@field children? table + +---@param props AlertProps +---@return UIComponent function M.render(props) local variant = props.variant or "info" local icon = variant == "error" and "alert-circle" or variant == "warning" and "alert-triangle" or "info" diff --git a/packages/ui_dialogs/seed/scripts/confirm.lua b/packages/ui_dialogs/seed/scripts/confirm.lua index f2accd33e..d89a3707e 100644 --- a/packages/ui_dialogs/seed/scripts/confirm.lua +++ b/packages/ui_dialogs/seed/scripts/confirm.lua @@ -1,5 +1,20 @@ +---@class Confirm local M = {} +---@class ConfirmProps +---@field open boolean +---@field title? string +---@field message string +---@field confirmText? string +---@field destructive? boolean + +---@class UIComponent +---@field type string +---@field props? table +---@field children? table + +---@param props ConfirmProps +---@return UIComponent function M.render(props) return { type = "Dialog", diff --git a/packages/ui_dialogs/seed/scripts/init.lua b/packages/ui_dialogs/seed/scripts/init.lua index c5e9a3edd..5d1be5c9c 100644 --- a/packages/ui_dialogs/seed/scripts/init.lua +++ b/packages/ui_dialogs/seed/scripts/init.lua @@ -1,3 +1,15 @@ +---@class UIDialogs local M = {} + +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@param ctx InstallContext +---@return InstallResult function M.on_install(ctx) return { message = "UI Dialogs installed", version = ctx.version } end + return M diff --git a/packages/ui_footer/seed/scripts/init.lua b/packages/ui_footer/seed/scripts/init.lua index 24917828b..af33f4df7 100644 --- a/packages/ui_footer/seed/scripts/init.lua +++ b/packages/ui_footer/seed/scripts/init.lua @@ -1,5 +1,15 @@ +---@class UIFooter local M = {} +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@param ctx InstallContext +---@return InstallResult function M.on_install(ctx) return { message = "App Footer installed", version = ctx.version } end diff --git a/packages/ui_footer/seed/scripts/render.lua b/packages/ui_footer/seed/scripts/render.lua index 2a989d3d2..801a7b187 100644 --- a/packages/ui_footer/seed/scripts/render.lua +++ b/packages/ui_footer/seed/scripts/render.lua @@ -1,5 +1,16 @@ +---@class FooterRender local M = {} +---@class FooterProps +---@field text? string + +---@class UIComponent +---@field type string +---@field props? table +---@field children? table + +---@param props FooterProps +---@return UIComponent function M.render(props) local text = props.text or "© 2024 MetaBuilder. Built with visual workflows." return { diff --git a/packages/ui_header/seed/scripts/init.lua b/packages/ui_header/seed/scripts/init.lua index 36c588368..68146394d 100644 --- a/packages/ui_header/seed/scripts/init.lua +++ b/packages/ui_header/seed/scripts/init.lua @@ -1,5 +1,15 @@ +---@class UIHeader local M = {} +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@param ctx InstallContext +---@return InstallResult function M.on_install(ctx) return { message = "App Header installed", version = ctx.version } end diff --git a/packages/ui_home/seed/scripts/init.lua b/packages/ui_home/seed/scripts/init.lua index c099a4971..3884052ef 100644 --- a/packages/ui_home/seed/scripts/init.lua +++ b/packages/ui_home/seed/scripts/init.lua @@ -1,9 +1,23 @@ +---@class UIHome local M = {} +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@class UninstallResult +---@field message string + +---@param ctx InstallContext +---@return InstallResult function M.on_install(ctx) return { message = "Home Page installed", version = ctx.version } end +---@return UninstallResult function M.on_uninstall() return { message = "Home Page removed" } end diff --git a/packages/ui_intro/seed/scripts/init.lua b/packages/ui_intro/seed/scripts/init.lua index a916b646c..b7b013bd7 100644 --- a/packages/ui_intro/seed/scripts/init.lua +++ b/packages/ui_intro/seed/scripts/init.lua @@ -1,5 +1,15 @@ +---@class UIIntro local M = {} +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@param ctx InstallContext +---@return InstallResult function M.on_install(ctx) return { message = "Intro Section installed", version = ctx.version } end diff --git a/packages/ui_level2/seed/scripts/init.lua b/packages/ui_level2/seed/scripts/init.lua index ba6f2947a..3afef8aa6 100644 --- a/packages/ui_level2/seed/scripts/init.lua +++ b/packages/ui_level2/seed/scripts/init.lua @@ -1,3 +1,15 @@ +---@class UILevel2 local M = {} + +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@param ctx InstallContext +---@return InstallResult function M.on_install(ctx) return { message = "Level 2 installed", version = ctx.version } end + return M diff --git a/packages/ui_level3/seed/scripts/init.lua b/packages/ui_level3/seed/scripts/init.lua index a5e116d85..3c7b597b8 100644 --- a/packages/ui_level3/seed/scripts/init.lua +++ b/packages/ui_level3/seed/scripts/init.lua @@ -1,3 +1,15 @@ +---@class UILevel3 local M = {} + +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@param ctx InstallContext +---@return InstallResult function M.on_install(ctx) return { message = "Level 3 installed", version = ctx.version } end + return M diff --git a/packages/ui_level4/seed/scripts/init.lua b/packages/ui_level4/seed/scripts/init.lua index 7af2f4f94..53138099b 100644 --- a/packages/ui_level4/seed/scripts/init.lua +++ b/packages/ui_level4/seed/scripts/init.lua @@ -1,3 +1,15 @@ +---@class UILevel4 local M = {} + +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@param ctx InstallContext +---@return InstallResult function M.on_install(ctx) return { message = "Level 4 installed", version = ctx.version } end + return M diff --git a/packages/ui_level5/seed/scripts/init.lua b/packages/ui_level5/seed/scripts/init.lua index 558a01a60..eadef87dc 100644 --- a/packages/ui_level5/seed/scripts/init.lua +++ b/packages/ui_level5/seed/scripts/init.lua @@ -1,3 +1,15 @@ +---@class UILevel5 local M = {} + +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@param ctx InstallContext +---@return InstallResult function M.on_install(ctx) return { message = "Level 5 installed", version = ctx.version } end + return M diff --git a/packages/ui_level6/seed/scripts/init.lua b/packages/ui_level6/seed/scripts/init.lua index 61d2a7e44..1912dd752 100644 --- a/packages/ui_level6/seed/scripts/init.lua +++ b/packages/ui_level6/seed/scripts/init.lua @@ -1,10 +1,24 @@ -- Level 6 (Supergod) package initialization local levels = require("ui_permissions.levels") +---@class UILevel6 local M = {} +---@type number M.REQUIRED_LEVEL = levels.SUPERGOD +---@class User +---@field level number + +---@class InitContext +---@field user User + +---@class InitResult +---@field allowed boolean +---@field redirect? string + +---@param context InitContext +---@return InitResult function M.init(context) if context.user.level < M.REQUIRED_LEVEL then return { allowed = false, redirect = "/access-denied" } diff --git a/packages/ui_login/seed/scripts/actions.lua b/packages/ui_login/seed/scripts/actions.lua index d2f3bd921..c6de2cdf8 100644 --- a/packages/ui_login/seed/scripts/actions.lua +++ b/packages/ui_login/seed/scripts/actions.lua @@ -1,6 +1,38 @@ local validate = require("validate") + +---@class LoginActions local M = {} +---@class LoginForm +---@field username string +---@field password string + +---@class RegisterForm +---@field username string +---@field email string +---@field password string + +---@class ValidationError +---@field field string +---@field message string + +---@class LoginSuccess +---@field success true +---@field action "login" +---@field username string + +---@class RegisterSuccess +---@field success true +---@field action "register" +---@field username string +---@field email string + +---@class ActionFailure +---@field success false +---@field errors ValidationError[] + +---@param form LoginForm +---@return LoginSuccess|ActionFailure function M.handleLogin(form) local result = validate.login(form) if not result.valid then @@ -9,6 +41,8 @@ function M.handleLogin(form) return { success = true, action = "login", username = form.username } end +---@param form RegisterForm +---@return RegisterSuccess|ActionFailure function M.handleRegister(form) local result = validate.register(form) if not result.valid then diff --git a/packages/ui_login/seed/scripts/init.lua b/packages/ui_login/seed/scripts/init.lua index a5cf908e1..e2635cd8f 100644 --- a/packages/ui_login/seed/scripts/init.lua +++ b/packages/ui_login/seed/scripts/init.lua @@ -1,9 +1,23 @@ +---@class UILogin local M = {} +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@class UninstallResult +---@field message string + +---@param ctx InstallContext +---@return InstallResult function M.on_install(ctx) return { message = "Login Page installed", version = ctx.version } end +---@return UninstallResult function M.on_uninstall() return { message = "Login Page removed" } end diff --git a/packages/ui_permissions/seed/scripts/init.lua b/packages/ui_permissions/seed/scripts/init.lua index d72dabaeb..47de7e817 100644 --- a/packages/ui_permissions/seed/scripts/init.lua +++ b/packages/ui_permissions/seed/scripts/init.lua @@ -1,9 +1,23 @@ +---@class UIPermissions local M = {} +---@class InstallContext +---@field version string + +---@class InstallResult +---@field message string +---@field version string + +---@class UninstallResult +---@field message string + +---@param ctx InstallContext +---@return InstallResult function M.on_install(ctx) return { message = "UI Permissions installed", version = ctx.version } end +---@return UninstallResult function M.on_uninstall() return { message = "UI Permissions removed" } end