raayraay commited on
Commit
fbe0dd1
·
verified ·
1 Parent(s): 02114a6

create a figma material 3 expressive design for a smart journal with ai assistant all local app is called clarity

Browse files
Files changed (4) hide show
  1. README.md +7 -4
  2. index.html +337 -19
  3. script.js +528 -0
  4. style.css +94 -18
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
  title: Clarity1
3
- emoji: 🚀
4
- colorFrom: green
5
- colorTo: red
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
  title: Clarity1
3
+ colorFrom: pink
4
+ colorTo: green
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
index.html CHANGED
@@ -1,19 +1,337 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en" class="dark h-full">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Clarity1 Smart Journal</title>
7
+ <link rel="stylesheet" href="style.css" />
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
10
+ <script>
11
+ tailwind.config = {
12
+ darkMode: 'class',
13
+ theme: {
14
+ extend: {
15
+ fontFamily: {
16
+ sans: ['Inter', 'system-ui', 'sans-serif'],
17
+ },
18
+ boxShadow: {
19
+ md3: '0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15)',
20
+ md3lg: '0 4px 8px 3px rgb(0 0 0 / 0.25), 0 2px 3px -1px rgb(0 0 0 / 0.15)',
21
+ },
22
+ borderRadius: {
23
+ xl4: '1.5rem',
24
+ xl5: '2rem',
25
+ },
26
+ }
27
+ }
28
+ }
29
+ </script>
30
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
31
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
32
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
33
+ </head>
34
+ <body class="h-full bg-surface text-onSurface font-sans antialiased selection:bg-primary/30 selection:text-onPrimary">
35
+ <div id="app" class="min-h-full">
36
+ <!-- Top App Bar -->
37
+ <header class="sticky top-0 z-40 border-b border-outlineVariant/30 bg-surface/80 backdrop-blur supports-[backdrop-filter]:bg-surface/60">
38
+ <div class="mx-auto max-w-[1400px] px-4 sm:px-6">
39
+ <div class="flex h-16 items-center gap-3">
40
+ <!-- App Title -->
41
+ <button id="sidebarToggle" class="inline-flex h-10 w-10 items-center justify-center rounded-full text-onSurfaceVariant hover:bg-surfaceVariant focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:ring-offset-surface" aria-label="Toggle navigation">
42
+ <i data-feather="menu" class="h-5 w-5"></i>
43
+ </button>
44
+ <div class="flex items-center gap-2">
45
+ <div class="inline-flex h-9 w-9 items-center justify-center rounded-xl bg-primary text-onPrimary shadow-md3">
46
+ <i data-feather="book-open" class="h-5 w-5"></i>
47
+ </div>
48
+ <div class="flex flex-col">
49
+ <h1 class="text-lg font-semibold tracking-tight text-onSurface">Clarity1</h1>
50
+ <span class="text-xs text-onSurfaceVariant">Smart Journal • Local AI</span>
51
+ </div>
52
+ </div>
53
+
54
+ <div class="ml-4 flex-1">
55
+ <div class="relative max-w-xl">
56
+ <i data-feather="search" class="pointer-events-none absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-onSurfaceVariant"></i>
57
+ <input id="searchInput" type="search" placeholder="Search your entries..." class="w-full rounded-xl bg-surfaceVariant py-2.5 pl-10 pr-3 text-sm text-onSurfaceVariant placeholder-onSurfaceVariant/70 outline-none ring-1 ring-outlineVariant/40 focus:ring-2 focus:ring-primary" />
58
+ </div>
59
+ </div>
60
+
61
+ <div class="flex items-center gap-2">
62
+ <button id="composeBtn" class="inline-flex items-center gap-2 rounded-xl bg-primary px-4 py-2.5 text-sm font-semibold text-onPrimary shadow-md3 hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:ring-offset-surface">
63
+ <i data-feather="edit-3" class="h-4 w-4"></i>
64
+ New Entry
65
+ </button>
66
+
67
+ <div class="flex items-center rounded-xl bg-surfaceVariant p-1">
68
+ <button data-theme="light" class="theme-toggle inline-flex h-9 w-9 items-center justify-center rounded-lg text-onSurfaceVariant hover:bg-surface hover:text-onSurface">
69
+ <i data-feather="sun" class="h-4 w-4"></i>
70
+ </button>
71
+ <button data-theme="dark" class="theme-toggle inline-flex h-9 w-9 items-center justify-center rounded-lg bg-primary text-onPrimary">
72
+ <i data-feather="moon" class="h-4 w-4"></i>
73
+ </button>
74
+ </div>
75
+
76
+ <button id="profileBtn" class="ml-1 inline-flex h-10 w-10 items-center justify-center rounded-full text-onSurfaceVariant hover:bg-surfaceVariant">
77
+ <i data-feather="user" class="h-5 w-5"></i>
78
+ </button>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </header>
83
+
84
+ <!-- Layout -->
85
+ <div class="mx-auto max-w-[1400px] px-4 sm:px-6 py-6">
86
+ <div class="grid grid-cols-12 gap-6">
87
+ <!-- Sidebar -->
88
+ <aside id="sidebar" class="col-span-12 lg:col-span-3 xl:col-span-2">
89
+ <div class="rounded-2xl bg-surfaceVariant p-4">
90
+ <nav class="space-y-2">
91
+ <button data-nav="journal" class="nav-item w-full flex items-center gap-3 rounded-xl px-3 py-2.5 text-left font-medium text-onSurfaceVariant hover:bg-primary hover:text-onPrimary">
92
+ <i data-feather="book" class="h-5 w-5"></i>
93
+ Journal
94
+ </button>
95
+ <button data-nav="prompts" class="nav-item w-full flex items-center gap-3 rounded-xl px-3 py-2.5 text-left font-medium text-onSurfaceVariant hover:bg-primary hover:text-onPrimary">
96
+ <i data-feather="wand-2" class="h-5 w-5"></i>
97
+ Prompts
98
+ </button>
99
+ <button data-nav="chat" class="nav-item w-full flex items-center gap-3 rounded-xl px-3 py-2.5 text-left font-medium text-onSurfaceVariant hover:bg-primary hover:text-onPrimary">
100
+ <i data-feather="message-circle" class="h-5 w-5"></i>
101
+ AI Chat
102
+ </button>
103
+ <button data-nav="analytics" class="nav-item w-full flex items-center gap-3 rounded-xl px-3 py-2.5 text-left font-medium text-onSurfaceVariant hover:bg-primary hover:text-onPrimary">
104
+ <i data-feather="activity" class="h-5 w-5"></i>
105
+ Analytics
106
+ </button>
107
+ <button data-nav="settings" class="nav-item w-full flex items-center gap-3 rounded-xl px-3 py-2.5 text-left font-medium text-onSurfaceVariant hover:bg-primary hover:text-onPrimary">
108
+ <i data-feather="settings" class="h-5 w-5"></i>
109
+ Settings
110
+ </button>
111
+ </nav>
112
+ <div class="mt-6 rounded-xl bg-surface p-4 text-sm text-onSurfaceVariant">
113
+ <p class="mb-2 font-semibold text-onSurface">Tip</p>
114
+ <p>Use the compose button to start a new entry. AI stays local, no cloud required.</p>
115
+ </div>
116
+ </div>
117
+ </aside>
118
+
119
+ <!-- Main -->
120
+ <main class="col-span-12 lg:col-span-9 xl:col-span-10">
121
+ <div id="views">
122
+ <!-- Journal View -->
123
+ <section id="view-journal" class="space-y-6">
124
+ <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
125
+ <div>
126
+ <h2 class="text-2xl font-bold tracking-tight text-onSurface">Recent Entries</h2>
127
+ <p class="text-sm text-onSurfaceVariant">Capture thoughts and ideas effortlessly.</p>
128
+ </div>
129
+ <div class="flex gap-2">
130
+ <button id="viewModeGrid" class="inline-flex h-10 w-10 items-center justify-center rounded-xl text-onSurfaceVariant hover:bg-surfaceVariant">
131
+ <i data-feather="grid" class="h-5 w-5"></i>
132
+ </button>
133
+ <button id="viewModeList" class="inline-flex h-10 w-10 items-center justify-center rounded-xl text-onSurfaceVariant hover:bg-surfaceVariant">
134
+ <i data-feather="list" class="h-5 w-5"></i>
135
+ </button>
136
+ </div>
137
+ </div>
138
+
139
+ <div id="entriesGrid" class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3"></div>
140
+ <div id="entriesList" class="hidden space-y-3"></div>
141
+ </section>
142
+
143
+ <!-- Prompts View -->
144
+ <section id="view-prompts" class="hidden space-y-6">
145
+ <div>
146
+ <h2 class="text-2xl font-bold tracking-tight text-onSurface">Prompts</h2>
147
+ <p class="text-sm text-onSurfaceVariant">Jumpstart your writing with AI-curated prompts.</p>
148
+ </div>
149
+ <div id="promptsGrid" class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3"></div>
150
+ </section>
151
+
152
+ <!-- AI Chat View -->
153
+ <section id="view-chat" class="hidden">
154
+ <div class="mb-4">
155
+ <h2 class="text-2xl font-bold tracking-tight text-onSurface">AI Assistant</h2>
156
+ <p class="text-sm text-onSurfaceVariant">Ask anything. Everything runs locally on your device.</p>
157
+ </div>
158
+
159
+ <div class="grid grid-cols-1 gap-4 xl:grid-cols-3">
160
+ <div class="xl:col-span-2 rounded-2xl bg-surfaceVariant p-4">
161
+ <div id="chatMessages" class="mb-4 max-h-[460px] space-y-3 overflow-y-auto pr-2">
162
+ <!-- Messages -->
163
+ </div>
164
+ <form id="chatForm" class="flex items-end gap-2">
165
+ <textarea id="chatInput" rows="1" placeholder="Ask Clarity1 anything..." class="min-h-[44px] w-full resize-none rounded-xl bg-surface px-3 py-2.5 text-sm text-onSurface placeholder-onSurfaceVariant/70 outline-none ring-1 ring-outlineVariant/40 focus:ring-2 focus:ring-primary"></textarea>
166
+ <button type="submit" class="inline-flex h-11 items-center justify-center rounded-xl bg-primary px-4 font-semibold text-onPrimary shadow-md3 hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:ring-offset-surface">
167
+ <i data-feather="send" class="h-4 w-4"></i>
168
+ </button>
169
+ </form>
170
+ </div>
171
+ <div class="rounded-2xl bg-surfaceVariant p-4">
172
+ <h3 class="mb-2 font-semibold text-onSurface">Examples</h3>
173
+ <div class="space-y-2">
174
+ <button class="chat-example w-full rounded-xl bg-surface p-3 text-left text-sm text-onSurface hover:bg-surfaceVariant">Summarize my entries from last week</button>
175
+ <button class="chat-example w-full rounded-xl bg-surface p-3 text-left text-sm text-onSurface hover:bg-surfaceVariant">Give me three prompts about overcoming procrastination</button>
176
+ <button class="chat-example w-full rounded-xl bg-surface p-3 text-left text-sm text-onSurface hover:bg-surfaceVariant">Turn my draft into actionable steps</button>
177
+ </div>
178
+ </div>
179
+ </div>
180
+ </section>
181
+
182
+ <!-- Analytics View -->
183
+ <section id="view-analytics" class="hidden space-y-6">
184
+ <div>
185
+ <h2 class="text-2xl font-bold tracking-tight text-onSurface">Analytics</h2>
186
+ <p class="text-sm text-onSurfaceVariant">Reflect on your journaling cadence and themes.</p>
187
+ </div>
188
+
189
+ <div class="grid grid-cols-1 gap-4 md:grid-cols-3">
190
+ <div class="rounded-2xl bg-surfaceVariant p-6">
191
+ <div class="mb-2 text-sm text-onSurfaceVariant">Total Entries</div>
192
+ <div class="text-3xl font-bold text-onSurface" id="statTotal">0</div>
193
+ </div>
194
+ <div class="rounded-2xl bg-surfaceVariant p-6">
195
+ <div class="mb-2 text-sm text-onSurfaceVariant">This Week</div>
196
+ <div class="text-3xl font-bold text-onSurface" id="statWeek">0</div>
197
+ </div>
198
+ <div class="rounded-2xl bg-surfaceVariant p-6">
199
+ <div class="mb-2 text-sm text-onSurfaceVariant">Longest Streak</div>
200
+ <div class="text-3xl font-bold text-onSurface" id="statStreak">0</div>
201
+ </div>
202
+ </div>
203
+ </section>
204
+
205
+ <!-- Settings View -->
206
+ <section id="view-settings" class="hidden space-y-6">
207
+ <div>
208
+ <h2 class="text-2xl font-bold tracking-tight text-onSurface">Settings</h2>
209
+ <p class="text-sm text-onSurfaceVariant">Customize your experience.</p>
210
+ </div>
211
+ <div class="rounded-2xl bg-surfaceVariant p-6">
212
+ <div class="mb-4 flex items-center justify-between">
213
+ <div>
214
+ <div class="font-semibold text-onSurface">Theme</div>
215
+ <div class="text-sm text-onSurfaceVariant">Switch between light and dark modes.</div>
216
+ </div>
217
+ <div class="flex items-center rounded-xl bg-surface p-1">
218
+ <button data-theme="light" class="theme-toggle inline-flex h-9 w-9 items-center justify-center rounded-lg text-onSurfaceVariant hover:bg-surfaceVariant hover:text-onSurface">
219
+ <i data-feather="sun" class="h-4 w-4"></i>
220
+ </button>
221
+ <button data-theme="dark" class="theme-toggle inline-flex h-9 w-9 items-center justify-center rounded-lg bg-primary text-onPrimary">
222
+ <i data-feather="moon" class="h-4 w-4"></i>
223
+ </button>
224
+ </div>
225
+ </div>
226
+ <div class="flex items-center justify-between">
227
+ <div>
228
+ <div class="font-semibold text-onSurface">AI Model</div>
229
+ <div class="text-sm text-onSurfaceVariant">Runs fully local; no data leaves your device.</div>
230
+ </div>
231
+ <span class="rounded-full bg-surface px-3 py-1 text-xs text-onSurface">Local Only</span>
232
+ </div>
233
+ </div>
234
+ </section>
235
+ </div>
236
+ </main>
237
+ </div>
238
+ </div>
239
+ </div>
240
+
241
+ <!-- Compose Modal -->
242
+ <div id="composeModal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black/50 p-4">
243
+ <div class="w-full max-w-3xl overflow-hidden rounded-3xl bg-surface shadow-md3lg ring-1 ring-outlineVariant/20">
244
+ <div class="flex items-center justify-between border-b border-outlineVariant/30 px-6 py-4">
245
+ <div class="flex items-center gap-2">
246
+ <div class="inline-flex h-9 w-9 items-center justify-center rounded-lg bg-primary text-onPrimary">
247
+ <i data-feather="feather" class="h-4 w-4"></i>
248
+ </div>
249
+ <h3 class="text-lg font-semibold text-onSurface">New Entry</h3>
250
+ </div>
251
+ <button id="closeCompose" class="inline-flex h-10 w-10 items-center justify-center rounded-xl text-onSurfaceVariant hover:bg-surfaceVariant">
252
+ <i data-feather="x" class="h-5 w-5"></i>
253
+ </button>
254
+ </div>
255
+ <form id="composeForm" class="p-6 space-y-4">
256
+ <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
257
+ <div class="space-y-2">
258
+ <label class="text-sm font-medium text-onSurface">Title</label>
259
+ <input id="entryTitle" type="text" placeholder="Give your entry a title" class="w-full rounded-xl bg-surfaceVariant px-3 py-2.5 text-sm text-onSurface placeholder-onSurfaceVariant/70 outline-none ring-1 ring-outlineVariant/40 focus:ring-2 focus:ring-primary" />
260
+ </div>
261
+ <div class="space-y-2">
262
+ <label class="text-sm font-medium text-onSurface">Mood</label>
263
+ <select id="entryMood" class="w-full rounded-xl bg-surfaceVariant px-3 py-2.5 text-sm text-onSurface outline-none ring-1 ring-outlineVariant/40 focus:ring-2 focus:ring-primary">
264
+ <option>😊 Calm</option>
265
+ <option>🎯 Focused</option>
266
+ <option>🌟 Grateful</option>
267
+ <option>💡 Insight</option>
268
+ <option>🌧️ Low</option>
269
+ </select>
270
+ </div>
271
+ </div>
272
+ <div class="space-y-2">
273
+ <label class="text-sm font-medium text-onSurface">Content</label>
274
+ <textarea id="entryContent" rows="8" placeholder="Start writing..." class="w-full resize-none rounded-xl bg-surfaceVariant px-3 py-2.5 text-sm text-onSurface placeholder-onSurfaceVariant/70 outline-none ring-1 ring-outlineVariant/40 focus:ring-2 focus:ring-primary"></textarea>
275
+ </div>
276
+ <div class="flex items-center justify-between gap-2">
277
+ <div class="flex items-center gap-3 text-sm text-onSurfaceVariant">
278
+ <span>Tags:</span>
279
+ <input id="entryTags" type="text" placeholder="comma, separated, tags" class="w-64 rounded-xl bg-surfaceVariant px-3 py-2 text-sm text-onSurface placeholder-onSurfaceVariant/70 outline-none ring-1 ring-outlineVariant/40 focus:ring-2 focus:ring-primary" />
280
+ </div>
281
+ <div class="flex items-center gap-2">
282
+ <button type="button" id="aiAssistBtn" class="inline-flex items-center gap-2 rounded-xl bg-secondary px-4 py-2.5 text-sm font-semibold text-onSecondary shadow-md3 hover:bg-secondary/90 focus:outline-none focus:ring-2 focus:ring-secondary focus:ring-offset-2 focus:ring-offset-surface">
283
+ <i data-feather="sparkles" class="h-4 w-4"></i>
284
+ AI Assist
285
+ </button>
286
+ <button type="submit" class="inline-flex items-center gap-2 rounded-xl bg-primary px-4 py-2.5 text-sm font-semibold text-onPrimary shadow-md3 hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:ring-offset-surface">
287
+ <i data-feather="save" class="h-4 w-4"></i>
288
+ Save Entry
289
+ </button>
290
+ </div>
291
+ </div>
292
+ </form>
293
+ </div>
294
+ </div>
295
+
296
+ <!-- Entry Modal -->
297
+ <div id="entryModal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black/50 p-4">
298
+ <div class="w-full max-w-3xl overflow-hidden rounded-3xl bg-surface shadow-md3lg ring-1 ring-outlineVariant/20">
299
+ <div class="flex items-center justify-between border-b border-outlineVariant/30 px-6 py-4">
300
+ <div class="flex items-center gap-2">
301
+ <div class="inline-flex h-9 w-9 items-center justify-center rounded-lg bg-primary text-onPrimary">
302
+ <i data-feather="file-text" class="h-4 w-4"></i>
303
+ </div>
304
+ <h3 id="entryModalTitle" class="text-lg font-semibold text-onSurface">Entry</h3>
305
+ </div>
306
+ <div class="flex items-center gap-2">
307
+ <button id="deleteEntryBtn" class="inline-flex items-center gap-2 rounded-xl bg-error px-3 py-2 text-sm font-semibold text-onError hover:bg-error/90 focus:outline-none focus:ring-2 focus:ring-error focus:ring-offset-2 focus:ring-offset-surface">
308
+ <i data-feather="trash-2" class="h-4 w-4"></i>
309
+ Delete
310
+ </button>
311
+ <button id="closeEntry" class="inline-flex h-10 w-10 items-center justify-center rounded-xl text-onSurfaceVariant hover:bg-surfaceVariant">
312
+ <i data-feather="x" class="h-5 w-5"></i>
313
+ </button>
314
+ </div>
315
+ </div>
316
+ <div class="space-y-4 p-6">
317
+ <div class="flex items-center gap-3 text-sm text-onSurfaceVariant">
318
+ <span id="entryModalDate"></span>
319
+ <span>•</span>
320
+ <span id="entryModalMood"></span>
321
+ <span>•</span>
322
+ <span id="entryModalTags"></span>
323
+ </div>
324
+ <div id="entryModalContent" class="prose prose-invert max-w-none text-onSurface">
325
+ <!-- Content -->
326
+ </div>
327
+ </div>
328
+ </div>
329
+ </div>
330
+
331
+ <script src="script.js"></script>
332
+ <script>
333
+ feather.replace();
334
+ </script>
335
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
336
+ </body>
337
+ </html>
script.js ADDED
@@ -0,0 +1,528 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Clarity1 — Smart Journal (Local)
2
+ const storeKey = 'clarity1_entries_v1';
3
+ const chatKey = 'clarity1_chat_v1';
4
+
5
+ const els = {
6
+ sidebar: document.getElementById('sidebar'),
7
+ sidebarToggle: document.getElementById('sidebarToggle'),
8
+ searchInput: document.getElementById('searchInput'),
9
+ composeBtn: document.getElementById('composeBtn'),
10
+ composeModal: document.getElementById('composeModal'),
11
+ composeForm: document.getElementById('composeForm'),
12
+ entryTitle: document.getElementById('entryTitle'),
13
+ entryMood: document.getElementById('entryMood'),
14
+ entryContent: document.getElementById('entryContent'),
15
+ entryTags: document.getElementById('entryTags'),
16
+ aiAssistBtn: document.getElementById('aiAssistBtn'),
17
+ closeCompose: document.getElementById('closeCompose'),
18
+
19
+ entriesGrid: document.getElementById('entriesGrid'),
20
+ entriesList: document.getElementById('entriesList'),
21
+ viewModeGrid: document.getElementById('viewModeGrid'),
22
+ viewModeList: document.getElementById('viewModeList'),
23
+
24
+ viewJournal: document.getElementById('view-journal'),
25
+ viewPrompts: document.getElementById('view-prompts'),
26
+ viewChat: document.getElementById('view-chat'),
27
+ viewAnalytics: document.getElementById('view-analytics'),
28
+ viewSettings: document.getElementById('view-settings'),
29
+ promptsGrid: document.getElementById('promptsGrid'),
30
+
31
+ chatMessages: document.getElementById('chatMessages'),
32
+ chatForm: document.getElementById('chatForm'),
33
+ chatInput: document.getElementById('chatInput'),
34
+
35
+ statTotal: document.getElementById('statTotal'),
36
+ statWeek: document.getElementById('statWeek'),
37
+ statStreak: document.getElementById('statStreak'),
38
+
39
+ entryModal: document.getElementById('entryModal'),
40
+ entryModalTitle: document.getElementById('entryModalTitle'),
41
+ entryModalDate: document.getElementById('entryModalDate'),
42
+ entryModalMood: document.getElementById('entryModalMood'),
43
+ entryModalTags: document.getElementById('entryModalTags'),
44
+ entryModalContent: document.getElementById('entryModalContent'),
45
+ deleteEntryBtn: document.getElementById('deleteEntryBtn'),
46
+ closeEntry: document.getElementById('closeEntry'),
47
+ };
48
+
49
+ let state = {
50
+ entries: [],
51
+ selectedEntryId: null,
52
+ view: 'journal',
53
+ mode: 'grid', // or 'list'
54
+ chat: [],
55
+ };
56
+
57
+ const promptsCatalog = [
58
+ { title: 'Values Check', body: 'What matters most to me this week?', tags: ['reflection', 'values'] },
59
+ { title: 'Peak Moment', body: 'Describe a high point today and why it resonated.', tags: ['gratitude'] },
60
+ { title: 'Friction Map', body: 'What’s one friction point I can reduce tomorrow?', tags: ['action'] },
61
+ { title: 'Anchor Habit', body: 'If I had to pick one habit to do daily, what is it?', tags: ['habit'] },
62
+ { title: 'Perspective Shift', body: 'What assumption am I ready to challenge?', tags: ['growth'] },
63
+ { title: 'Energy Audit', body: 'When do I feel most alive?', tags: ['energy'] },
64
+ ];
65
+
66
+ function loadEntries() {
67
+ try {
68
+ const data = JSON.parse(localStorage.getItem(storeKey) || '[]');
69
+ if (Array.isArray(data)) state.entries = data;
70
+ } catch (e) {
71
+ console.warn('Failed to load entries', e);
72
+ state.entries = [];
73
+ }
74
+ }
75
+
76
+ function saveEntries() {
77
+ localStorage.setItem(storeKey, JSON.stringify(state.entries));
78
+ }
79
+
80
+ function loadChat() {
81
+ try {
82
+ const data = JSON.parse(localStorage.getItem(chatKey) || '[]');
83
+ if (Array.isArray(data)) state.chat = data;
84
+ } catch {
85
+ state.chat = [];
86
+ }
87
+ }
88
+
89
+ function saveChat() {
90
+ localStorage.setItem(chatKey, JSON.stringify(state.chat));
91
+ }
92
+
93
+ function uid() {
94
+ return Math.random().toString(36).slice(2) + Date.now().toString(36);
95
+ }
96
+
97
+ function formatDate(ts) {
98
+ const d = new Date(ts);
99
+ return d.toLocaleString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });
100
+ }
101
+
102
+ function renderNav(active) {
103
+ document.querySelectorAll('.nav-item').forEach(btn => {
104
+ btn.classList.toggle('bg-primary', btn.dataset.nav === active);
105
+ btn.classList.toggle('text-onPrimary', btn.dataset.nav === active);
106
+ });
107
+ }
108
+
109
+ function showView(name) {
110
+ state.view = name;
111
+ els.viewJournal.classList.toggle('hidden', name !== 'journal');
112
+ els.viewPrompts.classList.toggle('hidden', name !== 'prompts');
113
+ els.viewChat.classList.toggle('hidden', name !== 'chat');
114
+ els.viewAnalytics.classList.toggle('hidden', name !== 'analytics');
115
+ els.viewSettings.classList.toggle('hidden', name !== 'settings');
116
+ renderNav(name);
117
+
118
+ if (name === 'journal') {
119
+ renderEntries();
120
+ updateAnalytics();
121
+ }
122
+ if (name === 'prompts') {
123
+ renderPrompts();
124
+ }
125
+ if (name === 'chat') {
126
+ renderChat();
127
+ }
128
+ if (name === 'analytics') {
129
+ updateAnalytics();
130
+ }
131
+ }
132
+
133
+ function entryCard(e) {
134
+ const tags = (e.tags || []).slice(0, 3).map(t => `#${t}`).join(' ');
135
+ return `
136
+ <article class="card animate-fadeUp overflow-hidden">
137
+ <div class="p-4 space-y-3">
138
+ <div class="flex items-start justify-between gap-2">
139
+ <h3 class="text-lg font-semibold text-onSurface">${escapeHtml(e.title || 'Untitled')}</h3>
140
+ <span class="rounded-full bg-surface-container-high px-2.5 py-1 text-xs text-onSurfaceVariant">${e.mood}</span>
141
+ </div>
142
+ <p class="text-sm text-onSurfaceVariant line-clamp-3">${escapeHtml((e.content || '').slice(0, 200))}</p>
143
+ <div class="mt-2 flex items-center justify-between">
144
+ <span class="text-xs text-onSurfaceVariant">${formatDate(e.createdAt)}</span>
145
+ <div class="flex items-center gap-2 text-onSurfaceVariant">
146
+ <span class="text-xs">${tags}</span>
147
+ <button data-view="${e.id}" class="inline-flex h-8 w-8 items-center justify-center rounded-lg hover:bg-surface-variant text-onSurfaceVariant hover:text-onSurface">
148
+ <i data-feather="eye" class="h-4 w-4"></i>
149
+ </button>
150
+ </div>
151
+ </div>
152
+ </div>
153
+ </article>
154
+ `;
155
+ }
156
+
157
+ function entryRow(e) {
158
+ const tags = (e.tags || []).map(t => `#${t}`).join(' ');
159
+ return `
160
+ <div class="card p-4">
161
+ <div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
162
+ <div class="min-w-0">
163
+ <div class="flex items-center gap-2">
164
+ <h3 class="truncate text-lg font-semibold text-onSurface">${escapeHtml(e.title || 'Untitled')}</h3>
165
+ <span class="rounded-full bg-surface-container-high px-2.5 py-1 text-xs text-onSurfaceVariant">${e.mood}</span>
166
+ </div>
167
+ <p class="truncate text-sm text-onSurfaceVariant">${escapeHtml(e.content || '')}</p>
168
+ </div>
169
+ <div class="flex items-center gap-3">
170
+ <span class="text-xs text-onSurfaceVariant">${formatDate(e.createdAt)}</span>
171
+ <span class="text-xs text-onSurfaceVariant">${tags}</span>
172
+ <button data-view="${e.id}" class="inline-flex h-8 w-8 items-center justify-center rounded-lg hover:bg-surface-variant text-onSurfaceVariant hover:text-onSurface">
173
+ <i data-feather="eye" class="h-4 w-4"></i>
174
+ </button>
175
+ </div>
176
+ </div>
177
+ </div>
178
+ `;
179
+ }
180
+
181
+ function renderEntries() {
182
+ const q = (els.searchInput.value || '').toLowerCase().trim();
183
+ const list = state.entries
184
+ .slice()
185
+ .sort((a, b) => b.createdAt - a.createdAt)
186
+ .filter(e => {
187
+ if (!q) return true;
188
+ return (
189
+ (e.title || '').toLowerCase().includes(q) ||
190
+ (e.content || '').toLowerCase().includes(q) ||
191
+ (e.tags || []).some(t => t.toLowerCase().includes(q))
192
+ );
193
+ });
194
+
195
+ if (state.mode === 'grid') {
196
+ els.entriesGrid.classList.remove('hidden');
197
+ els.entriesList.classList.add('hidden');
198
+ els.entriesGrid.innerHTML = list.map(entryCard).join('') || `
199
+ <div class="card p-10 text-center text-onSurfaceVariant">
200
+ <i data-feather="inbox" class="mx-auto h-6 w-6 mb-2"></i>
201
+ No entries yet. Create your first one!
202
+ </div>`;
203
+ } else {
204
+ els.entriesGrid.classList.add('hidden');
205
+ els.entriesList.classList.remove('hidden');
206
+ els.entriesList.innerHTML = list.map(entryRow).join('') || `
207
+ <div class="card p-10 text-center text-onSurfaceVariant">
208
+ <i data-feather="inbox" class="mx-auto h-6 w-6 mb-2"></i>
209
+ No entries yet. Create your first one!
210
+ </div>`;
211
+ }
212
+
213
+ // Re-bind icons in dynamically injected DOM
214
+ if (window.feather) feather.replace();
215
+ // Bind view buttons
216
+ document.querySelectorAll('[data-view]').forEach(btn => {
217
+ btn.addEventListener('click', () => openEntry(btn.getAttribute('data-view')));
218
+ });
219
+ }
220
+
221
+ function renderPrompts() {
222
+ els.promptsGrid.innerHTML = promptsCatalog.map(p => `
223
+ <div class="card p-5">
224
+ <h3 class="text-lg font-semibold text-onSurface">${p.title}</h3>
225
+ <p class="mt-2 text-sm text-onSurfaceVariant">${p.body}</p>
226
+ <div class="mt-3 flex items-center justify-between">
227
+ <div class="text-xs text-onSurfaceVariant">${(p.tags || []).map(t => `#${t}`).join(' ')}</div>
228
+ <button data-use-prompt="${encodeURIComponent(JSON.stringify(p))}" class="inline-flex items-center gap-2 rounded-xl bg-primary px-3 py-1.5 text-sm font-semibold text-onPrimary shadow-md3 hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:ring-offset-surface">
229
+ <i data-feather="arrow-right" class="h-4 w-4"></i> Use
230
+ </button>
231
+ </div>
232
+ </div>
233
+ `).join('');
234
+ if (window.feather) feather.replace();
235
+
236
+ document.querySelectorAll('[data-use-prompt]').forEach(btn => {
237
+ btn.addEventListener('click', () => {
238
+ const p = JSON.parse(decodeURIComponent(btn.getAttribute('data-use-prompt')));
239
+ openCompose({ preset: p });
240
+ });
241
+ });
242
+ }
243
+
244
+ function openCompose({ preset } = {}) {
245
+ if (preset) {
246
+ els.entryTitle.value = preset.title || '';
247
+ els.entryContent.value = `${preset.body}\n\n`;
248
+ els.entryTags.value = (preset.tags || []).join(', ');
249
+ } else {
250
+ els.composeForm.reset();
251
+ }
252
+ els.composeModal.classList.remove('hidden');
253
+ els.composeModal.classList.add('flex');
254
+ }
255
+
256
+ function closeCompose() {
257
+ els.composeModal.classList.add('hidden');
258
+ els.composeModal.classList.remove('flex');
259
+ }
260
+
261
+ function openEntry(id) {
262
+ const entry = state.entries.find(e => e.id === id);
263
+ if (!entry) return;
264
+ state.selectedEntryId = id;
265
+ els.entryModalTitle.textContent = entry.title || 'Untitled';
266
+ els.entryModalDate.textContent = formatDate(entry.createdAt);
267
+ els.entryModalMood.textContent = entry.mood;
268
+ els.entryModalTags.textContent = (entry.tags || []).map(t => `#${t}`).join(' ');
269
+ els.entryModalContent.innerHTML = markdownToHtml(entry.content || '');
270
+ els.entryModal.classList.remove('hidden');
271
+ els.entryModal.classList.add('flex');
272
+ }
273
+
274
+ function closeEntry() {
275
+ els.entryModal.classList.add('hidden');
276
+ els.entryModal.classList.remove('flex');
277
+ state.selectedEntryId = null;
278
+ }
279
+
280
+ function renderChat() {
281
+ els.chatMessages.innerHTML = state.chat.map(m => `
282
+ <div class="flex items-start gap-3 ${m.role === 'assistant' ? '' : 'justify-end'}">
283
+ ${m.role === 'assistant' ? `
284
+ <div class="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-secondary text-onSecondary">
285
+ <i data-feather="cpu" class="h-4 w-4"></i>
286
+ </div>
287
+ ` : `
288
+ <div class="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-primary text-onPrimary">
289
+ <i data-feather="user" class="h-4 w-4"></i>
290
+ </div>
291
+ `}
292
+ <div class="max-w-[85%] rounded-2xl px-4 py-2.5 ${m.role === 'assistant' ? 'bg-surface-container-high text-onSurface' : 'bg-primary text-onPrimary'}">
293
+ <div class="text-sm whitespace-pre-wrap">${escapeHtml(m.content)}</div>
294
+ </div>
295
+ </div>
296
+ `).join('') || `
297
+ <div class="text-center text-onSurfaceVariant">
298
+ <i data-feather="message-square" class="mx-auto h-6 w-6"></i>
299
+ Ask Clarity1 to reflect on your journal.
300
+ </div>
301
+ `;
302
+ if (window.feather) feather.replace();
303
+ els.chatMessages.scrollTop = els.chatMessages.scrollHeight;
304
+ }
305
+
306
+ function aiAssistCompose(content, mood, tags) {
307
+ // Lightweight local "AI" assistant for compose window
308
+ const base = content.trim();
309
+ const suggestions = [
310
+ 'You might expand this with a concrete next step.',
311
+ 'Try adding an example that illustrates your point.',
312
+ 'Consider noting one thing you’re grateful for related to this.',
313
+ ];
314
+ const pick = () => suggestions[Math.floor(Math.random() * suggestions.length)];
315
+ return `
316
+ Here’s a polished version of your entry:
317
+
318
+ ${base}
319
+
320
+ Suggestions:
321
+ - ${pick()}
322
+ - ${pick()}
323
+ - ${pick()}
324
+
325
+ Mood matched: ${mood}. Tags suggested: ${(tags || []).slice(0, 4).join(', ') || 'journal, reflection'}
326
+ `.trim();
327
+ }
328
+
329
+ function aiChatRespond(message) {
330
+ const m = message.trim();
331
+ if (!m) return 'Please enter a message.';
332
+ if (/summar/i.test(m)) {
333
+ return summarizeEntries();
334
+ }
335
+ if (/prompt/i.test(m)) {
336
+ return 'Here are three prompts to help you reflect:\n1) What energized me today?\n2) What did I avoid, and why?\n3) What assumption am I willing to revisit?';
337
+ }
338
+ if (/procrastinat/i.test(m)) {
339
+ return 'A quick approach:\n- Name the task in one sentence.\n- Identify the tiniest next action.\n- Timebox 10 minutes and start.';
340
+ }
341
+ if (/action|next|steps/i.test(m)) {
342
+ return 'Turn your draft into steps:\n1) Define the outcome.\n2) List the smallest actionable task.\n3) Assign a time window.';
343
+ }
344
+ if (/last week|week/i.test(m)) {
345
+ return summarizeEntries();
346
+ }
347
+ return localFallback(m);
348
+ }
349
+
350
+ function localFallback(msg) {
351
+ const rules = [
352
+ { k: /hello|hi/i, r: 'Hello! Ask me to summarize your week, generate prompts, or refine a draft.' },
353
+ { k: /help/i, r: 'Try: “Summarize my entries from last week”, “Give me three prompts”, or “Turn my draft into steps”.' },
354
+ ];
355
+ for (const { k, r } of rules) if (k.test(msg)) return r;
356
+ return `I heard: “${msg}”. I can help summarize your entries or turn thoughts into steps.`;
357
+ }
358
+
359
+ function summarizeEntries() {
360
+ const total = state.entries.length;
361
+ if (!total) return 'No entries to summarize yet.';
362
+ const last7 = state.entries.filter(e => Date.now() - e.createdAt <= 7 * 86400000);
363
+ const words = state.entries.reduce((acc, e) => acc + (e.content || '').split(/\s+/).filter(Boolean).length, 0);
364
+ const moods = state.entries.reduce((acc, e) => {
365
+ const m = (e.mood || '').split(' ')[0];
366
+ acc[m] = (acc[m] || 0) + 1;
367
+ return acc;
368
+ }, {});
369
+ const moodTop = Object.entries(moods).sort((a,b) => b[1]-a[1])[0]?.[0] || 'Unknown';
370
+
371
+ return `Summary:
372
+ - ${total} total entries
373
+ - ${last7.length} entries in the last 7 days
374
+ - Common mood: ${moodTop}
375
+ - Estimated total words: ${words}`;
376
+ }
377
+
378
+ function updateAnalytics() {
379
+ const total = state.entries.length;
380
+ const week = state.entries.filter(e => Date.now() - e.createdAt <= 7 * 86400000).length;
381
+
382
+ // Simple streak: count consecutive days with entries ending today
383
+ const days = new Set(state.entries.map(e => new Date(e.createdAt).toDateString()));
384
+ let streak = 0;
385
+ let cursor = new Date();
386
+ while (days.has(cursor.toDateString())) {
387
+ streak += 1;
388
+ cursor.setDate(cursor.getDate() - 1);
389
+ }
390
+
391
+ els.statTotal.textContent = total;
392
+ els.statWeek.textContent = week;
393
+ els.statStreak.textContent = streak;
394
+ }
395
+
396
+ function escapeHtml(s) {
397
+ return (s || '').replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
398
+ }
399
+
400
+ function markdownToHtml(s) {
401
+ // Very small subset: headings, bold, italics, lists, line breaks
402
+ return escapeHtml(s)
403
+ .replace(/^### (.*$)/gim, '<h3 class="text-base font-semibold mt-4">$1</h3>')
404
+ .replace(/^## (.*$)/gim, '<h2 class="text-lg font-semibold mt-4">$1</h2>')
405
+ .replace(/^# (.*$)/gim, '<h1 class="text-xl font-semibold mt-4">$1</h1>')
406
+ .replace(/\*\*(.*?)\*\*/gim, '<strong class="font-semibold">$1</strong>')
407
+ .replace(/\*(.*?)\*/gim, '<em class="italic">$1</em>')
408
+ .replace(/\n$/gim, '<br />')
409
+ .replace(/\n/gim, '<br />');
410
+ }
411
+
412
+ // Theme toggle
413
+ function setTheme(t) {
414
+ document.documentElement.classList.toggle('dark', t === 'dark');
415
+ localStorage.setItem('clarity1_theme', t);
416
+ }
417
+ function initTheme() {
418
+ const saved = localStorage.getItem('clarity1_theme');
419
+ if (saved) {
420
+ setTheme(saved);
421
+ } else {
422
+ setTheme('dark'); // enforce dark as requested
423
+ }
424
+ }
425
+
426
+ function bindEvents() {
427
+ els.sidebarToggle.addEventListener('click', () => {
428
+ els.sidebar.classList.toggle('hidden');
429
+ });
430
+
431
+ document.querySelectorAll('.nav-item').forEach(btn => {
432
+ btn.addEventListener('click', () => showView(btn.dataset.nav));
433
+ });
434
+
435
+ els.searchInput.addEventListener('input', renderEntries);
436
+ els.viewModeGrid.addEventListener('click', () => {
437
+ state.mode = 'grid';
438
+ renderEntries();
439
+ });
440
+ els.viewModeList.addEventListener('click', () => {
441
+ state.mode = 'list';
442
+ renderEntries();
443
+ });
444
+
445
+ els.composeBtn.addEventListener('click', () => openCompose());
446
+ els.closeCompose.addEventListener('click', closeCompose);
447
+
448
+ els.composeForm.addEventListener('submit', (e) => {
449
+ e.preventDefault();
450
+ const entry = {
451
+ id: uid(),
452
+ title: els.entryTitle.value.trim(),
453
+ mood: els.entryMood.value,
454
+ content: els.entryContent.value,
455
+ tags: els.entryTags.value.split(',').map(s => s.trim()).filter(Boolean),
456
+ createdAt: Date.now(),
457
+ };
458
+ state.entries.unshift(entry);
459
+ saveEntries();
460
+ closeCompose();
461
+ renderEntries();
462
+ updateAnalytics();
463
+ });
464
+
465
+ els.aiAssistBtn.addEventListener('click', () => {
466
+ const suggestion = aiAssistCompose(
467
+ els.entryContent.value,
468
+ els.entryMood.value,
469
+ els.entryTags.value.split(',').map(s => s.trim()).filter(Boolean)
470
+ );
471
+ els.entryContent.value = suggestion;
472
+ });
473
+
474
+ els.chatForm.addEventListener('submit', (e) => {
475
+ e.preventDefault();
476
+ const text = els.chatInput.value.trim();
477
+ if (!text) return;
478
+ state.chat.push({ role: 'user', content: text });
479
+ els.chatInput.value = '';
480
+ saveChat();
481
+ renderChat();
482
+
483
+ setTimeout(() => {
484
+ const reply = aiChatRespond(text);
485
+ state.chat.push({ role: 'assistant', content: reply });
486
+ saveChat();
487
+ renderChat();
488
+ }, 300);
489
+ });
490
+
491
+ document.querySelectorAll('.chat-example').forEach(btn => {
492
+ btn.addEventListener('click', () => {
493
+ els.chatInput.value = btn.textContent.trim();
494
+ els.chatForm.dispatchEvent(new Event('submit'));
495
+ });
496
+ });
497
+
498
+ document.querySelectorAll('.theme-toggle').forEach(btn => {
499
+ btn.addEventListener('click', () => setTheme(btn.dataset.theme));
500
+ });
501
+
502
+ els.deleteEntryBtn.addEventListener('click', () => {
503
+ if (!state.selectedEntryId) return;
504
+ if (confirm('Delete this entry?')) {
505
+ state.entries = state.entries.filter(e => e.id !== state.selectedEntryId);
506
+ saveEntries();
507
+ closeEntry();
508
+ renderEntries();
509
+ updateAnalytics();
510
+ }
511
+ });
512
+
513
+ els.closeEntry.addEventListener('click', closeEntry);
514
+ }
515
+
516
+ function boot() {
517
+ initTheme();
518
+ loadEntries();
519
+ loadChat();
520
+ bindEvents();
521
+ renderNav('journal');
522
+ renderEntries();
523
+ renderPrompts();
524
+ renderChat();
525
+ updateAnalytics();
526
+ }
527
+
528
+ document.addEventListener('DOMContentLoaded', boot);
style.css CHANGED
@@ -1,28 +1,104 @@
1
- body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  }
17
 
 
18
  .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
 
 
 
 
 
28
  }
 
 
 
 
 
 
 
 
 
 
1
+ /* Material 3 Expressive — Dark Theme Tokens for Clarity1 */
2
+ :root {
3
+ --brand-primary: #6750A4; /* Material 3 Tonal Primary ( expressive purple ) */
4
+ --brand-on-primary: #FFFFFF;
5
+ --brand-secondary: #625B71; /* Material 3 Tonal Secondary ( expressive neutral ) */
6
+ --brand-on-secondary: #FFFFFF;
7
+ --brand-tertiary: #7D5260;
8
+
9
+ /* Surfaces */
10
+ --md-sys-color-surface: #1B1B1F;
11
+ --md-sys-color-surface-variant: #2B2B31;
12
+ --md-sys-color-surface-container-low: #202026;
13
+ --md-sys-color-surface-container-high: #292931;
14
+
15
+ /* Text */
16
+ --md-sys-color-on-surface: #E3E1E6;
17
+ --md-sys-color-on-surface-variant: #C8C5D0;
18
+ --md-sys-color-outline: #56565E;
19
+ --md-sys-color-outline-variant: #696973;
20
+
21
+ /* Special */
22
+ --md-sys-color-error: #B3261E;
23
+ --md-sys-color-on-error: #FFFFFF;
24
+ --md-sys-color-scrim: #000000;
25
+ --md-sys-color-inverse-surface: #E6E0E9;
26
+ --md-sys-color-inverse-on-surface: #1B1B1F;
27
+ }
28
+
29
+ html.dark {
30
+ color-scheme: dark;
31
  }
32
 
33
+ body {
34
+ background: var(--md-sys-color-surface);
35
+ color: var(--md-sys-color-on-surface);
36
  }
37
 
38
+ .bg-surface { background-color: var(--md-sys-color-surface); }
39
+ .bg-surfaceVariant { background-color: var(--md-sys-color-surface-variant); }
40
+ .bg-surface-container-low { background-color: var(--md-sys-color-surface-container-low); }
41
+ .bg-surface-container-high { background-color: var(--md-sys-color-surface-container-high); }
42
+
43
+ .bg-primary { background-color: var(--brand-primary); }
44
+ .bg-onPrimary { color: var(--brand-on-primary); }
45
+ .text-onPrimary { color: var(--brand-on-primary); }
46
+
47
+ .bg-secondary { background-color: var(--brand-secondary); }
48
+ .text-onSecondary { color: var(--brand-on-secondary); }
49
+
50
+ .bg-error { background-color: var(--md-sys-color-error); }
51
+ .text-onError { color: var(--md-sys-color-on-error); }
52
+
53
+ .text-onSurface { color: var(--md-sys-color-on-surface); }
54
+ .text-onSurfaceVariant { color: var(--md-sys-color-on-surface-variant); }
55
+
56
+ .border-outlineVariant { border-color: var(--md-sys-color-outline-variant); }
57
+ .ring-outlineVariant\/40 { --tw-ring-color: color-mix(in srgb, var(--md-sys-color-outline-variant) 40%, transparent); }
58
+ .ring-outlineVariant\/20 { --tw-ring-color: color-mix(in srgb, var(--md-sys-color-outline-variant) 20%, transparent); }
59
+
60
+ .bg-scrim { background-color: var(--md-sys-color-scrim); }
61
+
62
+ .text-error { color: var(--md-sys-color-error); }
63
+
64
+ /* Scrollbars */
65
+ * {
66
+ scrollbar-width: thin;
67
+ scrollbar-color: var(--md-sys-color-outline) transparent;
68
+ }
69
+ *::-webkit-scrollbar { height: 8px; width: 8px; }
70
+ *::-webkit-scrollbar-track { background: transparent; }
71
+ *::-webkit-scrollbar-thumb {
72
+ background-color: var(--md-sys-color-outline);
73
+ border-radius: 9999px;
74
+ }
75
+ *::-webkit-scrollbar-thumb:hover {
76
+ background-color: var(--md-sys-color-outline-variant);
77
  }
78
 
79
+ /* Elevation-like surfaces for expressive flair */
80
  .card {
81
+ border-radius: 1.25rem;
82
+ background: var(--md-sys-color-surface);
83
+ border: 1px solid var(--md-sys-color-outline-variant);
84
+ box-shadow: 0 8px 24px rgba(0,0,0,0.28);
 
85
  }
86
 
87
+ /* Prose tweaks for readability in dark mode */
88
+ .prose :where(a, strong, em, code, pre, blockquote) { color: inherit; }
89
+ .prose :where(h1, h2, h3, h4) { color: var(--md-sys-color-on-surface); }
90
+
91
+ /* Animations */
92
+ @keyframes fadeUp {
93
+ from { opacity: 0; transform: translateY(8px); }
94
+ to { opacity: 1; transform: translateY(0); }
95
  }
96
+ .animate-fadeUp {
97
+ animation: fadeUp 240ms ease;
98
+ }
99
+
100
+ /* Focus rings for accessibility */
101
+ button:focus-visible, input:focus-visible, textarea:focus-visible, select:focus-visible {
102
+ outline: none;
103
+ box-shadow: 0 0 0 2px var(--brand-primary);
104
+ }