Skip to content

Commit 1a2f459

Browse files
committed
UI clean up
1 parent ec908af commit 1a2f459

11 files changed

Lines changed: 372 additions & 17 deletions

File tree

utilities/dashboard/app.js

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,144 @@ function setDockPinned(next) {
167167

168168
function ready(fn) { if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', fn); else fn(); }
169169

170+
function setupDarkTooltips() {
171+
if (typeof document === 'undefined') return;
172+
if (window.__chronosDarkTooltipsBound) return;
173+
window.__chronosDarkTooltipsBound = true;
174+
175+
const style = document.createElement('style');
176+
style.id = 'chronos-dark-tooltips-style';
177+
style.textContent = `
178+
.chronos-tooltip {
179+
position: fixed;
180+
z-index: 30000;
181+
max-width: min(320px, calc(100vw - 24px));
182+
padding: 8px 10px;
183+
border-radius: 10px;
184+
background: linear-gradient(180deg, rgba(15, 20, 29, 0.98) 0%, rgba(10, 14, 21, 0.98) 100%);
185+
color: var(--text, #e6e8ef);
186+
border: 1px solid rgba(255,255,255,0.12);
187+
box-shadow: 0 12px 32px rgba(0,0,0,0.42);
188+
font-size: 12px;
189+
line-height: 1.35;
190+
pointer-events: none;
191+
opacity: 0;
192+
transform: translateY(4px);
193+
transition: opacity 120ms ease, transform 120ms ease;
194+
backdrop-filter: blur(14px) saturate(120%);
195+
-webkit-backdrop-filter: blur(14px) saturate(120%);
196+
white-space: normal;
197+
}
198+
.chronos-tooltip.visible {
199+
opacity: 1;
200+
transform: translateY(0);
201+
}
202+
`;
203+
document.head.appendChild(style);
204+
205+
const tooltip = document.createElement('div');
206+
tooltip.className = 'chronos-tooltip';
207+
tooltip.setAttribute('role', 'tooltip');
208+
document.body.appendChild(tooltip);
209+
210+
let activeEl = null;
211+
212+
function getTooltipText(el) {
213+
if (!el) return '';
214+
const explicit = el.getAttribute('data-chronos-tooltip');
215+
if (explicit) return explicit;
216+
const label = el.getAttribute('title');
217+
if (label) return label;
218+
return '';
219+
}
220+
221+
function restoreTitle(el) {
222+
if (!el) return;
223+
const stored = el.getAttribute('data-chronos-tooltip-title');
224+
if (stored != null) {
225+
el.setAttribute('title', stored);
226+
el.removeAttribute('data-chronos-tooltip-title');
227+
}
228+
}
229+
230+
function suppressNativeTitle(el) {
231+
if (!el) return;
232+
if (el.hasAttribute('title')) {
233+
el.setAttribute('data-chronos-tooltip-title', el.getAttribute('title') || '');
234+
el.removeAttribute('title');
235+
}
236+
}
237+
238+
function positionTooltip(el) {
239+
if (!el || activeEl !== el) return;
240+
const rect = el.getBoundingClientRect();
241+
const pad = 10;
242+
const tipRect = tooltip.getBoundingClientRect();
243+
let left = rect.left + (rect.width / 2) - (tipRect.width / 2);
244+
left = Math.max(pad, Math.min(left, window.innerWidth - tipRect.width - pad));
245+
let top = rect.top - tipRect.height - 10;
246+
if (top < pad) top = rect.bottom + 10;
247+
if (top + tipRect.height > window.innerHeight - pad) {
248+
top = Math.max(pad, window.innerHeight - tipRect.height - pad);
249+
}
250+
tooltip.style.left = `${Math.round(left)}px`;
251+
tooltip.style.top = `${Math.round(top)}px`;
252+
}
253+
254+
function showTooltip(el) {
255+
const text = getTooltipText(el);
256+
if (!text) return;
257+
activeEl = el;
258+
suppressNativeTitle(el);
259+
tooltip.textContent = text;
260+
tooltip.classList.add('visible');
261+
positionTooltip(el);
262+
}
263+
264+
function hideTooltip(el) {
265+
if (el) restoreTitle(el);
266+
if (activeEl && (!el || activeEl === el)) {
267+
restoreTitle(activeEl);
268+
activeEl = null;
269+
}
270+
tooltip.classList.remove('visible');
271+
}
272+
273+
function tooltipTarget(start) {
274+
return start?.closest?.('[data-chronos-tooltip], [title]') || null;
275+
}
276+
277+
document.addEventListener('mouseover', (ev) => {
278+
const target = tooltipTarget(ev.target);
279+
if (!target) return;
280+
if (activeEl === target) return;
281+
hideTooltip(activeEl);
282+
showTooltip(target);
283+
}, true);
284+
285+
document.addEventListener('mouseout', (ev) => {
286+
if (!activeEl) return;
287+
const related = ev.relatedTarget;
288+
if (related && activeEl.contains?.(related)) return;
289+
if (ev.target === activeEl || activeEl.contains?.(ev.target)) hideTooltip(activeEl);
290+
}, true);
291+
292+
document.addEventListener('focusin', (ev) => {
293+
const target = tooltipTarget(ev.target);
294+
if (!target) return;
295+
hideTooltip(activeEl);
296+
showTooltip(target);
297+
}, true);
298+
299+
document.addEventListener('focusout', (ev) => {
300+
if (!activeEl) return;
301+
if (ev.target === activeEl || activeEl.contains?.(ev.target)) hideTooltip(activeEl);
302+
}, true);
303+
304+
window.addEventListener('scroll', () => positionTooltip(activeEl), true);
305+
window.addEventListener('resize', () => positionTooltip(activeEl));
306+
}
307+
170308
if (typeof window !== 'undefined' && !window.__cockpitPanelDefinitions) {
171309
window.__cockpitPanelDefinitions = [];
172310
}
@@ -436,6 +574,7 @@ async function runCliCommandWithSleepInterrupt({ command, args = [], properties
436574

437575
ready(async () => {
438576
console.log('[Chronos][app] Booting dashboard app');
577+
try { setupDarkTooltips(); } catch { }
439578
let bootOverlay = null;
440579
let bootMessage = null;
441580
let bootFailed = false;

utilities/dashboard/core/runtime.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,10 @@ const HELP_TEXT = {
188188
HabitTracker: 'Habits: Snapshot of habits, streaks, and today\'s status.',
189189
DebugConsole: 'Debug: Inspect/debug data and actions.',
190190
Achievements: 'Achievements: Review milestones you\'ve unlocked and mark awards/archives.',
191+
Admin: 'Admin: Run dashboard maintenance and developer-facing actions from one place.',
191192
Rewards: 'Rewards: Review point balance/history and redeem reward items.',
192193
MP3Player: 'MP3 Player: Spin up playlists from user/media/mp3, shuffle or repeat tracks, and manage uploads without leaving the dashboard.',
194+
NiaAssistant: 'Nia Assistant: Chat with Chronos assistance, ask for guidance, and trigger supported actions from the dashboard.',
193195
Settings: 'Settings: View and edit YAML files under user/settings via API.',
194196
Profile: 'Profile: View/Edit profile (nickname, theme, etc.).',
195197
Journal: 'Journal: Create/edit Journal or Dream entries. Autosaves; use Type/Date/Tags; Dream fields (lucid, signs, sleep) appear for dream type.',
@@ -199,6 +201,7 @@ const HELP_TEXT = {
199201
ResolutionTracker: 'Resolutions: Track yearly resolutions and link them to Chronos items.',
200202
Link: 'Link: Connect to a peer and sync a shared Canvas board (polling, last-write-wins).',
201203
Trends: 'Trends: View performance metrics including habits, goals, focus time, quality, and adherence stats.',
204+
SleepSettings: 'Sleep Settings: Tune sleep targets, windows, and related templates used by Chronos scheduling.',
202205
// Views
203206
Calendar: 'Calendar View: Timeline of scheduled blocks. Use zoom/level controls and toolstrip to navigate and manage.',
204207
TemplateBuilder: 'Template Builder: Build templates via drag & drop. Indent/outdent to nest. Toggle Sequential/Parallel; Save to persist.',
@@ -208,6 +211,12 @@ const HELP_TEXT = {
208211
Tracker: 'Tracker View: Visualize a full-year calendar for a selected habit or commitment with done/not-done states.',
209212
Docs: 'Docs View: Browse the Docs tree, search content, and read files without leaving the dashboard.',
210213
ADUC: 'ADUC View: Launches ADUC (Chronos mode) and embeds it in a dashboard iframe.',
214+
Atlas: 'Atlas View: Explore the broader Chronos workspace and navigate connected structures visually.',
215+
Canvas: 'Canvas View: Freeform spatial workspace for laying out information and references.',
216+
DayBuilder: 'Day Builder: Assemble and adjust a day plan by arranging blocks, timing, and sequencing.',
217+
GoalPlanner: 'Goal Planner: Shape a goal into milestones, timelines, and supporting structure before saving.',
218+
RoutineBuilder: 'Routine Builder: Design reusable routines and sub-steps with timing and nesting.',
219+
WeekBuilder: 'Week Builder: Plan a week at a higher level before turning it into concrete daily structure.',
211220
// Panels
212221
schedule: 'Schedule Panel: Mirrors today\'s agenda, lets you refresh/reschedule, jump dates, and kick off the Chronos day timer.',
213222
matrix: 'Matrix Panel: Interactive pivot grid for Chronos data. Choose rows/cols/values/filters, save presets, and spawn additional panels.',
@@ -233,12 +242,20 @@ const HELP_TEXT = {
233242
RandomPicker: 'Random Picker Panel: Pull a random item from saved pools.',
234243
Schedule: 'Schedule Panel: Mirrors today\'s agenda inside the cockpit.',
235244
StatusStrip: 'Status Chart Panel: Live read-only radar chart of current status indicators.',
245+
DataCards: 'Data Cards Panel: Summarize selected metrics or item groups in compact card form for the cockpit.',
236246
// Wizards
237247
Onboarding: 'Chronos Onboarding Wizard: Guided flow to set nickname, categories, statuses, templates, and starter goals/rewards.',
238248
GoalPlanning: 'Goal Planning Wizard: Capture intent, milestones, and supporting work to spin up a rich goal file.',
239249
ProjectLaunch: 'Project Launch Wizard: Draft a project brief, milestones, and kickoff actions before writing YAML.',
240250
BrainDump: 'Brain Dump Wizard: Rapid task capture, horizon buckets, and light refinement.',
241-
LifeSetup: 'Life Setup Wizard: Build a schedule skeleton with anchors like sleep, meals, work, and exercise.'
251+
LifeSetup: 'Life Setup Wizard: Build a schedule skeleton with anchors like sleep, meals, work, and exercise.',
252+
Big5: 'Big5 Wizard: Run a personality inventory flow and save the resulting trait profile into Chronos.',
253+
ChoreSetup: 'Chore Setup Wizard: Define recurring chores, cadence, and defaults without hand-writing templates.',
254+
FutureSelfDialogue: 'Future Self Dialogue Wizard: Reflect through a guided conversation with a future-oriented lens.',
255+
MapOfHappiness: 'Map of Happiness Wizard: Capture needs, rank them, and map item coverage against what matters most.',
256+
NewYearResolutions: 'New Year Resolutions Wizard: Draft yearly resolutions and supporting structure for the coming year.',
257+
SelfAuthoring: 'Self Authoring Wizard: Work through structured reflection and planning prompts across life areas.',
258+
StatusMapping: 'Status Mapping Wizard: Configure dashboard status axes, labels, and available value options.'
242259
};
243260

244261
function getHelpTip(name, fallback) {
@@ -247,7 +264,7 @@ function getHelpTip(name, fallback) {
247264
if (key && HELP_TEXT[key]) return HELP_TEXT[key];
248265
if (fallbackKey && HELP_TEXT[fallbackKey]) return HELP_TEXT[fallbackKey];
249266
const label = fallbackKey || key || 'Component';
250-
return `${label}: No help available.`;
267+
return `${label}: This dashboard surface opens a focused workspace for ${label.toLowerCase()}. Use the main controls in the header or toolbar, and refresh if the data looks stale.`;
251268
}
252269

253270
function createHelpButton(name, options = {}) {

utilities/dashboard/dashboard.html

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
html,
1616
body {
1717
height: 100%;
18+
scrollbar-width: thin;
19+
scrollbar-color: #293246 #0d1219;
20+
}
21+
22+
* {
23+
scrollbar-width: thin;
24+
scrollbar-color: #293246 #0d1219;
1825
}
1926

2027
body {
@@ -26,6 +33,31 @@
2633
padding-top: 36px;
2734
}
2835

36+
*::-webkit-scrollbar {
37+
width: 12px;
38+
height: 12px;
39+
background: #0d1219;
40+
}
41+
42+
*::-webkit-scrollbar-track {
43+
background: linear-gradient(180deg, #0d1219 0%, #101722 100%);
44+
border-radius: 999px;
45+
}
46+
47+
*::-webkit-scrollbar-thumb {
48+
background: linear-gradient(180deg, #2a3446 0%, #1f2837 100%);
49+
border: 2px solid #0d1219;
50+
border-radius: 999px;
51+
}
52+
53+
*::-webkit-scrollbar-thumb:hover {
54+
background: linear-gradient(180deg, #344057 0%, #273244 100%);
55+
}
56+
57+
*::-webkit-scrollbar-corner {
58+
background: #0d1219;
59+
}
60+
2961
#scaleRoot {
3062
position: fixed;
3163
inset: 0;

utilities/dashboard/views/calendar/inspector.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1102,7 +1102,7 @@ export function Inspector() {
11021102
});
11031103

11041104
return `
1105-
${renderCollapsibleSection('Resolutions', `${resolutionSummaries.length ? `
1105+
${renderCollapsibleSection('Resolutions <span class="inspector-soon-badge">Coming soon</span>', `${resolutionSummaries.length ? `
11061106
<div class="inspector-goal-rings">
11071107
${resolutionSummaries.slice(0, 12).map((summary) => {
11081108
const percent = Math.max(0, Math.min(100, Number(summary.percent || 0)));
@@ -2355,6 +2355,20 @@ export function Inspector() {
23552355
.inspector-section-title { font-size: 11px; text-transform: uppercase; letter-spacing: 0.8px; color: var(--chronos-text-muted, #9aa4b7); margin-bottom: 10px; }
23562356
.inspector-collapsible > .inspector-section-title { margin-bottom: 0; cursor: pointer; list-style: none; user-select: none; }
23572357
.inspector-collapsible > .inspector-section-title::-webkit-details-marker { display: none; }
2358+
.inspector-soon-badge {
2359+
display: inline-flex;
2360+
align-items: center;
2361+
margin-left: 8px;
2362+
padding: 2px 7px;
2363+
border-radius: 999px;
2364+
background: rgba(122, 162, 247, 0.14);
2365+
border: 1px solid rgba(122, 162, 247, 0.26);
2366+
color: var(--chronos-accent, #7aa2f7);
2367+
font-size: 10px;
2368+
font-weight: 700;
2369+
letter-spacing: 0.04em;
2370+
text-transform: uppercase;
2371+
}
23582372
.inspector-collapsible > .inspector-section-title::after {
23592373
content: '+';
23602374
float: right;

utilities/dashboard/views/tracker/index.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,20 +340,25 @@ export function mount(el) {
340340
const known = state.dayStates[key];
341341
let cls = 'tracker-day';
342342
let inner = String(day);
343+
let tooltip = key;
343344
if (future) {
344345
cls += ' future';
345346
} else if (!known) {
346347
cls += ' unknown';
347348
inner = `<span class="tracker-qmark">?</span>`;
349+
tooltip = `No data logged for ${key}. This day has not been marked yet.`;
348350
} else if (known.state === 'done') {
349351
cls += ` done${badContext ? ' bad' : ''}`;
352+
tooltip = `${key}: marked ${badContext ? 'bad' : 'done'}.`;
350353
} else if (known.state === 'not_done') {
351354
cls += ` not-done${badContext ? ' good' : ''}`;
355+
tooltip = `${key}: marked ${badContext ? 'clean' : 'not done'}.`;
352356
} else {
353357
cls += ' unknown';
354358
inner = `<span class="tracker-qmark">?</span>`;
359+
tooltip = `No data logged for ${key}. This day has not been marked yet.`;
355360
}
356-
dayCells.push(`<div class="${cls}" title="${escapeHtml(key)}">${inner}</div>`);
361+
dayCells.push(`<div class="${cls}" title="${escapeHtml(tooltip)}">${inner}</div>`);
357362
}
358363

359364
card.innerHTML = `

utilities/dashboard/widgets/achievements/index.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,21 @@ export function mount(el) {
6868
.ac-head { display:flex; align-items:center; justify-content:space-between; gap:8px; flex-wrap:nowrap; cursor:pointer; }
6969
.ac-name { font-size:15px; font-weight:700; }
7070
.ac-pill { padding:2px 10px; border-radius:999px; font-size:11px; text-transform:uppercase; letter-spacing:0.05em; }
71-
.ac-pill.pending { background:rgba(122,162,247,0.15); color:#7aa2f7; }
71+
.ac-pill.icon {
72+
width:28px;
73+
height:28px;
74+
padding:0;
75+
display:inline-flex;
76+
align-items:center;
77+
justify-content:center;
78+
font-size:15px;
79+
font-weight:700;
80+
letter-spacing:0;
81+
text-transform:none;
82+
border:1px solid rgba(255,255,255,0.08);
83+
box-shadow:inset 0 0 0 1px rgba(255,255,255,0.02);
84+
}
85+
.ac-pill.pending { background:rgba(201,167,75,0.16); color:#f0c96c; }
7286
.ac-pill.awarded { background:rgba(91,220,130,0.18); color:#5bdc82; }
7387
.ac-pill.archived { background:rgba(239,106,106,0.18); color:#ef6a6a; }
7488
.ac-meta { font-size:12px; color:var(--text-dim); }
@@ -429,8 +443,19 @@ export function mount(el) {
429443
name.className = 'ac-name';
430444
name.textContent = expandText(itemName);
431445
const pill = document.createElement('div');
446+
const stateLabel = state.charAt(0).toUpperCase() + state.slice(1);
432447
pill.className = `ac-pill ${state}`;
433-
pill.textContent = state.charAt(0).toUpperCase() + state.slice(1);
448+
pill.setAttribute('aria-label', stateLabel);
449+
pill.title = stateLabel;
450+
if (state === 'awarded') {
451+
pill.classList.add('icon');
452+
pill.textContent = '✓';
453+
} else if (state === 'pending') {
454+
pill.classList.add('icon');
455+
pill.textContent = '⌛';
456+
} else {
457+
pill.textContent = stateLabel;
458+
}
434459
headLeft.append(expander, name);
435460

436461
const actions = document.createElement('div');

0 commit comments

Comments
 (0)