@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,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,104 @@
1
+ import { useParams, useNavigate } from 'react-router'
2
+ import { Card, Badge, Button, EmptyState, useDrawerHeader } from 'even-toolkit/web'
3
+ import { IcEdit, IcTrash } from 'even-toolkit/web/icons/svg-icons'
4
+ import { useNotes } from '../contexts/NotesContext'
5
+
6
+ const CATEGORY_BADGE_VARIANT = {
7
+ Personal: 'positive',
8
+ Work: 'accent',
9
+ Ideas: 'neutral',
10
+ } as const
11
+
12
+ function formatDate(timestamp: number): string {
13
+ return new Date(timestamp).toLocaleDateString('en-US', {
14
+ weekday: 'short',
15
+ month: 'short',
16
+ day: 'numeric',
17
+ year: 'numeric',
18
+ hour: 'numeric',
19
+ minute: '2-digit',
20
+ })
21
+ }
22
+
23
+ export function NoteDetail() {
24
+ const { id } = useParams<{ id: string }>()
25
+ const navigate = useNavigate()
26
+ const { notes, deleteNote } = useNotes()
27
+
28
+ const note = notes.find((n) => n.id === id)
29
+
30
+ useDrawerHeader({
31
+ title: note?.title ?? 'Note',
32
+ backTo: '/',
33
+ right: note ? (
34
+ <div className="flex items-center gap-1">
35
+ <Button variant="ghost" size="icon" onClick={() => navigate(`/note/${note.id}/edit`)}>
36
+ <IcEdit width={20} height={20} />
37
+ </Button>
38
+ <Button
39
+ variant="ghost"
40
+ size="icon"
41
+ onClick={() => {
42
+ deleteNote(note.id)
43
+ navigate('/')
44
+ }}
45
+ >
46
+ <IcTrash width={20} height={20} />
47
+ </Button>
48
+ </div>
49
+ ) : undefined,
50
+ })
51
+
52
+ if (!note) {
53
+ return (
54
+ <main className="px-3 pt-4 pb-8">
55
+ <EmptyState
56
+ title="Note not found"
57
+ description="This note may have been deleted."
58
+ action={{ label: 'Back to Notes', onClick: () => navigate('/') }}
59
+ />
60
+ </main>
61
+ )
62
+ }
63
+
64
+ return (
65
+ <main className="px-3 pt-4 pb-8 space-y-3">
66
+ <div className="flex items-center gap-2">
67
+ <Badge variant={CATEGORY_BADGE_VARIANT[note.category]}>{note.category}</Badge>
68
+ </div>
69
+
70
+ <Card className="p-4 space-y-3">
71
+ <p className="text-[15px] tracking-[-0.15px] text-text whitespace-pre-wrap leading-relaxed">
72
+ {note.content}
73
+ </p>
74
+ </Card>
75
+
76
+ <Card className="p-4 space-y-1.5">
77
+ <div className="flex items-center justify-between">
78
+ <span className="text-[11px] tracking-[-0.11px] text-text-dim">Created</span>
79
+ <span className="text-[11px] tracking-[-0.11px] text-text">{formatDate(note.createdAt)}</span>
80
+ </div>
81
+ <div className="flex items-center justify-between">
82
+ <span className="text-[11px] tracking-[-0.11px] text-text-dim">Updated</span>
83
+ <span className="text-[11px] tracking-[-0.11px] text-text">{formatDate(note.updatedAt)}</span>
84
+ </div>
85
+ </Card>
86
+
87
+ <div className="flex gap-2">
88
+ <Button className="flex-1" onClick={() => navigate(`/note/${note.id}/edit`)}>
89
+ Edit Note
90
+ </Button>
91
+ <Button
92
+ variant="danger"
93
+ className="flex-1"
94
+ onClick={() => {
95
+ deleteNote(note.id)
96
+ navigate('/')
97
+ }}
98
+ >
99
+ Delete
100
+ </Button>
101
+ </div>
102
+ </main>
103
+ )
104
+ }
@@ -0,0 +1,84 @@
1
+ import { useState, useEffect } from 'react'
2
+ import { useParams, useNavigate } from 'react-router'
3
+ import { Input, Textarea, Select, Button, Card, useDrawerHeader } from 'even-toolkit/web'
4
+ import { useNotes } from '../contexts/NotesContext'
5
+ import { CATEGORIES, type NoteCategory } from '../types'
6
+
7
+ const CATEGORY_OPTIONS = CATEGORIES.map((c) => ({ value: c, label: c }))
8
+
9
+ export function NoteForm() {
10
+ const { id } = useParams<{ id: string }>()
11
+ const navigate = useNavigate()
12
+ const { notes, addNote, updateNote } = useNotes()
13
+
14
+ const existing = id ? notes.find((n) => n.id === id) : undefined
15
+ const isEdit = Boolean(existing)
16
+
17
+ const [title, setTitle] = useState(existing?.title ?? '')
18
+ const [content, setContent] = useState(existing?.content ?? '')
19
+ const [category, setCategory] = useState<NoteCategory>(existing?.category ?? 'Personal')
20
+
21
+ useEffect(() => {
22
+ if (existing) {
23
+ setTitle(existing.title)
24
+ setContent(existing.content)
25
+ setCategory(existing.category)
26
+ }
27
+ }, [existing])
28
+
29
+ useDrawerHeader({
30
+ title: isEdit ? 'Edit Note' : 'New Note',
31
+ backTo: isEdit && id ? `/note/${id}` : '/',
32
+ })
33
+
34
+ const canSave = title.trim().length > 0
35
+
36
+ function handleSave() {
37
+ if (!canSave) return
38
+ if (isEdit && id) {
39
+ updateNote(id, title.trim(), content.trim(), category)
40
+ navigate(`/note/${id}`)
41
+ } else {
42
+ const note = addNote(title.trim(), content.trim(), category)
43
+ navigate(`/note/${note.id}`)
44
+ }
45
+ }
46
+
47
+ return (
48
+ <main className="px-3 pt-4 pb-8 space-y-3">
49
+ <Card className="p-4 space-y-3">
50
+ <div className="space-y-1.5">
51
+ <label className="text-[11px] tracking-[-0.11px] text-text-dim block">Title</label>
52
+ <Input
53
+ value={title}
54
+ onChange={(e) => setTitle(e.target.value)}
55
+ placeholder="Note title"
56
+ />
57
+ </div>
58
+
59
+ <div className="space-y-1.5">
60
+ <label className="text-[11px] tracking-[-0.11px] text-text-dim block">Content</label>
61
+ <Textarea
62
+ value={content}
63
+ onChange={(e) => setContent(e.target.value)}
64
+ placeholder="Write your note..."
65
+ rows={8}
66
+ />
67
+ </div>
68
+
69
+ <div className="space-y-1.5">
70
+ <label className="text-[11px] tracking-[-0.11px] text-text-dim block">Category</label>
71
+ <Select
72
+ options={CATEGORY_OPTIONS}
73
+ value={category}
74
+ onValueChange={(v) => setCategory(v as NoteCategory)}
75
+ />
76
+ </div>
77
+ </Card>
78
+
79
+ <Button className="w-full" onClick={handleSave} disabled={!canSave}>
80
+ {isEdit ? 'Save Changes' : 'Create Note'}
81
+ </Button>
82
+ </main>
83
+ )
84
+ }
@@ -0,0 +1,108 @@
1
+ import { useNavigate } from 'react-router'
2
+ import { SearchBar, CategoryFilter, ListItem, Badge, EmptyState, Button, ScreenHeader, useDrawerHeader } from 'even-toolkit/web'
3
+ import { IcPlus, IcFeatQuickNote } from 'even-toolkit/web/icons/svg-icons'
4
+ import { useNotes } from '../contexts/NotesContext'
5
+ import { ALL_FILTER, CATEGORIES, type CategoryFilter as CategoryFilterType } from '../types'
6
+
7
+ const FILTER_OPTIONS = [ALL_FILTER, ...CATEGORIES]
8
+
9
+ const CATEGORY_BADGE_VARIANT = {
10
+ Personal: 'positive',
11
+ Work: 'accent',
12
+ Ideas: 'neutral',
13
+ } as const
14
+
15
+ function formatRelativeTime(timestamp: number): string {
16
+ const diff = Date.now() - timestamp
17
+ const minutes = Math.floor(diff / 60000)
18
+ if (minutes < 1) return 'Just now'
19
+ if (minutes < 60) return `${minutes}m ago`
20
+ const hours = Math.floor(minutes / 60)
21
+ if (hours < 24) return `${hours}h ago`
22
+ const days = Math.floor(hours / 24)
23
+ if (days < 7) return `${days}d ago`
24
+ return new Date(timestamp).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
25
+ }
26
+
27
+ export function NoteList() {
28
+ const navigate = useNavigate()
29
+ const {
30
+ filteredNotes,
31
+ searchQuery,
32
+ setSearchQuery,
33
+ selectedCategory,
34
+ setSelectedCategory,
35
+ deleteNote,
36
+ compactView,
37
+ } = useNotes()
38
+
39
+ useDrawerHeader({
40
+ right: (
41
+ <Button variant="ghost" size="icon" onClick={() => navigate('/new')}>
42
+ <IcPlus width={20} height={20} />
43
+ </Button>
44
+ ),
45
+ })
46
+
47
+ return (
48
+ <main className="px-3 pt-4 pb-8 space-y-3">
49
+ <ScreenHeader
50
+ title="Notes"
51
+ subtitle={`${filteredNotes.length} note${filteredNotes.length !== 1 ? 's' : ''}`}
52
+ />
53
+
54
+ <SearchBar
55
+ value={searchQuery}
56
+ onChange={(e) => setSearchQuery(e.target.value)}
57
+ placeholder="Search notes..."
58
+ />
59
+
60
+ <CategoryFilter
61
+ categories={FILTER_OPTIONS}
62
+ selected={selectedCategory}
63
+ onSelect={(c) => setSelectedCategory(c as CategoryFilterType)}
64
+ />
65
+
66
+ {filteredNotes.length === 0 ? (
67
+ <EmptyState
68
+ icon={<IcFeatQuickNote width={48} height={48} />}
69
+ title={searchQuery || selectedCategory !== ALL_FILTER ? 'No matching notes' : 'No notes yet'}
70
+ description={
71
+ searchQuery || selectedCategory !== ALL_FILTER
72
+ ? 'Try adjusting your search or category filter.'
73
+ : 'Tap the + button to create your first note.'
74
+ }
75
+ action={
76
+ !searchQuery && selectedCategory === ALL_FILTER
77
+ ? { label: 'New Note', onClick: () => navigate('/new') }
78
+ : undefined
79
+ }
80
+ />
81
+ ) : (
82
+ <div className="rounded-[6px] overflow-hidden divide-y divide-border">
83
+ {filteredNotes.map((note) => (
84
+ <ListItem
85
+ key={note.id}
86
+ title={note.title}
87
+ subtitle={
88
+ compactView
89
+ ? undefined
90
+ : note.content.split('\n')[0].slice(0, 80) + (note.content.length > 80 ? '...' : '')
91
+ }
92
+ trailing={
93
+ <div className="flex items-center gap-2">
94
+ <Badge variant={CATEGORY_BADGE_VARIANT[note.category]}>{note.category}</Badge>
95
+ <span className="text-[11px] tracking-[-0.11px] text-text-dim whitespace-nowrap">
96
+ {formatRelativeTime(note.updatedAt)}
97
+ </span>
98
+ </div>
99
+ }
100
+ onPress={() => navigate(`/note/${note.id}`)}
101
+ onDelete={() => deleteNote(note.id)}
102
+ />
103
+ ))}
104
+ </div>
105
+ )}
106
+ </main>
107
+ )
108
+ }
@@ -0,0 +1,88 @@
1
+ import { useState } from 'react'
2
+ import { SettingsGroup, Toggle, ListItem, Card, Button, Divider, useDrawerHeader } from 'even-toolkit/web'
3
+ import { useNotes } from '../contexts/NotesContext'
4
+
5
+ export function Settings() {
6
+ const { notes, compactView, setCompactView } = useNotes()
7
+ const [confirmClear, setConfirmClear] = useState(false)
8
+
9
+ useDrawerHeader({ title: 'Settings', backTo: '/' })
10
+
11
+ function handleExport() {
12
+ const data = JSON.stringify(notes, null, 2)
13
+ const blob = new Blob([data], { type: 'application/json' })
14
+ const url = URL.createObjectURL(blob)
15
+ const a = document.createElement('a')
16
+ a.href = url
17
+ a.download = `notes-export-${Date.now()}.json`
18
+ a.click()
19
+ URL.revokeObjectURL(url)
20
+ }
21
+
22
+ function handleClearAll() {
23
+ if (!confirmClear) {
24
+ setConfirmClear(true)
25
+ return
26
+ }
27
+ localStorage.removeItem('{{APP_NAME}}-notes')
28
+ window.location.reload()
29
+ }
30
+
31
+ return (
32
+ <main className="px-3 pt-4 pb-8 space-y-6">
33
+ <SettingsGroup label="Display">
34
+ <Card className="p-4">
35
+ <div className="flex items-center justify-between">
36
+ <div>
37
+ <p className="text-[15px] tracking-[-0.15px] text-text">Compact View</p>
38
+ <p className="text-[11px] tracking-[-0.11px] text-text-dim mt-0.5">
39
+ Hide note previews in the list
40
+ </p>
41
+ </div>
42
+ <Toggle checked={compactView} onChange={setCompactView} />
43
+ </div>
44
+ </Card>
45
+ </SettingsGroup>
46
+
47
+ <SettingsGroup label="Data">
48
+ <Card className="divide-y divide-border">
49
+ <ListItem
50
+ title="Export Notes"
51
+ subtitle={`Export all ${notes.length} notes as JSON`}
52
+ onPress={handleExport}
53
+ />
54
+ <div>
55
+ <ListItem
56
+ title={confirmClear ? 'Tap again to confirm' : 'Clear All Notes'}
57
+ subtitle="Permanently delete all notes"
58
+ onPress={handleClearAll}
59
+ />
60
+ {confirmClear && (
61
+ <div className="px-4 pb-3">
62
+ <Button
63
+ variant="ghost"
64
+ size="sm"
65
+ className="w-full"
66
+ onClick={() => setConfirmClear(false)}
67
+ >
68
+ Cancel
69
+ </Button>
70
+ </div>
71
+ )}
72
+ </div>
73
+ </Card>
74
+ </SettingsGroup>
75
+
76
+ <SettingsGroup label="About">
77
+ <Card className="p-4 space-y-1.5">
78
+ <p className="text-[15px] tracking-[-0.15px] text-text">{{DISPLAY_NAME}}</p>
79
+ <p className="text-[13px] tracking-[-0.13px] text-text-dim">Version 1.0.0</p>
80
+ <Divider className="my-2" />
81
+ <p className="text-[11px] tracking-[-0.11px] text-text-dim">
82
+ A notes app for Even Realities G2 smart glasses. All data is stored locally in your browser.
83
+ </p>
84
+ </Card>
85
+ </SettingsGroup>
86
+ </main>
87
+ )
88
+ }
@@ -0,0 +1,14 @@
1
+ export type NoteCategory = 'Personal' | 'Work' | 'Ideas'
2
+
3
+ export interface Note {
4
+ id: string
5
+ title: string
6
+ content: string
7
+ category: NoteCategory
8
+ createdAt: number
9
+ updatedAt: number
10
+ }
11
+
12
+ export const CATEGORIES: NoteCategory[] = ['Personal', 'Work', 'Ideas']
13
+ export const ALL_FILTER = 'All'
14
+ export type CategoryFilter = typeof ALL_FILTER | NoteCategory
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "notes",
3
+ "displayName": "Notes App",
4
+ "description": "A notes and productivity CRUD app with categories, search, and glass display",
5
+ "tags": ["productivity", "crud", "notes"],
6
+ "components": ["DrawerShell", "SearchBar", "CategoryFilter", "ListItem", "Badge", "EmptyState", "Card", "Input", "Textarea", "Select", "Button", "SettingsGroup", "Toggle", "ScreenHeader"]
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,27 @@
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
+ Open [http://localhost:5173](http://localhost:5173) in your browser.
13
+
14
+ ## Test with Simulator
15
+
16
+ ```bash
17
+ npx @evenrealities/evenhub-simulator@latest http://localhost:5173
18
+ ```
19
+
20
+ ## Build for Even Hub
21
+
22
+ ```bash
23
+ npm run build
24
+ npx @evenrealities/evenhub-cli pack app.json dist
25
+ ```
26
+
27
+ Upload the generated `.ehpk` file to the Even Hub.
@@ -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,24 @@
1
+ import { Routes, Route } from 'react-router'
2
+ import { Shell } from './layouts/shell'
3
+ import { TrackerProvider } from './contexts/TrackerContext'
4
+ import { TodayScreen } from './screens/TodayScreen'
5
+ import { HistoryScreen } from './screens/HistoryScreen'
6
+ import { NewEntryScreen } from './screens/NewEntryScreen'
7
+ import { Settings } from './screens/Settings'
8
+ import { AppGlasses } from './glass/AppGlasses'
9
+
10
+ export function App() {
11
+ return (
12
+ <TrackerProvider>
13
+ <Routes>
14
+ <Route element={<Shell />}>
15
+ <Route path="/" element={<TodayScreen />} />
16
+ <Route path="/history" element={<HistoryScreen />} />
17
+ <Route path="/new" element={<NewEntryScreen />} />
18
+ <Route path="/settings" element={<Settings />} />
19
+ </Route>
20
+ </Routes>
21
+ <AppGlasses />
22
+ </TrackerProvider>
23
+ )
24
+ }
@@ -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
+ }