@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,78 @@
1
+ import { Card, ListItem, ScreenHeader, Button, useDrawerHeader } from 'even-toolkit/web'
2
+ import { IcEditPlay, IcEditPause } from 'even-toolkit/web/icons/svg-icons'
3
+ import { useMedia } from '../contexts/MediaContext'
4
+
5
+ export function AudioScreen() {
6
+ const { audioTracks, selectedTrackId, setSelectedTrackId } = useMedia()
7
+
8
+ useDrawerHeader({ title: 'Audio' })
9
+
10
+ const selectedTrack = audioTracks.find((t) => t.id === selectedTrackId)
11
+
12
+ return (
13
+ <main className="px-3 pt-4 pb-8 space-y-3">
14
+ <ScreenHeader
15
+ title="Audio"
16
+ subtitle={`${audioTracks.length} track${audioTracks.length !== 1 ? 's' : ''}`}
17
+ />
18
+
19
+ <div className="rounded-[6px] overflow-hidden divide-y divide-border">
20
+ {audioTracks.map((track) => (
21
+ <ListItem
22
+ key={track.id}
23
+ title={track.title}
24
+ subtitle={track.artist}
25
+ trailing={
26
+ <div className="flex items-center gap-3">
27
+ <span className="text-[11px] tracking-[-0.11px] text-text-dim tabular-nums">
28
+ {track.duration}
29
+ </span>
30
+ <Button
31
+ variant="ghost"
32
+ size="icon"
33
+ onClick={(e) => {
34
+ e.stopPropagation()
35
+ setSelectedTrackId(selectedTrackId === track.id ? null : track.id)
36
+ }}
37
+ className="w-8 h-8 rounded-full bg-accent text-text-highlight"
38
+ >
39
+ {selectedTrackId === track.id ? (
40
+ <IcEditPause width={14} height={14} />
41
+ ) : (
42
+ <IcEditPlay width={14} height={14} />
43
+ )}
44
+ </Button>
45
+ </div>
46
+ }
47
+ onPress={() => setSelectedTrackId(track.id)}
48
+ />
49
+ ))}
50
+ </div>
51
+
52
+ {selectedTrack && (
53
+ <Card className="p-3 mt-3">
54
+ <div className="flex items-center gap-3">
55
+ <Button
56
+ variant="ghost"
57
+ size="icon"
58
+ onClick={() => setSelectedTrackId(null)}
59
+ className="shrink-0 w-10 h-10 rounded-full bg-accent text-text-highlight"
60
+ >
61
+ <IcEditPause width={16} height={16} />
62
+ </Button>
63
+ <div className="flex-1 min-w-0">
64
+ <p className="text-[15px] tracking-[-0.15px] text-text truncate">{selectedTrack.title}</p>
65
+ <p className="text-[11px] tracking-[-0.11px] text-text-dim">{selectedTrack.artist}</p>
66
+ </div>
67
+ <span className="text-[13px] tracking-[-0.13px] text-text-dim tabular-nums shrink-0">
68
+ {selectedTrack.duration}
69
+ </span>
70
+ </div>
71
+ <div className="mt-2 h-1 bg-surface rounded-full overflow-hidden">
72
+ <div className="h-full bg-accent rounded-full w-1/3" />
73
+ </div>
74
+ </Card>
75
+ )}
76
+ </main>
77
+ )
78
+ }
@@ -0,0 +1,98 @@
1
+ import { useState } from 'react'
2
+ import { CategoryFilter, BottomSheet, Card, ScreenHeader, Button, useDrawerHeader } from 'even-toolkit/web'
3
+ import { IcGuideChevronSmallBack, IcGuideChevronSmallDrillIn } from 'even-toolkit/web/icons/svg-icons'
4
+ import { useMedia } from '../contexts/MediaContext'
5
+ import { ALL_FILTER, CATEGORIES, type CategoryFilter as CategoryFilterType } from '../types'
6
+
7
+ const FILTER_OPTIONS = [ALL_FILTER, ...CATEGORIES]
8
+
9
+ export function GalleryScreen() {
10
+ const { filteredGallery, selectedCategory, setSelectedCategory, gridColumns } = useMedia()
11
+ const [viewerIndex, setViewerIndex] = useState<number | null>(null)
12
+
13
+ useDrawerHeader({})
14
+
15
+ const selectedItem = viewerIndex !== null ? filteredGallery[viewerIndex] : null
16
+
17
+ return (
18
+ <main className="px-3 pt-4 pb-8 space-y-3">
19
+ <ScreenHeader
20
+ title="Gallery"
21
+ subtitle={`${filteredGallery.length} item${filteredGallery.length !== 1 ? 's' : ''}`}
22
+ />
23
+
24
+ <CategoryFilter
25
+ categories={FILTER_OPTIONS}
26
+ selected={selectedCategory}
27
+ onSelect={(c) => setSelectedCategory(c as CategoryFilterType)}
28
+ />
29
+
30
+ <div className={`grid gap-3 ${gridColumns === 2 ? 'grid-cols-2' : 'grid-cols-3'}`}>
31
+ {filteredGallery.map((item, index) => (
32
+ <Card
33
+ key={item.id}
34
+ className="overflow-hidden cursor-pointer"
35
+ onClick={() => setViewerIndex(index)}
36
+ >
37
+ <div
38
+ className="w-full aspect-square rounded-[6px]"
39
+ style={{ background: item.gradient }}
40
+ />
41
+ <div className="p-2">
42
+ <p className="text-[13px] tracking-[-0.13px] text-text truncate">{item.title}</p>
43
+ <p className="text-[11px] tracking-[-0.11px] text-text-dim">{item.date}</p>
44
+ </div>
45
+ </Card>
46
+ ))}
47
+ </div>
48
+
49
+ <BottomSheet open={viewerIndex !== null} onClose={() => setViewerIndex(null)}>
50
+ {selectedItem && (
51
+ <div className="px-4 pb-2">
52
+ <div
53
+ className="w-full aspect-video rounded-[6px] mb-3"
54
+ style={{ background: selectedItem.gradient }}
55
+ />
56
+ <div className="flex items-center justify-between mb-2">
57
+ <div>
58
+ <p className="text-[17px] tracking-[-0.17px] text-text">{selectedItem.title}</p>
59
+ <p className="text-[13px] tracking-[-0.13px] text-text-dim">
60
+ {selectedItem.category} &middot; {selectedItem.date}
61
+ </p>
62
+ </div>
63
+ </div>
64
+ <div className="flex items-center justify-between pt-2">
65
+ <Button
66
+ variant="ghost"
67
+ size="sm"
68
+ onClick={() => {
69
+ if (viewerIndex !== null && viewerIndex > 0) setViewerIndex(viewerIndex - 1)
70
+ }}
71
+ disabled={viewerIndex === 0}
72
+ className="flex items-center gap-1 text-[13px] tracking-[-0.13px] text-text-dim"
73
+ >
74
+ <IcGuideChevronSmallBack width={16} height={16} />
75
+ Previous
76
+ </Button>
77
+ <span className="text-[11px] tracking-[-0.11px] text-text-dim tabular-nums">
78
+ {viewerIndex !== null ? viewerIndex + 1 : 0} / {filteredGallery.length}
79
+ </span>
80
+ <Button
81
+ variant="ghost"
82
+ size="sm"
83
+ onClick={() => {
84
+ if (viewerIndex !== null && viewerIndex < filteredGallery.length - 1) setViewerIndex(viewerIndex + 1)
85
+ }}
86
+ disabled={viewerIndex === filteredGallery.length - 1}
87
+ className="flex items-center gap-1 text-[13px] tracking-[-0.13px] text-text-dim"
88
+ >
89
+ Next
90
+ <IcGuideChevronSmallDrillIn width={16} height={16} />
91
+ </Button>
92
+ </div>
93
+ </div>
94
+ )}
95
+ </BottomSheet>
96
+ </main>
97
+ )
98
+ }
@@ -0,0 +1,86 @@
1
+ import { useState } from 'react'
2
+ import { SettingsGroup, Toggle, ListItem, Card, Button, Divider, useDrawerHeader } from 'even-toolkit/web'
3
+ import { useMedia } from '../contexts/MediaContext'
4
+
5
+ export function Settings() {
6
+ const { gridColumns, setGridColumns, uploads, clearUploads } = useMedia()
7
+ const [confirmClear, setConfirmClear] = useState(false)
8
+
9
+ useDrawerHeader({ title: 'Settings', backTo: '/' })
10
+
11
+ function handleClearCache() {
12
+ if (!confirmClear) {
13
+ setConfirmClear(true)
14
+ return
15
+ }
16
+ clearUploads()
17
+ setConfirmClear(false)
18
+ }
19
+
20
+ return (
21
+ <main className="px-3 pt-4 pb-8 space-y-6">
22
+ <SettingsGroup label="Display">
23
+ <Card className="p-4 space-y-3">
24
+ <div className="flex items-center justify-between">
25
+ <div>
26
+ <p className="text-[15px] tracking-[-0.15px] text-text">Grid Columns</p>
27
+ <p className="text-[11px] tracking-[-0.11px] text-text-dim mt-0.5">
28
+ Toggle between 2 and 3 column layout
29
+ </p>
30
+ </div>
31
+ <Toggle
32
+ checked={gridColumns === 2}
33
+ onChange={(v) => setGridColumns(v ? 2 : 3)}
34
+ />
35
+ </div>
36
+ <Divider />
37
+ <div>
38
+ <p className="text-[15px] tracking-[-0.15px] text-text">Thumbnail Size</p>
39
+ <p className="text-[11px] tracking-[-0.11px] text-text-dim mt-0.5">
40
+ {gridColumns === 2 ? 'Large thumbnails (2 columns)' : 'Compact thumbnails (3 columns)'}
41
+ </p>
42
+ </div>
43
+ </Card>
44
+ </SettingsGroup>
45
+
46
+ <SettingsGroup label="Storage">
47
+ <Card className="divide-y divide-border">
48
+ <div>
49
+ <ListItem
50
+ title={confirmClear ? 'Tap again to confirm' : 'Clear Cache'}
51
+ subtitle={`${uploads.length} uploaded file${uploads.length !== 1 ? 's' : ''} cached`}
52
+ onPress={handleClearCache}
53
+ />
54
+ {confirmClear && (
55
+ <div className="px-4 pb-3">
56
+ <Button
57
+ variant="ghost"
58
+ size="sm"
59
+ className="w-full"
60
+ onClick={() => setConfirmClear(false)}
61
+ >
62
+ Cancel
63
+ </Button>
64
+ </div>
65
+ )}
66
+ </div>
67
+ <ListItem
68
+ title="Storage Used"
69
+ subtitle="In-memory only (no persistent storage)"
70
+ />
71
+ </Card>
72
+ </SettingsGroup>
73
+
74
+ <SettingsGroup label="About">
75
+ <Card className="p-4 space-y-1.5">
76
+ <p className="text-[15px] tracking-[-0.15px] text-text">{{DISPLAY_NAME}}</p>
77
+ <p className="text-[13px] tracking-[-0.13px] text-text-dim">Version 1.0.0</p>
78
+ <Divider className="my-2" />
79
+ <p className="text-[11px] tracking-[-0.11px] text-text-dim">
80
+ A media gallery app for Even Realities G2 smart glasses. Browse photos, listen to audio, and manage uploads.
81
+ </p>
82
+ </Card>
83
+ </SettingsGroup>
84
+ </main>
85
+ )
86
+ }
@@ -0,0 +1,95 @@
1
+ import { FileUpload, EmptyState, ListItem, Card, Button, ScreenHeader, useDrawerHeader } from 'even-toolkit/web'
2
+ import { IcEditUploadToCloud, IcEditTrash } from 'even-toolkit/web/icons/svg-icons'
3
+ import { useMedia } from '../contexts/MediaContext'
4
+
5
+ function formatSize(bytes: number): string {
6
+ if (bytes < 1024) return `${bytes} B`
7
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
8
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
9
+ }
10
+
11
+ function formatTime(timestamp: number): string {
12
+ const diff = Date.now() - timestamp
13
+ const minutes = Math.floor(diff / 60000)
14
+ if (minutes < 1) return 'Just now'
15
+ if (minutes < 60) return `${minutes}m ago`
16
+ const hours = Math.floor(minutes / 60)
17
+ if (hours < 24) return `${hours}h ago`
18
+ return new Date(timestamp).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
19
+ }
20
+
21
+ export function UploadScreen() {
22
+ const { uploads, addUpload, removeUpload, clearUploads } = useMedia()
23
+
24
+ useDrawerHeader({ title: 'Upload' })
25
+
26
+ function handleFiles(files: File[]) {
27
+ files.forEach((file) => {
28
+ addUpload(file.name, formatSize(file.size), file.type || 'unknown')
29
+ })
30
+ }
31
+
32
+ return (
33
+ <main className="px-3 pt-4 pb-8 space-y-3">
34
+ <ScreenHeader
35
+ title="Upload"
36
+ subtitle="Add media to your library"
37
+ actions={
38
+ uploads.length > 0 ? (
39
+ <Button variant="ghost" size="sm" onClick={clearUploads}>
40
+ Clear All
41
+ </Button>
42
+ ) : undefined
43
+ }
44
+ />
45
+
46
+ <FileUpload
47
+ onFiles={handleFiles}
48
+ accept="image/*,audio/*,video/*"
49
+ multiple
50
+ label="Drop media files or tap to browse"
51
+ />
52
+
53
+ {uploads.length === 0 ? (
54
+ <EmptyState
55
+ icon={<IcEditUploadToCloud width={48} height={48} />}
56
+ title="No uploads yet"
57
+ description="Use the drop zone above to add photos, audio, or video files to your media library."
58
+ />
59
+ ) : (
60
+ <div>
61
+ <p className="text-[13px] tracking-[-0.13px] text-text-dim mb-2">
62
+ Recent Uploads ({uploads.length})
63
+ </p>
64
+ <Card className="rounded-[6px] overflow-hidden divide-y divide-border">
65
+ {uploads.map((upload) => (
66
+ <ListItem
67
+ key={upload.id}
68
+ title={upload.name}
69
+ subtitle={`${upload.size} \u00b7 ${upload.type}`}
70
+ trailing={
71
+ <div className="flex items-center gap-2">
72
+ <span className="text-[11px] tracking-[-0.11px] text-text-dim">
73
+ {formatTime(upload.timestamp)}
74
+ </span>
75
+ <Button
76
+ variant="ghost"
77
+ size="icon"
78
+ onClick={(e) => {
79
+ e.stopPropagation()
80
+ removeUpload(upload.id)
81
+ }}
82
+ className="text-text-dim hover:text-negative"
83
+ >
84
+ <IcEditTrash width={16} height={16} />
85
+ </Button>
86
+ </div>
87
+ }
88
+ />
89
+ ))}
90
+ </Card>
91
+ </div>
92
+ )}
93
+ </main>
94
+ )
95
+ }
@@ -0,0 +1,29 @@
1
+ export type MediaCategory = 'Photos' | 'Artwork' | 'Screenshots'
2
+
3
+ export interface GalleryItem {
4
+ id: string
5
+ title: string
6
+ category: MediaCategory
7
+ date: string
8
+ gradient: string
9
+ }
10
+
11
+ export interface AudioTrack {
12
+ id: string
13
+ title: string
14
+ artist: string
15
+ duration: string
16
+ durationSeconds: number
17
+ }
18
+
19
+ export interface UploadItem {
20
+ id: string
21
+ name: string
22
+ size: string
23
+ type: string
24
+ timestamp: number
25
+ }
26
+
27
+ export const CATEGORIES: MediaCategory[] = ['Photos', 'Artwork', 'Screenshots']
28
+ export const ALL_FILTER = 'All'
29
+ export type CategoryFilter = typeof ALL_FILTER | MediaCategory
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "media",
3
+ "displayName": "Media Gallery",
4
+ "description": "A media gallery app with photo grid, audio player, file uploads, and glass display",
5
+ "tags": ["media", "gallery", "audio", "photos"],
6
+ "components": ["DrawerShell", "Card", "ListItem", "CategoryFilter", "BottomSheet", "ImageViewer", "AudioPlayer", "FileUpload", "EmptyState", "SettingsGroup", "Toggle", "Button", "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,50 @@
1
+ import { Routes, Route } from 'react-router'
2
+ import { AppShell, NavHeader, ScreenHeader, Card, Button, SectionHeader, ListItem } from 'even-toolkit/web'
3
+ import { AppGlasses } from './glass/AppGlasses'
4
+
5
+ function Home() {
6
+ return (
7
+ <AppShell header={<NavHeader title="{{DISPLAY_NAME}}" />}>
8
+ <div className="px-3 pt-4 pb-8 space-y-3">
9
+ <ScreenHeader
10
+ title="Welcome"
11
+ subtitle="Your G2 glasses app is ready"
12
+ />
13
+
14
+ <Card>
15
+ <ListItem
16
+ title="Get started"
17
+ subtitle="Edit src/App.tsx to build your app"
18
+ />
19
+ <ListItem
20
+ title="Components"
21
+ subtitle="55+ React components from even-toolkit"
22
+ />
23
+ <ListItem
24
+ title="Glasses SDK"
25
+ subtitle="G2 display, gestures, and speech-to-text"
26
+ />
27
+ </Card>
28
+
29
+ <SectionHeader title="Quick links" />
30
+ <div className="flex gap-2">
31
+ <Button variant="highlight" size="sm" onClick={() => window.open('https://www.npmjs.com/package/even-toolkit', '_blank')}>
32
+ Toolkit Docs
33
+ </Button>
34
+ <Button variant="highlight" size="sm" onClick={() => window.open('https://even-demo.vercel.app', '_blank')}>
35
+ Component Demo
36
+ </Button>
37
+ </div>
38
+ </div>
39
+ <AppGlasses />
40
+ </AppShell>
41
+ )
42
+ }
43
+
44
+ export function App() {
45
+ return (
46
+ <Routes>
47
+ <Route path="/*" element={<Home />} />
48
+ </Routes>
49
+ )
50
+ }
@@ -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
+ }