@even-toolkit/create-even-app 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/index.js +159 -0
  2. package/package.json +28 -0
  3. package/templates/chat/README.md +27 -0
  4. package/templates/chat/index.html +12 -0
  5. package/templates/chat/package.json +34 -0
  6. package/templates/chat/src/App.tsx +61 -0
  7. package/templates/chat/src/app.css +54 -0
  8. package/templates/chat/src/contexts/ChatContext.tsx +99 -0
  9. package/templates/chat/src/glass/AppGlasses.tsx +70 -0
  10. package/templates/chat/src/glass/screens/home.ts +24 -0
  11. package/templates/chat/src/glass/selectors.ts +9 -0
  12. package/templates/chat/src/glass/shared.ts +8 -0
  13. package/templates/chat/src/glass/splash.ts +25 -0
  14. package/templates/chat/src/main.tsx +13 -0
  15. package/templates/chat/src/screens/ChatScreen.tsx +69 -0
  16. package/templates/chat/src/screens/Settings.tsx +88 -0
  17. package/templates/chat/src/types.ts +13 -0
  18. package/templates/chat/src/vite-env.d.ts +1 -0
  19. package/templates/chat/template.json +7 -0
  20. package/templates/chat/tsconfig.json +20 -0
  21. package/templates/chat/tsconfig.node.json +13 -0
  22. package/templates/chat/vite.config.ts +12 -0
  23. package/templates/dashboard/README.md +17 -0
  24. package/templates/dashboard/index.html +12 -0
  25. package/templates/dashboard/package.json +34 -0
  26. package/templates/dashboard/src/App.tsx +27 -0
  27. package/templates/dashboard/src/app.css +54 -0
  28. package/templates/dashboard/src/glass/AppGlasses.tsx +53 -0
  29. package/templates/dashboard/src/glass/screens/home.ts +23 -0
  30. package/templates/dashboard/src/glass/selectors.ts +9 -0
  31. package/templates/dashboard/src/glass/shared.ts +8 -0
  32. package/templates/dashboard/src/glass/splash.ts +22 -0
  33. package/templates/dashboard/src/main.tsx +13 -0
  34. package/templates/dashboard/src/screens/ChartsScreen.tsx +99 -0
  35. package/templates/dashboard/src/screens/OverviewScreen.tsx +102 -0
  36. package/templates/dashboard/src/screens/SettingsScreen.tsx +60 -0
  37. package/templates/dashboard/src/vite-env.d.ts +1 -0
  38. package/templates/dashboard/template.json +7 -0
  39. package/templates/dashboard/tsconfig.json +20 -0
  40. package/templates/dashboard/tsconfig.node.json +13 -0
  41. package/templates/dashboard/vite.config.ts +12 -0
  42. package/templates/media/README.md +27 -0
  43. package/templates/media/index.html +12 -0
  44. package/templates/media/package.json +34 -0
  45. package/templates/media/src/App.tsx +24 -0
  46. package/templates/media/src/app.css +54 -0
  47. package/templates/media/src/contexts/MediaContext.tsx +108 -0
  48. package/templates/media/src/glass/AppGlasses.tsx +59 -0
  49. package/templates/media/src/glass/screens/home.ts +24 -0
  50. package/templates/media/src/glass/selectors.ts +9 -0
  51. package/templates/media/src/glass/shared.ts +8 -0
  52. package/templates/media/src/glass/splash.ts +25 -0
  53. package/templates/media/src/layouts/shell.tsx +39 -0
  54. package/templates/media/src/main.tsx +13 -0
  55. package/templates/media/src/screens/AudioScreen.tsx +78 -0
  56. package/templates/media/src/screens/GalleryScreen.tsx +98 -0
  57. package/templates/media/src/screens/Settings.tsx +86 -0
  58. package/templates/media/src/screens/UploadScreen.tsx +95 -0
  59. package/templates/media/src/types.ts +29 -0
  60. package/templates/media/src/vite-env.d.ts +1 -0
  61. package/templates/media/template.json +7 -0
  62. package/templates/media/tsconfig.json +20 -0
  63. package/templates/media/tsconfig.node.json +13 -0
  64. package/templates/media/vite.config.ts +12 -0
  65. package/templates/minimal/README.md +27 -0
  66. package/templates/minimal/index.html +12 -0
  67. package/templates/minimal/package.json +34 -0
  68. package/templates/minimal/src/App.tsx +50 -0
  69. package/templates/minimal/src/app.css +54 -0
  70. package/templates/minimal/src/glass/AppGlasses.tsx +54 -0
  71. package/templates/minimal/src/glass/screens/home.ts +24 -0
  72. package/templates/minimal/src/glass/selectors.ts +9 -0
  73. package/templates/minimal/src/glass/shared.ts +8 -0
  74. package/templates/minimal/src/glass/splash.ts +25 -0
  75. package/templates/minimal/src/main.tsx +13 -0
  76. package/templates/minimal/src/vite-env.d.ts +1 -0
  77. package/templates/minimal/template.json +7 -0
  78. package/templates/minimal/tsconfig.json +20 -0
  79. package/templates/minimal/tsconfig.node.json +13 -0
  80. package/templates/minimal/vite.config.ts +12 -0
  81. package/templates/notes/README.md +27 -0
  82. package/templates/notes/index.html +12 -0
  83. package/templates/notes/package.json +34 -0
  84. package/templates/notes/src/App.tsx +25 -0
  85. package/templates/notes/src/app.css +54 -0
  86. package/templates/notes/src/contexts/NotesContext.tsx +140 -0
  87. package/templates/notes/src/glass/AppGlasses.tsx +58 -0
  88. package/templates/notes/src/glass/screens/home.ts +24 -0
  89. package/templates/notes/src/glass/selectors.ts +9 -0
  90. package/templates/notes/src/glass/shared.ts +8 -0
  91. package/templates/notes/src/glass/splash.ts +24 -0
  92. package/templates/notes/src/layouts/shell.tsx +36 -0
  93. package/templates/notes/src/main.tsx +13 -0
  94. package/templates/notes/src/screens/NoteDetail.tsx +104 -0
  95. package/templates/notes/src/screens/NoteForm.tsx +84 -0
  96. package/templates/notes/src/screens/NoteList.tsx +108 -0
  97. package/templates/notes/src/screens/Settings.tsx +88 -0
  98. package/templates/notes/src/types.ts +14 -0
  99. package/templates/notes/src/vite-env.d.ts +1 -0
  100. package/templates/notes/template.json +7 -0
  101. package/templates/notes/tsconfig.json +20 -0
  102. package/templates/notes/tsconfig.node.json +13 -0
  103. package/templates/notes/vite.config.ts +12 -0
  104. package/templates/tracker/README.md +27 -0
  105. package/templates/tracker/index.html +12 -0
  106. package/templates/tracker/package.json +34 -0
  107. package/templates/tracker/src/App.tsx +24 -0
  108. package/templates/tracker/src/app.css +54 -0
  109. package/templates/tracker/src/contexts/TrackerContext.tsx +193 -0
  110. package/templates/tracker/src/glass/AppGlasses.tsx +64 -0
  111. package/templates/tracker/src/glass/screens/home.ts +24 -0
  112. package/templates/tracker/src/glass/selectors.ts +9 -0
  113. package/templates/tracker/src/glass/shared.ts +8 -0
  114. package/templates/tracker/src/glass/splash.ts +24 -0
  115. package/templates/tracker/src/layouts/shell.tsx +37 -0
  116. package/templates/tracker/src/main.tsx +13 -0
  117. package/templates/tracker/src/screens/HistoryScreen.tsx +106 -0
  118. package/templates/tracker/src/screens/NewEntryScreen.tsx +135 -0
  119. package/templates/tracker/src/screens/Settings.tsx +135 -0
  120. package/templates/tracker/src/screens/TodayScreen.tsx +147 -0
  121. package/templates/tracker/src/types.ts +34 -0
  122. package/templates/tracker/src/vite-env.d.ts +1 -0
  123. package/templates/tracker/template.json +7 -0
  124. package/templates/tracker/tsconfig.json +20 -0
  125. package/templates/tracker/tsconfig.node.json +13 -0
  126. package/templates/tracker/vite.config.ts +12 -0
@@ -0,0 +1,69 @@
1
+ import { useState } from 'react'
2
+ import { ChatContainer, ChatInput, ChatThinking, VoiceInput } from 'even-toolkit/web'
3
+ import type { ChatMessage } from 'even-toolkit/web'
4
+ import { useChat } from '../contexts/ChatContext'
5
+
6
+ export function ChatScreen() {
7
+ const { messages, isLoading, settings, sendMessage } = useChat()
8
+ const [inputValue, setInputValue] = useState('')
9
+
10
+ const handleSend = () => {
11
+ const trimmed = inputValue.trim()
12
+ if (!trimmed) return
13
+ sendMessage(trimmed)
14
+ setInputValue('')
15
+ }
16
+
17
+ const handleVoiceTranscript = (text: string) => {
18
+ if (text.trim()) {
19
+ sendMessage(text.trim())
20
+ }
21
+ }
22
+
23
+ // Map app messages to toolkit ChatMessage format
24
+ const chatMessages: ChatMessage[] = messages.map((msg) => ({
25
+ id: msg.id,
26
+ role: msg.role,
27
+ content: msg.content,
28
+ timestamp: msg.timestamp,
29
+ }))
30
+
31
+ // Add thinking indicator when loading
32
+ if (isLoading) {
33
+ chatMessages.push({
34
+ id: 'thinking',
35
+ role: 'assistant',
36
+ content: '',
37
+ thinking: 'Composing a response...',
38
+ })
39
+ }
40
+
41
+ return (
42
+ <div className="h-full flex flex-col">
43
+ <ChatContainer
44
+ messages={chatMessages}
45
+ className="flex-1 min-h-0"
46
+ input={
47
+ <div className="flex items-end gap-2 p-3 bg-bg border-t border-border/30">
48
+ {settings.voiceEnabled && (
49
+ <VoiceInput
50
+ onTranscript={handleVoiceTranscript}
51
+ className="shrink-0"
52
+ />
53
+ )}
54
+ <div className="flex-1 flex items-end gap-2">
55
+ <ChatInput
56
+ value={inputValue}
57
+ onChange={setInputValue}
58
+ onSend={handleSend}
59
+ placeholder="Ask anything..."
60
+ disabled={isLoading}
61
+ className="flex-1 !p-0 !bg-transparent"
62
+ />
63
+ </div>
64
+ </div>
65
+ }
66
+ />
67
+ </div>
68
+ )
69
+ }
@@ -0,0 +1,88 @@
1
+ import { SettingsGroup, ListItem, Toggle, Select, Input } from 'even-toolkit/web'
2
+ import { useChat } from '../contexts/ChatContext'
3
+
4
+ const PROVIDER_OPTIONS = [
5
+ { value: 'openai', label: 'OpenAI' },
6
+ { value: 'anthropic', label: 'Anthropic' },
7
+ { value: 'local', label: 'Local Model' },
8
+ ]
9
+
10
+ export function Settings() {
11
+ const { settings, setSettings, clearMessages } = useChat()
12
+
13
+ return (
14
+ <main className="px-3 pt-4 pb-8 space-y-6">
15
+ {/* AI Provider */}
16
+ <SettingsGroup label="AI Provider">
17
+ <ListItem
18
+ title="Provider"
19
+ trailing={
20
+ <Select
21
+ options={PROVIDER_OPTIONS}
22
+ value={settings.provider}
23
+ onValueChange={(v) => setSettings({ ...settings, provider: v as 'openai' | 'anthropic' | 'local' })}
24
+ />
25
+ }
26
+ />
27
+ <div className="px-4 py-3 space-y-1.5">
28
+ <span className="text-[13px] tracking-[-0.13px] text-text-dim">API Key</span>
29
+ <Input
30
+ value={settings.apiKey}
31
+ onChange={(e) => setSettings({ ...settings, apiKey: e.target.value })}
32
+ type="password"
33
+ placeholder="Enter your API key"
34
+ />
35
+ <p className="text-[11px] tracking-[-0.11px] text-text-dim">
36
+ {settings.provider === 'local' ? 'No API key needed for local models.' : 'Your key is stored locally and never shared.'}
37
+ </p>
38
+ </div>
39
+ </SettingsGroup>
40
+
41
+ {/* Voice */}
42
+ <SettingsGroup label="Voice">
43
+ <ListItem
44
+ title="Enable voice input"
45
+ subtitle="Use microphone to send messages"
46
+ trailing={
47
+ <Toggle
48
+ checked={settings.voiceEnabled}
49
+ onChange={(v) => setSettings({ ...settings, voiceEnabled: v })}
50
+ />
51
+ }
52
+ />
53
+ </SettingsGroup>
54
+
55
+ {/* Display */}
56
+ <SettingsGroup label="Display">
57
+ <ListItem
58
+ title="Dark mode"
59
+ subtitle="Use dark color scheme"
60
+ trailing={
61
+ <Toggle
62
+ checked={settings.darkMode}
63
+ onChange={(v) => setSettings({ ...settings, darkMode: v })}
64
+ />
65
+ }
66
+ />
67
+ </SettingsGroup>
68
+
69
+ {/* Data */}
70
+ <SettingsGroup label="Data">
71
+ <div onClick={clearMessages}>
72
+ <ListItem
73
+ title="Clear conversation"
74
+ subtitle="Remove all messages from the current session"
75
+ />
76
+ </div>
77
+ </SettingsGroup>
78
+
79
+ {/* About */}
80
+ <SettingsGroup label="About">
81
+ <ListItem
82
+ title="{{DISPLAY_NAME}} v1.0.0"
83
+ subtitle="AI chat for G2 smart glasses"
84
+ />
85
+ </SettingsGroup>
86
+ </main>
87
+ )
88
+ }
@@ -0,0 +1,13 @@
1
+ export interface Message {
2
+ id: string
3
+ role: 'user' | 'assistant' | 'system'
4
+ content: string
5
+ timestamp: number
6
+ }
7
+
8
+ export interface ChatSettings {
9
+ provider: 'openai' | 'anthropic' | 'local'
10
+ apiKey: string
11
+ voiceEnabled: boolean
12
+ darkMode: boolean
13
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "chat",
3
+ "displayName": "AI Chat",
4
+ "description": "AI chat interface with ChatContainer, voice input, and G2 glasses display",
5
+ "tags": ["ai", "chat", "voice"],
6
+ "components": ["AppShell", "NavHeader", "Button", "ChatContainer", "ChatBubble", "ChatInput", "ChatThinking", "VoiceInput", "SettingsGroup", "ListItem", "Select", "Toggle", "Input"]
7
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "jsx": "react-jsx",
8
+ "strict": true,
9
+ "noEmit": true,
10
+ "skipLibCheck": true,
11
+ "esModuleInterop": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "baseUrl": ".",
16
+ "paths": { "@/*": ["./src/*"] }
17
+ },
18
+ "include": ["src"],
19
+ "references": [{ "path": "./tsconfig.node.json" }]
20
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2022"],
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "types": ["node"],
8
+ "composite": true,
9
+ "noEmit": false,
10
+ "skipLibCheck": true
11
+ },
12
+ "include": ["vite.config.ts"]
13
+ }
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import tailwindcss from '@tailwindcss/vite'
4
+ import path from 'path'
5
+
6
+ export default defineConfig({
7
+ plugins: [react(), tailwindcss()],
8
+ resolve: {
9
+ alias: { '@': path.resolve(__dirname, './src') },
10
+ dedupe: ['react', 'react-dom', 'react-router', '@evenrealities/even_hub_sdk', '@jappyjan/even-better-sdk', 'upng-js'],
11
+ },
12
+ })
@@ -0,0 +1,17 @@
1
+ # {{DISPLAY_NAME}}
2
+
3
+ A G2 smart glasses app built with [even-toolkit](https://www.npmjs.com/package/even-toolkit).
4
+
5
+ ## Development
6
+
7
+ ```bash
8
+ npm install
9
+ npm run dev
10
+ ```
11
+
12
+ ## Build for Even Hub
13
+
14
+ ```bash
15
+ npm run build
16
+ npx @evenrealities/evenhub-cli pack app.json dist
17
+ ```
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
6
+ <title>{{DISPLAY_NAME}}</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "{{APP_NAME}}",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite --port 5173 --host",
8
+ "build": "tsc -b && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@evenrealities/even_hub_sdk": "^0.0.9",
13
+ "@jappyjan/even-better-sdk": "^0.0.11",
14
+ "class-variance-authority": "^0.7.1",
15
+ "clsx": "^2.1.0",
16
+ "even-toolkit": "^1.5.0",
17
+ "react": "^19.0.0",
18
+ "react-dom": "^19.0.0",
19
+ "react-is": "^19.2.4",
20
+ "react-router": "^7.0.0",
21
+ "tailwind-merge": "^3.0.0",
22
+ "upng-js": "^2.1.0"
23
+ },
24
+ "devDependencies": {
25
+ "@tailwindcss/vite": "^4.0.0",
26
+ "@types/react": "^19.0.0",
27
+ "@types/react-dom": "^19.0.0",
28
+ "@types/node": "^22.0.0",
29
+ "@vitejs/plugin-react": "^4.3.0",
30
+ "tailwindcss": "^4.0.0",
31
+ "typescript": "~5.4.0",
32
+ "vite": "^5.4.0"
33
+ }
34
+ }
@@ -0,0 +1,27 @@
1
+ import { useState } from 'react'
2
+ import { AppShell, NavBar } from 'even-toolkit/web'
3
+ import { OverviewScreen } from './screens/OverviewScreen'
4
+ import { ChartsScreen } from './screens/ChartsScreen'
5
+ import { SettingsScreen } from './screens/SettingsScreen'
6
+ import { AppGlasses } from './glass/AppGlasses'
7
+
8
+ const tabs = [
9
+ { id: 'overview', label: 'Overview' },
10
+ { id: 'charts', label: 'Charts' },
11
+ { id: 'settings', label: 'Settings' },
12
+ ]
13
+
14
+ export function App() {
15
+ const [tab, setTab] = useState('overview')
16
+
17
+ return (
18
+ <AppShell header={<NavBar items={tabs} activeId={tab} onNavigate={setTab} />}>
19
+ <AppGlasses />
20
+ <div className="px-3 pt-4 pb-8">
21
+ {tab === 'overview' && <OverviewScreen />}
22
+ {tab === 'charts' && <ChartsScreen />}
23
+ {tab === 'settings' && <SettingsScreen />}
24
+ </div>
25
+ </AppShell>
26
+ )
27
+ }
@@ -0,0 +1,54 @@
1
+ @import "tailwindcss";
2
+ @import "even-toolkit/web/theme-light.css";
3
+ @import "even-toolkit/web/typography.css";
4
+ @import "even-toolkit/web/utilities.css";
5
+ @source "../src";
6
+ @source "../node_modules/even-toolkit/web";
7
+ @source "../node_modules/even-toolkit/dist";
8
+
9
+ @theme {
10
+ --color-bg: var(--color-bg);
11
+ --color-surface: var(--color-surface);
12
+ --color-surface-light: var(--color-surface-light);
13
+ --color-surface-lighter: var(--color-surface-lighter);
14
+ --color-border: var(--color-border);
15
+ --color-border-light: var(--color-border-light);
16
+ --color-text: var(--color-text);
17
+ --color-text-dim: var(--color-text-dim);
18
+ --color-text-muted: var(--color-text-muted);
19
+ --color-text-highlight: var(--color-text-highlight);
20
+ --color-accent: var(--color-accent);
21
+ --color-accent-alpha: var(--color-accent-alpha);
22
+ --color-accent-warning: var(--color-accent-warning);
23
+ --color-positive: var(--color-positive);
24
+ --color-positive-alpha: var(--color-positive-alpha);
25
+ --color-negative: var(--color-negative);
26
+ --color-negative-alpha: var(--color-negative-alpha);
27
+ --color-overlay: var(--color-overlay);
28
+ --color-input-bg: var(--color-input-bg);
29
+ --radius-default: var(--radius-default);
30
+ --font-display: var(--font-display);
31
+ --font-body: var(--font-body);
32
+ --font-mono: var(--font-mono);
33
+ }
34
+
35
+ html, body {
36
+ background: var(--color-bg);
37
+ color: var(--color-text);
38
+ min-height: 100dvh;
39
+ font-family: var(--font-display);
40
+ -webkit-font-smoothing: antialiased;
41
+ }
42
+
43
+ #root {
44
+ max-width: 430px;
45
+ margin: 0 auto;
46
+ }
47
+
48
+ .scrollbar-hide {
49
+ -ms-overflow-style: none;
50
+ scrollbar-width: none;
51
+ }
52
+ .scrollbar-hide::-webkit-scrollbar {
53
+ display: none;
54
+ }
@@ -0,0 +1,53 @@
1
+ import { useCallback, useMemo, useRef } from 'react'
2
+ import { useNavigate, useLocation } from 'react-router'
3
+ import { useGlasses } from 'even-toolkit/useGlasses'
4
+ import { useFlashPhase } from 'even-toolkit/useFlashPhase'
5
+ import { createScreenMapper, getHomeTiles } from 'even-toolkit/glass-router'
6
+ import { appSplash } from './splash'
7
+ import { toDisplayData, onGlassAction, type AppSnapshot } from './selectors'
8
+ import type { AppActions } from './shared'
9
+
10
+ const deriveScreen = createScreenMapper([
11
+ { pattern: '/', screen: 'home' },
12
+ ], 'home')
13
+
14
+ const homeTiles = getHomeTiles(appSplash)
15
+
16
+ export function AppGlasses() {
17
+ const navigate = useNavigate()
18
+ const location = useLocation()
19
+ const flashPhase = useFlashPhase(deriveScreen(location.pathname) === 'home')
20
+
21
+ const snapshotRef = useMemo(() => ({
22
+ current: null as AppSnapshot | null,
23
+ }), [])
24
+
25
+ const snapshot: AppSnapshot = {
26
+ items: ['{{DISPLAY_NAME}} Dashboard'],
27
+ flashPhase,
28
+ }
29
+ snapshotRef.current = snapshot
30
+
31
+ const getSnapshot = useCallback(() => snapshotRef.current!, [snapshotRef])
32
+ const ctxRef = useRef<AppActions>({ navigate })
33
+ ctxRef.current = { navigate }
34
+
35
+ const handleGlassAction = useCallback(
36
+ (action: Parameters<typeof onGlassAction>[0], nav: Parameters<typeof onGlassAction>[1], snap: AppSnapshot) =>
37
+ onGlassAction(action, nav, snap, ctxRef.current),
38
+ [],
39
+ )
40
+
41
+ useGlasses({
42
+ getSnapshot,
43
+ toDisplayData,
44
+ onGlassAction: handleGlassAction,
45
+ deriveScreen,
46
+ appName: '{{DISPLAY_NAME_UPPER}}',
47
+ splash: appSplash,
48
+ getPageMode: (screen) => screen === 'home' ? 'home' : 'text',
49
+ homeImageTiles: homeTiles,
50
+ })
51
+
52
+ return null
53
+ }
@@ -0,0 +1,23 @@
1
+ import type { GlassScreen } from 'even-toolkit/glass-screen-router'
2
+ import { buildScrollableList } from 'even-toolkit/glass-display-builders'
3
+ import { moveHighlight } from 'even-toolkit/glass-nav'
4
+ import type { AppSnapshot, AppActions } from '../shared'
5
+
6
+ export const homeScreen: GlassScreen<AppSnapshot, AppActions> = {
7
+ display(snapshot, nav) {
8
+ return {
9
+ lines: buildScrollableList({
10
+ items: snapshot.items,
11
+ highlightedIndex: nav.highlightedIndex,
12
+ maxVisible: 5,
13
+ formatter: (item) => item,
14
+ }),
15
+ }
16
+ },
17
+ action(action, nav, snapshot) {
18
+ if (action.type === 'HIGHLIGHT_MOVE') {
19
+ return { ...nav, highlightedIndex: moveHighlight(nav.highlightedIndex, action.direction, snapshot.items.length - 1) }
20
+ }
21
+ return nav
22
+ },
23
+ }
@@ -0,0 +1,9 @@
1
+ import { createGlassScreenRouter } from 'even-toolkit/glass-screen-router'
2
+ import type { AppSnapshot, AppActions } from './shared'
3
+ import { homeScreen } from './screens/home'
4
+
5
+ export type { AppSnapshot, AppActions }
6
+
7
+ export const { toDisplayData, onGlassAction } = createGlassScreenRouter<AppSnapshot, AppActions>({
8
+ 'home': homeScreen,
9
+ }, 'home')
@@ -0,0 +1,8 @@
1
+ export interface AppSnapshot {
2
+ items: string[]
3
+ flashPhase: boolean
4
+ }
5
+
6
+ export interface AppActions {
7
+ navigate: (path: string) => void
8
+ }
@@ -0,0 +1,22 @@
1
+ import { createSplash, TILE_PRESETS } from 'even-toolkit/splash'
2
+
3
+ export function renderSplash(ctx: CanvasRenderingContext2D, w: number, h: number) {
4
+ const fg = '#e0e0e0'
5
+ const s = Math.min(w / 200, h / 200)
6
+ ctx.fillStyle = fg
7
+ ctx.font = `bold ${14 * s}px "Courier New", monospace`
8
+ ctx.textAlign = 'center'
9
+ ctx.fillText('{{DISPLAY_NAME_UPPER}}', w / 2, 50 * s)
10
+ ctx.textAlign = 'left'
11
+ }
12
+
13
+ export const appSplash = createSplash({
14
+ tiles: 1,
15
+ tileLayout: 'vertical',
16
+ tilePositions: TILE_PRESETS.topCenter1,
17
+ canvasSize: { w: 200, h: 200 },
18
+ minTimeMs: 0,
19
+ maxTimeMs: 0,
20
+ menuText: '',
21
+ render: renderSplash,
22
+ })
@@ -0,0 +1,13 @@
1
+ import { StrictMode } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import { BrowserRouter } from 'react-router'
4
+ import { App } from './App'
5
+ import './app.css'
6
+
7
+ createRoot(document.getElementById('root')!).render(
8
+ <StrictMode>
9
+ <BrowserRouter>
10
+ <App />
11
+ </BrowserRouter>
12
+ </StrictMode>,
13
+ )
@@ -0,0 +1,99 @@
1
+ import { ScreenHeader, LineChart, BarChart, PieChart, Card } from 'even-toolkit/web'
2
+ import type { LineChartPoint, BarChartItem, PieChartItem } from 'even-toolkit/web'
3
+
4
+ /* ── Monthly revenue data (12 months) ─────────────────────────── */
5
+
6
+ const monthLabels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
7
+
8
+ const revenueData: LineChartPoint[] = [
9
+ { x: 0, y: 28400, label: 'Jan' },
10
+ { x: 1, y: 31200, label: 'Feb' },
11
+ { x: 2, y: 29800, label: 'Mar' },
12
+ { x: 3, y: 34600, label: 'Apr' },
13
+ { x: 4, y: 37100, label: 'May' },
14
+ { x: 5, y: 35800, label: 'Jun' },
15
+ { x: 6, y: 39200, label: 'Jul' },
16
+ { x: 7, y: 41500, label: 'Aug' },
17
+ { x: 8, y: 40100, label: 'Sep' },
18
+ { x: 9, y: 44300, label: 'Oct' },
19
+ { x: 10, y: 46800, label: 'Nov' },
20
+ { x: 11, y: 48200, label: 'Dec' },
21
+ ]
22
+
23
+ /* ── Weekly breakdown (7 days) ─────────────────────────────────── */
24
+
25
+ const weeklyData: BarChartItem[] = [
26
+ { label: 'Mon', value: 8420 },
27
+ { label: 'Tue', value: 9150 },
28
+ { label: 'Wed', value: 8780 },
29
+ { label: 'Thu', value: 9340 },
30
+ { label: 'Fri', value: 7860 },
31
+ { label: 'Sat', value: 4120 },
32
+ { label: 'Sun', value: 3580 },
33
+ ]
34
+
35
+ /* ── Traffic sources (pie/donut) ───────────────────────────────── */
36
+
37
+ const trafficSources: PieChartItem[] = [
38
+ { label: 'Organic Search', value: 4280, color: 'var(--color-accent)' },
39
+ { label: 'Direct', value: 2140, color: 'var(--color-positive)' },
40
+ { label: 'Social Media', value: 1630, color: 'var(--color-text-dim)' },
41
+ { label: 'Referral', value: 890, color: 'var(--color-border)' },
42
+ ]
43
+
44
+ /* ── Component ─────────────────────────────────────────────────── */
45
+
46
+ export function ChartsScreen() {
47
+ return (
48
+ <div className="flex flex-col gap-6">
49
+ <ScreenHeader title="Charts" />
50
+
51
+ {/* Revenue trend */}
52
+ <div className="flex flex-col gap-3">
53
+ <div className="flex items-baseline justify-between">
54
+ <div className="text-[13px] tracking-[-0.13px] text-text-dim">Monthly Revenue</div>
55
+ <div className="text-[11px] tracking-[-0.11px] text-text-dim">Jan - Dec 2025</div>
56
+ </div>
57
+ <Card className="p-4 overflow-hidden">
58
+ <LineChart
59
+ data={revenueData}
60
+ height={220}
61
+ showArea
62
+ showGrid
63
+ showLabels
64
+ color="var(--color-accent)"
65
+ />
66
+ </Card>
67
+ </div>
68
+
69
+ {/* Weekly breakdown */}
70
+ <div className="flex flex-col gap-3">
71
+ <div className="flex items-baseline justify-between">
72
+ <div className="text-[13px] tracking-[-0.13px] text-text-dim">Weekly Revenue</div>
73
+ <div className="text-[11px] tracking-[-0.11px] text-text-dim">This week</div>
74
+ </div>
75
+ <Card className="p-4 overflow-hidden">
76
+ <BarChart
77
+ data={weeklyData}
78
+ height={200}
79
+ showLabels
80
+ color="var(--color-accent)"
81
+ />
82
+ </Card>
83
+ </div>
84
+
85
+ {/* Traffic sources donut */}
86
+ <div className="flex flex-col gap-3">
87
+ <div className="text-[13px] tracking-[-0.13px] text-text-dim">Traffic Sources</div>
88
+ <Card className="p-4 flex justify-center">
89
+ <PieChart
90
+ data={trafficSources}
91
+ size={180}
92
+ donut
93
+ centerLabel="8,940"
94
+ />
95
+ </Card>
96
+ </div>
97
+ </div>
98
+ )
99
+ }