feat: implement MUI dependency elimination plan and update related styles and scripts

This commit is contained in:
2025-12-30 10:09:52 +00:00
parent e3ffff2ad7
commit c107c63848
18 changed files with 847 additions and 1 deletions

586
MUI_ELIMINATION_PLAN.md Normal file
View File

@@ -0,0 +1,586 @@
# MUI Dependency Elimination Plan
**Status:** Planning Phase
**Created:** 2025-12-30
**Goal:** Remove @mui/material dependency completely from the codebase
---
## Executive Summary
**Total Files Using MUI:** 160+
**Total MUI Imports:** 24,273+ import statements
**Migration Strategy:** 6 phases over 8 weeks
**Key Approach:** Lua packages for editors, fakemui for basic UI
---
## Phase Overview
```
Phase 1: Foundation (Weeks 1-2) ✓ Partially Complete
Phase 2: Atomic Components (Weeks 2-3) ⏳ Ready to start
Phase 3: Basic UI (Weeks 3-4) ⏳ Pending
Phase 4: Complex UI (Weeks 4-6) ⏳ Pending
Phase 5: Lua Packages (Weeks 6-8) ⏳ Pending
Phase 6: Cleanup (Week 8+) ⏳ Pending
```
---
## Category Breakdown
### 🎯 Category 1: Lua Packages (12 files)
**Components that should become dedicated Lua packages**
#### 1.1 Workflow Editor Suite (8 files)
- **Path:** `frontends/nextjs/src/components/workflow/`
- **Package Name:** `@packages/workflow_editor`
- **Files:**
- WorkflowEditor.tsx
- WorkflowRunCard.tsx
- WorkflowRunStatus.tsx
- editor/WorkflowNodeCard.tsx
- editor/WorkflowSidebar.tsx
- editor/WorkflowTester.tsx
- editor/WorkflowNodesPanel.tsx
- editor/constants.tsx
**Complexity:** Medium-High
**MUI Usage:** Icons (PlayArrow, CheckCircle, Delete), Layout (Card, Stack, Box)
**Recommendation:** Convert to Lua package with JSON-based node system
#### 1.2 GitHub Actions Viewer (10 files)
- **Path:** `frontends/nextjs/src/components/misc/github/`
- **Package Name:** `@packages/github_actions_viewer`
- **Files:**
- GitHubActionsFetcher.tsx
- views/AnalysisPanel.tsx
- views/RunList.tsx
- views/RunDetails.tsx
- views/run-list/* (6 files)
**Complexity:** Medium
**MUI Usage:** Tables, Chips, Alerts, Typography
**Recommendation:** Convert to Lua package with data table component
---
### 🔧 Category 2: Fakemui Migration (66 files)
**Basic UI components that should migrate to fakemui**
#### 2.1 Atomic Components (20 files)
**Path:** `frontends/nextjs/src/components/atoms/`
**Controls (7 files):**
- Button.tsx → fakemui/inputs/Button
- Checkbox.tsx → fakemui/inputs/Checkbox
- Radio.tsx → fakemui/inputs/Radio
- Switch.tsx → fakemui/inputs/Switch
- Slider.tsx → fakemui/inputs/Slider
- Toggle.tsx → fakemui/inputs/ToggleButton
**Display (8 files):**
- Avatar.tsx → fakemui/data-display/Avatar
- Badge.tsx → fakemui/data-display/Badge
- Icon.tsx → fakemui/icons/* (custom system)
- IconButton.tsx → fakemui/inputs/IconButton
- Label.tsx → fakemui/atoms/Label
- Link.tsx → fakemui/navigation/Link
- Text.tsx → fakemui/atoms/Text
**Feedback (5 files):**
- ErrorBoundary.tsx → Keep as-is (React boundary)
- Separator.tsx → fakemui/data-display/Divider
- Skeleton.tsx → fakemui/feedback/Skeleton
- ScrollArea.tsx → Custom CSS implementation
- Tooltip.tsx → fakemui/data-display/Tooltip
- Spinner.tsx → fakemui/feedback/CircularProgress
- Progress.tsx → fakemui/feedback/LinearProgress
**Inputs (3 files):**
- Input.tsx → fakemui/inputs/Input
- TextArea.tsx → fakemui/inputs/Textarea
- Select.tsx → fakemui/inputs/Select
#### 2.2 UI Molecules (15+ files)
**Path:** `frontends/nextjs/src/components/ui/molecules/`
**Display:**
- Accordion.tsx → fakemui/surfaces/Accordion
- Alert.tsx → fakemui/feedback/Alert
- Card.tsx → fakemui/surfaces/Card
**Navigation:**
- Breadcrumb.tsx → fakemui/navigation/Breadcrumbs
- NavGroup.tsx → Custom with fakemui primitives
- NavItem.tsx → fakemui/navigation/ListItem
- NavLink.tsx → fakemui/navigation/Link
**Overlay:**
- Dialog.tsx → fakemui/utils/Dialog
- DropdownMenu.tsx → fakemui/navigation/Menu
- Popover.tsx → fakemui/utils/Popover
- Tooltip.tsx → fakemui/data-display/Tooltip
**Selection:**
- RadioGroup.tsx → fakemui/inputs/Radio with FormGroup
- Select.tsx → fakemui/inputs/Select
- ToggleGroup.tsx → fakemui/inputs/ToggleButtonGroup
**Tabs:**
- Tabs.tsx → fakemui/navigation/Tabs
- TabsContent.tsx → fakemui/navigation/Tab (panel)
- TabsList.tsx → fakemui/navigation/Tabs (container)
- TabsTrigger.tsx → fakemui/navigation/Tab (trigger)
#### 2.3 UI Organisms (15+ files)
**Path:** `frontends/nextjs/src/components/ui/organisms/`
**Dialogs:**
- AlertDialog → fakemui/utils/Dialog with Alert
- Command → Custom with fakemui primitives
- Sheet → fakemui/surfaces/Drawer
**Navigation:**
- Navigation.tsx → Custom with fakemui/surfaces/AppBar
- Sidebar → Custom with fakemui/surfaces/Drawer
- Pagination → fakemui/navigation/Pagination
**Data:**
- Form → Custom with fakemui/inputs
- Table → fakemui/data-display/Table
---
### 🎨 Category 3: Theme & Providers (19 files)
**Configuration files to be cleaned up after migration**
#### 3.1 Theme Files
**Path:** `frontends/nextjs/src/theme/`
**Files to Remove:**
- components.ts (MUI component overrides)
- light-theme.ts (MUI theme)
- dark-theme.ts (MUI theme)
- mui-theme.ts (theme creation)
- modes/light-theme.ts
- modes/dark-theme.ts
- modes/components.ts
**Files to Keep/Convert:**
- colors.ts → CSS variables
- fonts.ts → CSS font definitions
- typography.ts → CSS typography scale
- layout.ts → CSS layout tokens
- tokens.ts → CSS custom properties
#### 3.2 Provider Files
**Path:** `frontends/nextjs/src/app/providers/`
**Updates Needed:**
- providers-component.tsx
- Remove: MuiThemeProvider, CssBaseline
- Keep: ThemeContext for dark/light mode
- Add: CSS variables injection
---
### 🧪 Category 4: Test Files (3 files)
**Update alongside component migrations**
- NavLink.test.tsx
- NavItem.test.tsx
- NavGroup.test.tsx
---
## Detailed Phase Plan
### ✅ Phase 1: Foundation (Weeks 1-2)
**Status:** Partially Complete
**Completed:**
- ✅ Created fakemui icon system (Plus, Trash, Copy, Arrows, etc.)
- ✅ Migrated LuaBlocksEditor to fakemui
- ✅ Created fakemui barrel export
**Remaining:**
1. Choose icon library (Recommendation: Lucide React)
2. Create CSS reset (replace CssBaseline)
3. Create CSS variable theme system
4. Update providers to use new theme
**Deliverables:**
- [ ] Icon system decision + implementation
- [x] fakemui basic structure
- [ ] CSS reset file
- [ ] Theme CSS variables
- [ ] Updated provider component
---
### Phase 2: Atomic Components (Weeks 2-3)
**Priority:** High - Foundation for everything else
#### Week 2: Controls & Inputs
**Files:** 10 files
1. **Day 1-2: Button Components**
- atoms/controls/Button.tsx
- atoms/display/IconButton.tsx
- Reference: fakemui/inputs/Button.tsx
2. **Day 3-4: Form Inputs**
- atoms/inputs/Input.tsx
- atoms/inputs/TextArea.tsx
- atoms/inputs/Select.tsx
- Reference: fakemui/inputs/Input.tsx, Textarea.tsx, Select.tsx
3. **Day 5: Form Controls**
- atoms/controls/Checkbox.tsx
- atoms/controls/Radio.tsx
- atoms/controls/Switch.tsx
- Reference: fakemui/inputs/Checkbox.tsx, Radio.tsx, Switch.tsx
#### Week 3: Display & Feedback
**Files:** 13 files
1. **Day 1-2: Display Components**
- atoms/display/Avatar.tsx
- atoms/display/Badge.tsx
- atoms/display/Icon.tsx (critical - replaces all MUI icons)
- atoms/display/Label.tsx
- atoms/display/Link.tsx
- atoms/display/Text.tsx
2. **Day 3-4: Feedback Components**
- atoms/feedback/Skeleton.tsx
- atoms/feedback/Spinner.tsx
- atoms/feedback/Progress.tsx
- atoms/feedback/Tooltip.tsx
- atoms/feedback/Separator.tsx
3. **Day 5: Testing & Verification**
- Update all atom tests
- Visual regression testing
- Component demo page
**Deliverables:**
- [ ] 20 atom components migrated to fakemui
- [ ] Icon replacement complete
- [ ] All atom tests passing
- [ ] Component showcase page
---
### Phase 3: Basic UI Components (Weeks 3-4)
**Priority:** High - User-facing components
#### Week 3: Display & Navigation Molecules
**Files:** 10 files
1. **Day 1-2: Display Components**
- molecules/display/Card.tsx
- molecules/display/Alert.tsx
- molecules/display/Accordion.tsx
2. **Day 3-4: Navigation Components**
- molecules/navigation/Breadcrumb.tsx
- molecules/navigation/NavGroup.tsx
- molecules/navigation/NavItem.tsx
- molecules/navigation/NavLink.tsx
3. **Day 5: Tabs**
- molecules/tabs/Tabs.tsx
- molecules/tabs/TabsContent.tsx
- molecules/tabs/TabsList.tsx
- molecules/tabs/TabsTrigger.tsx
#### Week 4: Overlay & Selection
**Files:** 8 files
1. **Day 1-2: Overlay Components**
- molecules/overlay/Dialog.tsx
- molecules/overlay/DropdownMenu.tsx
- molecules/overlay/Popover.tsx
- molecules/overlay/Tooltip.tsx
2. **Day 3-4: Selection Components**
- molecules/selection/RadioGroup.tsx
- molecules/selection/Select.tsx
- molecules/selection/ToggleGroup.tsx
3. **Day 5: Testing**
- Update molecule tests
- Integration testing
**Deliverables:**
- [ ] 18 molecule components migrated
- [ ] Navigation components working
- [ ] All overlay components functional
- [ ] Tests passing
---
### Phase 4: Complex UI Components (Weeks 4-6)
**Priority:** Medium - Advanced features
#### Week 5: Navigation Organisms
**Files:** 12 files
1. **Day 1-3: Main Navigation**
- organisms/navigation/Navigation.tsx
- organisms/navigation/NavigationMenuItems.tsx
- organisms/navigation/NavigationResponsive.tsx
- organisms/navigation/Sidebar (6 files)
2. **Day 4-5: Pagination**
- organisms/navigation/pagination/* (5 files)
#### Week 6: Dialogs & Data
**Files:** 10 files
1. **Day 1-2: Dialog System**
- organisms/dialogs/AlertDialog
- organisms/dialogs/Command
- organisms/dialogs/Sheet
2. **Day 3-4: Data Components**
- organisms/data/Form.tsx
- organisms/data/Table.tsx
- data/table/* (Body, Header, EmptyState)
3. **Day 5: Testing & Polish**
- Full UI testing
- Responsive testing
**Deliverables:**
- [ ] 22 organism components migrated
- [ ] Navigation system complete
- [ ] Data components functional
- [ ] Full regression testing
---
### Phase 5: Lua Packages (Weeks 6-8)
**Priority:** Medium - Specialized editors
#### Week 7: Workflow Editor Package
**Package:** `@packages/workflow_editor`
**Files:** 8 files
1. **Day 1-2: Package Structure**
- Create package directory structure
- Create components.json
- Define metadata schema
2. **Day 3-4: Component Migration**
- Migrate WorkflowEditor components
- Replace MUI with fakemui
- Implement node system with JSON
3. **Day 5: Testing**
- Package integration tests
- Visual workflow testing
#### Week 8: GitHub Viewer Package
**Package:** `@packages/github_actions_viewer`
**Files:** 10 files
1. **Day 1-2: Package Structure**
- Create package structure
- Define data schemas
2. **Day 3-4: Component Migration**
- Migrate GitHub viewer components
- Implement custom table with fakemui
- Replace MUI icons
3. **Day 5: Integration**
- Package testing
- Integration with main app
**Deliverables:**
- [ ] workflow_editor package created
- [ ] github_actions_viewer package created
- [ ] Both packages tested and documented
---
### Phase 6: Final Cleanup (Week 8+)
**Priority:** High - Complete removal
#### Cleanup Tasks
1. **Theme System Removal**
- [ ] Remove theme/components.ts
- [ ] Remove theme/*-theme.ts files
- [ ] Remove MUI theme imports
2. **Provider Updates**
- [ ] Remove MuiThemeProvider from providers
- [ ] Remove CssBaseline import
- [ ] Update theme context to use CSS variables
3. **Package.json**
- [ ] Remove @mui/material
- [ ] Remove @mui/icons-material
- [ ] Remove @mui/system
- [ ] Verify no residual MUI dependencies
4. **Testing**
- [ ] Full application smoke test
- [ ] Visual regression testing
- [ ] Performance benchmarking
- [ ] Bundle size analysis
5. **Documentation**
- [ ] Update component documentation
- [ ] Create migration guide for future devs
- [ ] Document new icon system
- [ ] Document theme CSS variables
**Deliverables:**
- [ ] Zero MUI imports remaining
- [ ] All tests passing
- [ ] Bundle size reduced by 15-25%
- [ ] Documentation complete
---
## Icon System Migration
### Current System
- `atoms/display/Icon.tsx` dynamically imports from @mui/icons-material
- Used across entire codebase
- Supports 100+ icon names
### Replacement Options
| Option | Icons | Size | Pros | Cons |
|--------|-------|------|------|------|
| **Lucide React** | 1000+ | ~50KB | Tree-shakeable, modern, well-maintained | Learning curve |
| **Feather Icons** | 286 | ~30KB | Minimal, beautiful | Limited selection |
| **Custom SVG** | Custom | ~5KB | Full control, zero deps | Manual maintenance |
| **Radix Icons** | 300+ | ~25KB | Radix-aligned | Smaller set |
### Recommendation: Custom SVG System
- **Why:** Already started with fakemui/icons
- **Approach:** Add icons as needed from Heroicons/Lucide
- **Benefit:** Zero dependencies, full control, tiny bundle
- **Process:** Copy SVG paths when needed
---
## Migration Metrics
### Success Criteria
- ✅ Zero imports from @mui/material
- ✅ Zero imports from @mui/icons-material
- ✅ Zero imports from @mui/system
- ✅ All theme tests passing
- ✅ All UI component tests passing
- ✅ No visual regressions
- ✅ Bundle size reduced 15-25%
- ✅ Performance improvements
### Current Progress
- **Phase 1:** 40% complete (icon system started, LuaBlocksEditor migrated)
- **Phase 2:** 0% complete
- **Phase 3:** 0% complete
- **Phase 4:** 0% complete
- **Phase 5:** 0% complete
- **Phase 6:** 0% complete
**Overall:** ~7% complete (11/160 files migrated)
---
## Key Files Reference
### Fakemui Examples
- [Button.tsx](fakemui/fakemui/inputs/Button.tsx) - Reference implementation
- [Card.tsx](fakemui/fakemui/surfaces/Card.tsx) - Compound component pattern
- [Table.tsx](fakemui/fakemui/data-display/Table.tsx) - Complex data display
- [Alert.tsx](fakemui/fakemui/feedback/Alert.tsx) - Feedback component
- [index.ts](fakemui/index.ts) - Barrel export pattern
### Migration Examples
- [LuaBlocksEditor.tsx](frontends/nextjs/src/components/editors/lua/LuaBlocksEditor.tsx) - MUI → fakemui complete
- [BlockItem.tsx](frontends/nextjs/src/components/editors/lua/blocks/BlockItem.tsx) - Icon migration example
---
## Risk Assessment
### Low Risk
- Atomic component migration (well-isolated)
- Icon system replacement (centralized)
- CSS variable theme (progressive enhancement)
### Medium Risk
- Complex organism components (navigation, dialogs)
- Form components (validation, error handling)
- Table components (sorting, filtering)
### High Risk
- Workflow editor (complex state, D&D)
- Package creation (new architecture)
- Provider changes (affects entire app)
### Mitigation Strategies
1. **Parallel Development:** Keep MUI versions until replacement tested
2. **Feature Flags:** Toggle between old/new components
3. **Incremental Rollout:** Migrate page-by-page if needed
4. **Testing:** Comprehensive visual regression testing
5. **Rollback Plan:** Git branches for easy reversion
---
## Next Steps
### Immediate (This Week)
1. ✅ Complete MUI elimination plan
2. Choose icon replacement strategy
3. Create CSS reset file
4. Start Phase 2: Migrate Button component
### Short Term (Next 2 Weeks)
1. Complete Phase 2: All atomic components
2. Start Phase 3: Molecule components
3. Set up visual regression testing
### Medium Term (Weeks 3-6)
1. Complete Phase 3 & 4: All UI components
2. Begin Lua package creation
3. Theme cleanup
### Long Term (Weeks 6-8)
1. Complete Lua packages
2. Final MUI removal
3. Documentation and cleanup
---
## Questions & Decisions Needed
1. **Icon System:** Custom SVG vs Lucide React?
- Recommendation: Custom SVG (already started)
2. **CSS Framework:** Pure CSS vs Tailwind utilities?
- Current: Mix of SCSS modules and Tailwind
- Recommendation: Keep current approach
3. **Component Library:** Continue fakemui or use Radix?
- Current: Fakemui + Radix for complex interactions
- Recommendation: Use both (fakemui for display, Radix for behavior)
4. **Lua Packages:** Which components become packages?
- Confirmed: Workflow editor, GitHub viewer
- TBD: Nerd mode IDE, other specialized editors
---
**Last Updated:** 2025-12-30
**Next Review:** After Phase 2 completion

View File

@@ -1,4 +1,5 @@
// CSS Reset / Base styles
// CSS Reset - Replaces @mui/material CssBaseline
// Modern, minimal reset for consistent cross-browser rendering
*, *::before, *::after {
box-sizing: border-box;
@@ -8,14 +9,91 @@
html {
font-size: 16px;
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
body {
min-height: 100vh;
font-family: var(--font-family);
font-size: var(--font-size-md);
line-height: 1.5;
color: var(--color-text);
background-color: var(--color-bg);
}
// Typography defaults
h1, h2, h3, h4, h5, h6 {
font-weight: 600;
line-height: 1.2;
}
// Lists
ul, ol {
list-style: none;
}
// Links
a {
color: var(--color-primary);
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
// Media elements
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
height: auto;
}
// Form elements inherit fonts
input, button, textarea, select {
font: inherit;
color: inherit;
}
button {
cursor: pointer;
border: none;
background: none;
-webkit-tap-highlight-color: transparent;
}
// Tables
table {
border-collapse: collapse;
border-spacing: 0;
}
// Focus visible
:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
// Disabled state
:disabled {
cursor: not-allowed;
opacity: 0.6;
}
// Text wrapping
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
// Code blocks
code, kbd, samp, pre {
font-family: var(--font-mono);
}
// Hidden attribute
[hidden] {
display: none !important;
}

View File

@@ -1,9 +1,23 @@
---@class ArcadeLobby
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 = "Arcade Lobby installed", version = context.version }
end
---@return UninstallResult
function M.on_uninstall()
return { message = "Arcade Lobby removed" }
end

View File

@@ -1,5 +1,11 @@
---@class Matchmaking
local M = {}
---@class Party
---@field size? number
---@param party Party
---@return "squad"|"duo"|"solo"
function M.assign_bucket(party)
local size = party.size or 1
if size >= 4 then

View File

@@ -1,5 +1,11 @@
---@class Permissions
local M = {}
---@class User
---@field role? "public"|"admin"|"god"|"supergod"
---@param user User
---@return boolean
function M.can_create_tournament(user)
local role = user.role or "public"
local allowed = {

View File

@@ -1,5 +1,16 @@
---@class QueueMetrics
local M = {}
---@class Queue
---@field players? number
---@field waitSeconds? number
---@class QueueSummary
---@field totalPlayers number
---@field longestWaitSeconds number
---@param queues Queue[]
---@return QueueSummary
function M.summarize(queues)
local total_players = 0
local longest_wait = 0

View File

@@ -1,5 +1,10 @@
---@class Blueprint
local M = {}
---@generic T
---@param value? T
---@param default_value T
---@return T
local function fallback(value, default_value)
if value == nil or value == '' then
return default_value
@@ -7,6 +12,23 @@ local function fallback(value, default_value)
return value
end
---@class BlueprintInput
---@field project_name? string
---@field runtime? string
---@field description? string
---@class GeneratedFile
---@field path string
---@field content string
---@class BlueprintOutput
---@field name string
---@field runtime string
---@field description string
---@field files GeneratedFile[]
---@param input BlueprintInput
---@return BlueprintOutput
function M.build_blueprint(input)
local name = fallback(input.project_name, "starter-app")
local runtime = fallback(input.runtime, "web")

View File

@@ -1,9 +1,23 @@
---@class CodegenStudio
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 = "Codegen Studio installed", version = context.version }
end
---@return UninstallResult
function M.on_uninstall()
return { message = "Codegen Studio removed" }
end

View File

@@ -1,5 +1,11 @@
---@class CodegenPermissions
local M = {}
---@class User
---@field role? "public"|"user"|"admin"|"god"|"supergod"
---@param user User
---@return boolean
function M.can_generate(user)
local role = user.role or "public"
local allowed = {

View File

@@ -1,5 +1,24 @@
---@class ZipPlan
local M = {}
---@class BlueprintFile
---@field path string
---@field content? string
---@class Blueprint
---@field name? string
---@field files? BlueprintFile[]
---@class ZipEntry
---@field path string
---@field size number
---@class ZipPlanOutput
---@field name string
---@field entries ZipEntry[]
---@param blueprint Blueprint
---@return ZipPlanOutput
function M.prepare_zip(blueprint)
local entries = {}
local files = blueprint.files or {}

View File

@@ -1,9 +1,23 @@
---@class ForumForge
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 = "Forum Forge installed", version = context.version }
end
---@return UninstallResult
function M.on_uninstall()
return { message = "Forum Forge removed" }
end

View File

@@ -1,5 +1,7 @@
---@class Moderation
local M = {}
---@type string[]
local banned_terms = {
"spam",
"scam",
@@ -7,6 +9,15 @@ local banned_terms = {
"abuse"
}
---@class Post
---@field content? string
---@class FlagResult
---@field flagged boolean
---@field reasons string[]
---@param post Post
---@return FlagResult
function M.flag_post(post)
local content = post.content or ""
local reasons = {}

View File

@@ -1,10 +1,18 @@
---@class ForumPermissions
local M = {}
---@class User
---@field role? "public"|"user"|"admin"|"god"|"supergod"
---@param user User
---@return boolean
function M.can_post(user)
local role = user.role or "public"
return role ~= "public"
end
---@param user User
---@return boolean
function M.can_moderate(user)
local role = user.role or "public"
local allowed = {

View File

@@ -1,5 +1,14 @@
---@class ThreadRank
local M = {}
---@class Thread
---@field replyCount? number
---@field likeCount? number
---@field lastReplyAt? number
---@field createdAt? number
---@param thread Thread
---@return number
function M.rank_thread(thread)
local replies = thread.replyCount or 0
local likes = thread.likeCount or 0

View File

@@ -1,5 +1,18 @@
---@class Analytics
local Analytics = {}
---@class Event
---@field views? number
---@field likes? number
---@field comments? number
---@class Summary
---@field views number
---@field likes number
---@field comments number
---@param events? Event[]
---@return Summary
function Analytics.aggregate(events)
local summary = { views = 0, likes = 0, comments = 0 }
for _, event in ipairs(events or {}) do

View File

@@ -1,5 +1,13 @@
---@class FeedRank
local FeedRank = {}
---@class FeedItem
---@field age_minutes? number
---@field likes? number
---@field comments? number
---@param item FeedItem
---@return number
function FeedRank.score(item)
local freshness = item.age_minutes and (100 - item.age_minutes) or 50
local engagement = (item.likes or 0) * 2 + (item.comments or 0) * 3

View File

@@ -1,9 +1,23 @@
---@class SocialHub
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 = "Social Hub installed", version = context.version }
end
---@return UninstallResult
function M.on_uninstall()
return { message = "Social Hub removed" }
end

View File

@@ -1,5 +1,12 @@
---@class Moderation
local Moderation = {}
---@class ModerationResult
---@field flagged boolean
---@field reason? string
---@param content? string
---@return ModerationResult
function Moderation.flag(content)
local lowered = string.lower(content or "")
if string.find(lowered, "spam") then