mirror of
https://github.com/RSSNext/Folo.git
synced 2026-10-17 05:08:13 +08:00
feat(ai): support customize chat title
This commit is contained in:
@@ -1 +1 @@
|
||||
Before you start, you need to read and follow the rules in @claude.md
|
||||
Before you start, you need to read and follow the rules in @../claude.md
|
||||
|
||||
@@ -13,6 +13,8 @@ Follow (Folo) is a modern RSS aggregator and content management platform with mu
|
||||
- **`apps/desktop/`** - Electron desktop application
|
||||
- `layer/main/` - Electron main process
|
||||
- `layer/renderer/` - Vite + React renderer (primary web app)
|
||||
- `plugins/` - Custom Electron plugins
|
||||
- `resources/` - Application resources and assets
|
||||
- **`apps/mobile/`** - React Native mobile app for iOS and Android
|
||||
- `src/` - React Native source code
|
||||
- `native/` - Native module implementations
|
||||
@@ -41,6 +43,16 @@ Follow (Folo) is a modern RSS aggregator and content management platform with mu
|
||||
- `tailwindcss/` - Tailwind CSS configurations
|
||||
- TypeScript and build configurations
|
||||
- **`packages/readability/`** - Content readability parsing and extraction
|
||||
- **`packages/changelog-cli/`** - CLI tool for changelog generation
|
||||
|
||||
### Supporting Directories
|
||||
|
||||
- **`scripts/`** - Build and development scripts
|
||||
- **`plugins/`** - Custom development plugins
|
||||
- **`locales/`** - Internationalization files organized by feature
|
||||
- **`icons/`** - Icon assets (MingCute and custom icons)
|
||||
- **`.github/`** - GitHub workflows, issue templates, and CI/CD
|
||||
- **`patches/`** - Package patches for dependency modifications
|
||||
|
||||
## Essential Commands
|
||||
|
||||
@@ -108,45 +120,9 @@ pnpm run test
|
||||
- **Tailwind CSS** for styling across all platforms
|
||||
- Platform-specific Tailwind configs in each app
|
||||
- Shared Tailwind utilities in `packages/configs/tailwindcss/`
|
||||
- **UI Design Style**: Follow Vercel and Linear SaaS UI aesthetics - clean, modern, minimal design with subtle shadows, rounded corners, and excellent typography
|
||||
|
||||
#### UIKit Colors for Desktop Components
|
||||
|
||||
For desktop components (`apps/desktop/**/*`) and shared UI components (`packages/internal/components/**/*`), use Apple UIKit color system with Tailwind classes. **Important**: Always use the correct Tailwind prefix for each color category:
|
||||
|
||||
**System Colors**: `text-red`, `bg-red`, `border-red` (same for `orange`, `yellow`, `green`, `mint`, `teal`, `cyan`, `blue`, `indigo`, `purple`, `pink`, `brown`, `gray`)
|
||||
|
||||
**Fill Colors**:
|
||||
|
||||
- Background: `bg-fill`, `bg-fill-secondary`, `bg-fill-tertiary`, `bg-fill-quaternary`, `bg-fill-quinary`, `bg-fill-vibrant`, `bg-fill-vibrant-secondary`, `bg-fill-vibrant-tertiary`, `bg-fill-vibrant-quaternary`, `bg-fill-vibrant-quinary`
|
||||
- Border: `border-fill`, `border-fill-secondary`, etc.
|
||||
|
||||
**Text Colors**: `text-text`, `text-text-secondary`, `text-text-tertiary`, `text-text-quaternary`, `text-text-quinary`, `text-text-vibrant`, `text-text-vibrant-secondary`, `text-text-vibrant-tertiary`, `text-text-vibrant-quaternary`, `text-text-vibrant-quinary`
|
||||
|
||||
**Material Colors**: `bg-material-ultra-thick`, `bg-material-thick`, `bg-material-medium`, `bg-material-thin`, `bg-material-ultra-thin`, `bg-material-opaque`
|
||||
|
||||
**Control Colors**: `bg-control-enabled`, `bg-control-disabled`
|
||||
|
||||
**Interface Colors**: `bg-menu`, `bg-popover`, `bg-titlebar`, `bg-sidebar`, `bg-selection-focused`, `bg-selection-focused-fill`, `bg-selection-unfocused`, `bg-selection-unfocused-fill`, `bg-header-view`, `bg-tooltip`, `bg-under-window-background`
|
||||
|
||||
These colors automatically adapt to light/dark mode following Apple's design system. Remember to use the appropriate prefix (`text-`, `bg-`, `border-`) based on the CSS property you're styling.
|
||||
|
||||
#### Icons
|
||||
|
||||
For icon usage, prioritize the MingCute icon library with the `i-mgc-` prefix. Icons are available in the format `i-mgc-[icon-name]-[style]` where style can be `re` (regular), `fi` (filled), etc.
|
||||
|
||||
**Important**: Always try to find an appropriate icon with the `i-mgc-` prefix first. Only use the `i-mingcute-` prefix as a fallback when no suitable `i-mgc-` icon exists.
|
||||
|
||||
Examples:
|
||||
|
||||
- Preferred: `i-mgc-copy-cute-re`, `i-mgc-external-link-cute-re`
|
||||
- Fallback only: `i-mingcute-copy-line` (only if no mgc equivalent exists)
|
||||
|
||||
### Database
|
||||
|
||||
- **Drizzle ORM** with SQLite for local data storage
|
||||
- Platform-specific database implementations in `packages/internal/database/`
|
||||
- Migration system with versioned SQL files
|
||||
For desktop, see @apps/desktop/CLAUDE.md
|
||||
For mobile, see @apps/mobile/CLAUDE.md
|
||||
|
||||
### Internationalization
|
||||
|
||||
@@ -172,25 +148,6 @@ Example:
|
||||
}
|
||||
```
|
||||
|
||||
## Development Setup Notes
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Enable Corepack: `corepack enable && corepack prepare`
|
||||
- For mobile development: macOS with Xcode required
|
||||
|
||||
### Environment Setup
|
||||
|
||||
- Copy `.env.example` to `.env` in relevant app directories
|
||||
- For development, use `VITE_API_URL=https://api.follow.is`
|
||||
- Browser development uses `https://app.folo.is/__debug_proxy` for API access
|
||||
|
||||
### Mobile Development
|
||||
|
||||
- iOS development requires Xcode and iOS simulator
|
||||
- Native module development in `apps/mobile/native/`
|
||||
- Use `pnpm expo prebuild --clean` for clean builds
|
||||
|
||||
## Code Organization Patterns
|
||||
|
||||
### Import Conventions
|
||||
@@ -204,26 +161,13 @@ Example:
|
||||
- Shared UI components in `packages/internal/components/`
|
||||
- Platform-specific components in respective app directories
|
||||
- Use TypeScript interfaces for component props
|
||||
- **Detailed guidelines**: See @packages/internal/CLAUDE.md for component development patterns
|
||||
|
||||
### Error Handling
|
||||
|
||||
- Custom error parsing utilities in `packages/internal/utils/`
|
||||
- Sentry integration for error reporting across platforms
|
||||
|
||||
## Build and Deployment
|
||||
|
||||
### Build Outputs
|
||||
|
||||
- Desktop: `apps/desktop/out/` for packaged applications
|
||||
- Web: `apps/desktop/out/web/` for static web assets
|
||||
- Mobile: Platform-specific build directories
|
||||
|
||||
### Turbo Configuration
|
||||
|
||||
- Build orchestration via `turbo.json`
|
||||
- Dependency-aware builds with output caching
|
||||
- Parallel execution for independent packages
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
- Vitest for unit testing across packages
|
||||
@@ -354,7 +298,7 @@ Claude Code has access to specialized agents for different development tasks. **
|
||||
|
||||
## Agent Usage Notes
|
||||
|
||||
- **Primary web app is at `@apps/desktop/layer/renderer`** - A Vite + React SPA, not full Electron features
|
||||
- **Primary web app is at `apps/desktop/layer/renderer`** - A Vite + React SPA, not full Electron features
|
||||
- **SSR app is minimal** - Only for external sharing pages, not main development focus
|
||||
- **Mobile app** - React Native with Expo for iOS/Android
|
||||
- **Always use Task tool with appropriate agent** - Don't implement complex features directly, delegate to specialized agents
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
# Desktop App Development Guide
|
||||
|
||||
This file provides specific guidance for developing the Electron desktop application.
|
||||
|
||||
## Architecture
|
||||
|
||||
- **Main Process** (`layer/main/`) - Electron main process handling system integration
|
||||
- **Renderer Process** (`layer/renderer/`) - Vite + React renderer (primary web app)
|
||||
|
||||
The renderer is the **primary web application** - a Vite + React SPA that can run both in Electron and as a standalone web app.
|
||||
|
||||
## UI Style
|
||||
|
||||
- **UI Design Style**: Follow Vercel and Linear SaaS UI aesthetics - clean, modern, minimal design with subtle shadows, rounded corners, and excellent typography
|
||||
- **Tailwind CSS** for styling across all platforms
|
||||
- Platform-specific Tailwind configs in each app
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
# Recommended: Browser development (faster)
|
||||
pnpm run dev:web
|
||||
|
||||
# Full Electron development
|
||||
pnpm run dev:electron
|
||||
|
||||
# Build web version
|
||||
pnpm run build:web
|
||||
```
|
||||
|
||||
## UIKit Colors for Desktop Components
|
||||
|
||||
For desktop components (`apps/desktop/**/*`) and shared UI components (`packages/internal/components/**/*`), use Apple UIKit color system with Tailwind classes. **Important**: Always use the correct Tailwind prefix for each color category:
|
||||
|
||||
**System Colors**: `text-red`, `bg-red`, `border-red` (same for `orange`, `yellow`, `green`, `mint`, `teal`, `cyan`, `blue`, `indigo`, `purple`, `pink`, `brown`, `gray`)
|
||||
|
||||
**Fill Colors**:
|
||||
|
||||
- Background: `bg-fill`, `bg-fill-secondary`, `bg-fill-tertiary`, `bg-fill-quaternary`, `bg-fill-quinary`, `bg-fill-vibrant`, `bg-fill-vibrant-secondary`, `bg-fill-vibrant-tertiary`, `bg-fill-vibrant-quaternary`, `bg-fill-vibrant-quinary`
|
||||
- Border: `border-fill`, `border-fill-secondary`, etc.
|
||||
|
||||
**Text Colors**: `text-text`, `text-text-secondary`, `text-text-tertiary`, `text-text-quaternary`, `text-text-quinary`, `text-text-vibrant`, `text-text-vibrant-secondary`, `text-text-vibrant-tertiary`, `text-text-vibrant-quaternary`, `text-text-vibrant-quinary`
|
||||
|
||||
**Material Colors**: `bg-material-ultra-thick`, `bg-material-thick`, `bg-material-medium`, `bg-material-thin`, `bg-material-ultra-thin`, `bg-material-opaque`
|
||||
|
||||
**Control Colors**: `bg-control-enabled`, `bg-control-disabled`
|
||||
|
||||
**Interface Colors**: `bg-menu`, `bg-popover`, `bg-titlebar`, `bg-sidebar`, `bg-selection-focused`, `bg-selection-focused-fill`, `bg-selection-unfocused`, `bg-selection-unfocused-fill`, `bg-header-view`, `bg-tooltip`, `bg-under-window-background`
|
||||
|
||||
These colors automatically adapt to light/dark mode following Apple's design system. Remember to use the appropriate prefix (`text-`, `bg-`, `border-`) based on the CSS property you're styling.
|
||||
|
||||
## Icons
|
||||
|
||||
For icon usage, prioritize the MingCute icon library with the `i-mgc-` prefix. Icons are available in the format `i-mgc-[icon-name]-[style]` where style can be `re` (regular), `fi` (filled), etc.
|
||||
|
||||
**Important**: Always try to find an appropriate icon with the `i-mgc-` prefix first. Only use the `i-mingcute-` prefix as a fallback when no suitable `i-mgc-` icon exists.
|
||||
|
||||
Examples:
|
||||
|
||||
- Preferred: `i-mgc-copy-cute-re`, `i-mgc-external-link-cute-re`
|
||||
- Fallback only: `i-mingcute-copy-line` (only if no mgc equivalent exists)
|
||||
|
||||
## Animation with Framer Motion
|
||||
|
||||
- **LazyMotion Integration**: Project uses Framer Motion with LazyMotion for optimized bundle size
|
||||
- **Usage Rule**: Always use `m.` instead of `motion.` when creating animated components
|
||||
- **Import**: `import { m } from 'motion/react'`
|
||||
- **Examples**: `m.div`, `m.button`, `m.span` (not `motion.div`, `motion.button`, etc.)
|
||||
- **Benefits**: Reduces bundle size while maintaining all Framer Motion functionality
|
||||
|
||||
**Animation Presets**:
|
||||
|
||||
- **Prefer Spring Presets**: Use predefined spring animations from `@follow/components/constants/spring.js`
|
||||
- **Available Presets Constants**: `Spring.presets.smooth`, `Spring.presets.snappy`, `Spring.presets.bouncy` (extracted from Apple's spring parameters)
|
||||
- **Usage Example**: `transition={Spring.presets.smooth}` or `transition={Spring.snappy(0.3, 0.1)}`
|
||||
- **Customization**: All presets accept optional `duration` and `extraBounce` parameters
|
||||
|
||||
## Reusable UI Components
|
||||
|
||||
When building UI components, follow this hierarchy:
|
||||
|
||||
1. **Check Existing Components First**: Look in `apps/desktop/layer/renderer/src/modules/renderer/components` for app-specific components
|
||||
2. **Create Reusable Components**: If the component doesn't exist and is **generic/reusable** (not tied to specific app business logic), create it in `packages/internal/components`
|
||||
|
||||
### Guidelines for Reusable Components (`packages/internal/components`)
|
||||
|
||||
- **Purpose**: Components should be generic and reusable across different apps/contexts
|
||||
- **No Business Logic**: Avoid coupling with specific app business logic, APIs, or state management
|
||||
- **Follow All Style Rules**: Must adhere to UIKit colors, MingCute icons (`i-mgc-` prefix), and Framer Motion (`m.` prefix) guidelines
|
||||
- **Export Pattern**: Export components from appropriate index files for clean imports
|
||||
- **TypeScript**: Use proper TypeScript interfaces for props and maintain type safety
|
||||
|
||||
**Example Structure**:
|
||||
|
||||
```
|
||||
packages/internal/components/
|
||||
├── ui/ # Basic UI primitives (Button, Input, Modal)
|
||||
├── layout/ # Layout components (Grid, Stack, Container)
|
||||
├── feedback/ # User feedback (Toast, Loading, Alert)
|
||||
└── index.ts # Main exports
|
||||
```
|
||||
|
||||
**Import Examples**:
|
||||
|
||||
```tsx
|
||||
// Correct - from shared components
|
||||
import { Button, Modal } from "@follow/components"
|
||||
|
||||
// App-specific components stay in name/components
|
||||
import { FeedList } from "~/modules/name/components"
|
||||
```
|
||||
|
||||
## Build Outputs
|
||||
|
||||
- Desktop: `apps/desktop/out/` for packaged applications
|
||||
- Web: `apps/desktop/out/web/` for static web assets
|
||||
@@ -7,6 +7,7 @@ import { useChatActions, useCurrentTitle } from "~/modules/ai-chat/store/hooks"
|
||||
import { useSettingModal } from "~/modules/settings/modal/use-setting-modal-hack"
|
||||
|
||||
import { ChatMoreDropdown } from "./ChatMoreDropdown"
|
||||
import { EditableTitle } from "./EditableTitle"
|
||||
|
||||
export const ChatHeader = () => {
|
||||
const currentTitle = useCurrentTitle()
|
||||
@@ -32,6 +33,13 @@ export const ChatHeader = () => {
|
||||
})
|
||||
}, [chatActions, currentTitle, ask, t])
|
||||
|
||||
const handleTitleSave = useCallback(
|
||||
async (newTitle: string) => {
|
||||
await chatActions.editChatTitle(newTitle)
|
||||
},
|
||||
[chatActions],
|
||||
)
|
||||
|
||||
const maskImage = `linear-gradient(to bottom, black 0%, black 75%, transparent 100%)`
|
||||
return (
|
||||
<div className="absolute inset-x-0 top-0 z-[1] h-12">
|
||||
@@ -46,13 +54,11 @@ export const ChatHeader = () => {
|
||||
<div className="relative z-10 flex h-full items-center justify-between px-4">
|
||||
{/* Left side - Title */}
|
||||
<div className="mr-2 min-w-0 flex-1">
|
||||
{currentTitle && (
|
||||
<h1 key={currentTitle} className="text-text truncate font-bold">
|
||||
<span className="animate-mask-left-to-right [--animation-duration:1s]">
|
||||
{currentTitle}
|
||||
</span>
|
||||
</h1>
|
||||
)}
|
||||
<EditableTitle
|
||||
title={currentTitle}
|
||||
onSave={handleTitleSave}
|
||||
placeholder="Untitled Chat"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Right side - Actions */}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import { Input } from "@follow/components/ui/input/Input.js"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
import { useCallback, useEffect, useState } from "react"
|
||||
|
||||
interface EditableTitleProps {
|
||||
title?: string
|
||||
onSave: (newTitle: string) => Promise<void>
|
||||
className?: string
|
||||
placeholder?: string
|
||||
maxLength?: number
|
||||
}
|
||||
|
||||
export const EditableTitle = ({
|
||||
title = "",
|
||||
onSave,
|
||||
className,
|
||||
placeholder = "Untitled Chat",
|
||||
maxLength = 100,
|
||||
}: EditableTitleProps) => {
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
const [editValue, setEditValue] = useState("")
|
||||
const [isSaving, setIsSaving] = useState(false)
|
||||
const [originalTitle, setOriginalTitle] = useState("")
|
||||
const [saveSuccess, setSaveSuccess] = useState(false)
|
||||
|
||||
const displayTitle = title || placeholder
|
||||
|
||||
const handleStartEdit = useCallback(() => {
|
||||
setOriginalTitle(title)
|
||||
setEditValue(title)
|
||||
setIsEditing(true)
|
||||
}, [title])
|
||||
|
||||
const handleSave = useCallback(async () => {
|
||||
if (isSaving) return
|
||||
|
||||
const trimmedValue = editValue.trim()
|
||||
|
||||
// If no changes, just exit edit mode
|
||||
if (trimmedValue === originalTitle) {
|
||||
setIsEditing(false)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSaving(true)
|
||||
await onSave(trimmedValue)
|
||||
|
||||
// Show success feedback briefly
|
||||
setSaveSuccess(true)
|
||||
setTimeout(() => setSaveSuccess(false), 2000)
|
||||
|
||||
setIsEditing(false)
|
||||
} catch (error) {
|
||||
console.error("Failed to save title:", error)
|
||||
// Keep in edit mode on error to allow retry
|
||||
} finally {
|
||||
setIsSaving(false)
|
||||
}
|
||||
}, [editValue, originalTitle, onSave, isSaving])
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
setEditValue(originalTitle)
|
||||
setIsEditing(false)
|
||||
setIsSaving(false)
|
||||
}, [originalTitle])
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(event: React.KeyboardEvent) => {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault()
|
||||
handleSave()
|
||||
} else if (event.key === "Escape") {
|
||||
event.preventDefault()
|
||||
handleCancel()
|
||||
}
|
||||
},
|
||||
[handleSave, handleCancel],
|
||||
)
|
||||
|
||||
const handleBlur = useCallback(() => {
|
||||
if (!isSaving) {
|
||||
handleSave()
|
||||
}
|
||||
}, [handleSave, isSaving])
|
||||
|
||||
// Auto-focus input when entering edit mode
|
||||
useEffect(() => {
|
||||
if (isEditing) {
|
||||
const input = document.activeElement as HTMLInputElement
|
||||
if (input?.select) {
|
||||
input.select()
|
||||
}
|
||||
}
|
||||
}, [isEditing])
|
||||
|
||||
if (isEditing) {
|
||||
return (
|
||||
<div className="relative min-w-0 flex-1">
|
||||
<Input
|
||||
ref={(ref) => ref?.focus()}
|
||||
value={editValue}
|
||||
onChange={(e) => setEditValue(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={handleBlur}
|
||||
maxLength={maxLength}
|
||||
disabled={isSaving}
|
||||
className={cn(
|
||||
"text-text border-accent/50 focus:border-accent bg-transparent font-bold",
|
||||
"h-auto p-2 text-base leading-none",
|
||||
isSaving && "cursor-not-allowed opacity-60",
|
||||
className,
|
||||
)}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
{isSaving && (
|
||||
<div className="animate-in fade-in zoom-in-75 absolute right-2 top-1/2 -translate-y-1/2 duration-200">
|
||||
<i className="i-mgc-loading-3-cute-re text-text-secondary size-4 animate-spin" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn("group flex min-w-0 flex-1 cursor-pointer items-center gap-2", className)}
|
||||
onClick={handleStartEdit}
|
||||
>
|
||||
<h1 className="text-text truncate font-bold transition-opacity group-hover:opacity-80">
|
||||
<span className="animate-mask-left-to-right [--animation-duration:1s]">{displayTitle}</span>
|
||||
</h1>
|
||||
{saveSuccess && (
|
||||
<i className="i-mgc-check-cute-re text-green animate-in fade-in zoom-in-50 size-4 transition-all duration-300" />
|
||||
)}
|
||||
<i className="i-mgc-edit-cute-re text-text-secondary group-hover:animate-in group-hover:fade-in group-hover:zoom-in-75 size-4 opacity-0 transition-all duration-150 hover:opacity-100 group-hover:opacity-60" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -92,6 +92,35 @@ export class ChatSliceActions {
|
||||
return this.get().chatId
|
||||
}
|
||||
|
||||
// Edit chat title
|
||||
editChatTitle = async (newTitle: string) => {
|
||||
const currentChatId = this.getCurrentChatId()
|
||||
if (!currentChatId) {
|
||||
throw new Error("No active chat to edit title for")
|
||||
}
|
||||
|
||||
const trimmedTitle = newTitle.trim()
|
||||
const currentTitle = this.getCurrentTitle()
|
||||
|
||||
// If no changes, return early
|
||||
if (trimmedTitle === currentTitle) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// Optimistic update
|
||||
this.setCurrentTitle(trimmedTitle)
|
||||
|
||||
// Persist to database
|
||||
await AIPersistService.updateSessionTitle(currentChatId, trimmedTitle)
|
||||
} catch (error) {
|
||||
// Rollback on error
|
||||
this.setCurrentTitle(currentTitle)
|
||||
console.error("Failed to update chat title:", error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Core chat actions using AI SDK AbstractChat methods
|
||||
sendMessage = async (message: string | BizUIMessage) => {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Mobile App Development Guide
|
||||
|
||||
This file provides specific guidance for developing the React Native mobile application.
|
||||
|
||||
## Architecture
|
||||
|
||||
- **React Native** with Expo for cross-platform mobile development
|
||||
- **Native modules** in `native/` for platform-specific functionality
|
||||
- **Web views** in `web-app/` for HTML rendering within the mobile app
|
||||
- **Shared components** with the desktop app through `packages/internal/`
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
# Start Expo development server
|
||||
pnpm run dev
|
||||
|
||||
# Start with specific platform
|
||||
pnpm run ios
|
||||
pnpm run android
|
||||
|
||||
# Clean rebuild (useful for native module changes)
|
||||
pnpm expo prebuild --clean
|
||||
```
|
||||
|
||||
## UIKit Colors for React Native Components
|
||||
|
||||
For react native components (`apps/mobile/**/*`), use Apple UIKit color system with Tailwind classes. **Important**: Always use the correct Tailwind prefix for each color category:
|
||||
|
||||
### System Colors
|
||||
|
||||
- Background: `system-background`, `secondary-system-background`, `tertiary-system-background`
|
||||
- Grouped: `system-grouped-background`, `secondary-system-grouped-background`, `tertiary-system-grouped-background`
|
||||
- Labels: `label`, `secondary-label`, `tertiary-label`, `quaternary-label`
|
||||
- Fills: `system-fill`, `secondary-system-fill`, `tertiary-system-fill`, `quaternary-system-fill`
|
||||
- Separators: `separator`, `opaque-separator`, `non-opaque-separator`
|
||||
|
||||
### Semantic Colors
|
||||
|
||||
- Core: `red`, `orange`, `yellow`, `green`, `mint`, `teal`, `cyan`, `blue`, `indigo`, `purple`, `pink`, `brown`
|
||||
- Grays: `gray`, `gray2`, `gray3`, `gray4`, `gray5`, `gray6`
|
||||
- Interactive: `link`, `placeholder-text`
|
||||
|
||||
These colors automatically adapt to light/dark mode following Apple's design system. Remember to use the appropriate prefix (`text-`, `bg-`, `border-`) based on the CSS property you're styling.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Internal Packages Development Guide
|
||||
|
||||
This file provides specific guidance for developing shared packages used across all platforms.
|
||||
|
||||
## Overview
|
||||
|
||||
The `packages/internal/` directory contains core shared packages that provide common functionality across desktop, mobile, and SSR applications.
|
||||
|
||||
## Package Structure
|
||||
|
||||
- `atoms/` - Jotai atomic state definitions
|
||||
- `components/` - Shared UI components
|
||||
- `constants/` - Application constants
|
||||
- `database/` - Drizzle ORM database layer
|
||||
- `hooks/` - Shared React hooks
|
||||
- `models/` - Data models and schemas
|
||||
- `shared/` - Cross-platform shared utilities
|
||||
- `store/` - Zustand stores
|
||||
- `types/` - TypeScript type definitions
|
||||
- `utils/` - Utility functions and helpers
|
||||
- `tracker/` - Analytics and tracking
|
||||
- `logger/` - Logging utilities
|
||||
- `legal/` - Legal and compliance utilities
|
||||
|
||||
## State Management
|
||||
|
||||
- **Jotai** for atomic state management across all platforms
|
||||
- **Zustand** for complex state stores (in `packages/internal/store/`)
|
||||
- **React Query** for server state management
|
||||
|
||||
## Database
|
||||
|
||||
- **Drizzle ORM** with SQLite for local data storage
|
||||
- Platform-specific database implementations in `packages/internal/database/`
|
||||
- Migration system with versioned SQL files
|
||||
|
||||
## Component Development Guidelines
|
||||
|
||||
- Shared UI components in `packages/internal/components/`
|
||||
- Platform-specific components in respective app directories
|
||||
- Use TypeScript interfaces for component props
|
||||
- Follow cross-platform compatibility patterns
|
||||
Reference in New Issue
Block a user