Skip to content

Releases

v1.9.2

Choose a tag to compare

@github-actions github-actions released this 14 Aug 06:08
057b439

Security

  • Closed a CodeQL js/xss-through-dom finding (alert #2) in the epic colour picker — renderTagRow()'s quick colour picker (src/js/02-utils.js) read the raw <input type="color"> value and wrote it straight into cat.color (persisted state) and the swatch's style.background, bypassing getCat()'s existing safeCssColor() sanitisation entirely; the innerHTML sink for the picker's own value attribute relied on that same upstream sanitisation, which CodeQL's dataflow analysis doesn't credit as a barrier across the object spread. All three sites now call safeCssColor() explicitly. A real <input type="color"> clamps its .value to valid hex at the DOM level, so this isn't exploitable through normal use today — it closes the gap CodeQL flags and guards against any future code path (e.g. a non-native picker, or a refactor) that could feed an unclamped value through the same handlers. 3 new regression tests in test/unit.mjs (VM-sandboxed, since a real browser can't hold an invalid value in a colour input to prove the "before" case). This alert had been open since PR #87 (2026-05); a prior attempt (PR #265) was closed as superseded without merging.

Removed

  • Dead V5 timer-bar CSS in _timer.scss — the .timer-bar layout (circle ticker, task-name block, action pills, note row, utility pills, paused-state variants, and the .timer-task/.timer-elapsed compat stubs left over from an even earlier layout) was fully superseded by the hero card redesign and had zero references left in any .js or .html file — confirmed with a full cross-check, not just the .tb-* prefix grep the backlog note was based on, which also caught .timer-pulse, .timer-paused-lbl, .timer-pause/.timer-stop, and .timer-distract as dead too. Kept .tb-mood-panel/.tb-mood-item (still used by the hero card's own mood picker, interleaved in the middle of the dead block) and @keyframes pulse (still used by .entry.is-timing .edot's animation). _timer.scss goes from 691 to 344 lines; compiled styles.css shrinks by ~4KB gzipped. No behaviour change — verified visually in the browser (mood panel opens with the right styling, .edot's pulse animation still fires) plus the full build/lint/test suite.

Fixed

  • Log view entries sort by start time, not insertion order — viewEntries() (src/js/02-utils.js) produced its newest-first ordering by reversing insertion order, which only matches chronological order when entries are logged in the order they happened. An entry added retroactively — filling in a missed morning slot after the rest of the day was already recorded — therefore landed wherever it happened to be typed rather than at the position its start time called for. It now sorts by ts descending, so an entry always appears where its start time says it belongs regardless of when it was added. Knock-on effect: exportTxt() derives its oldest-first ordering by re-reversing viewEntries(), so the exported timesheet is now genuinely chronological too, rather than reversed-insertion-order. 2 new regression tests cover the ts ordering independent of insertion sequence, and the existing viewed-date filter.
  • Gap check (end-of-week report and end-of-day export warnings) no longer flags non-billable entries for a missing note or proof link — findGapReportEntries() and findExportWarnings() (pure-fns-export.js) previously required every finished, non-cancelled entry to carry a note or link regardless of billing status, so meetings, admin work, and other entries marked internal via the 💰/💸 toggle showed up in the weekly gap report and the export's warnings section even though there's nothing to bill and so nothing to prove. Both functions now skip an entry whose resolved _billable status is false; callers (exportTxt() in 05a-export.js, openGapReportOverlay() in 12c-gapreport.js) resolve that status via the existing isEntryBillable() three-tier lookup before calling in, since the pure module has no access to the category/task state that lookup needs.
  • smoke-tests.cjs couldn't run in sandboxes with a pre-provisioned Chromium that lags Playwright's pinned version — chromium.launch() always resolved the browser Playwright's own installer would have downloaded for the currently pinned playwright version, with no way to point it at a different binary already on disk. In an offline/sandboxed dev environment where a Chromium build exists at an older revision than node_modules/playwright-core/browsers.json expects, and npx playwright install can't reach the network to fetch a matching one, this failed with browserType.launch: Executable doesn't exist at .../chrome-headless-shell. Real CI is unaffected — ci.yml, auto-fix-ci.yml, and auto-chore.yml all run npx playwright install --with-deps chromium before tests, which keeps the download in sync with package.json. chromium.launch() now accepts an optional PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH env var; unset (the default everywhere else), behaviour is unchanged.
  • auto-chore.yml's exit-code checks could misfire on an empty string — the "comment on the issue" step's elif [ "${{ steps.claude.outputs.exit_code }}" != "0" ] and the "fail the job" step's if: steps.claude.outputs.exit_code != '0' both treat an empty/unset exit_code output as "nonzero," since '' != '0' is true. In the current workflow the claude step always sets this output before finishing (wrapped in set +e/set -e), so the gap wasn't reachable in practice — but a hard runner kill mid-step (cancellation, timeout) before that final echo runs would have produced a spurious 🔴 "CLI crashed" comment and an unwarranted red job, misreporting what actually happened. Both checks now require the output to be non-empty first. Flagged as a nitpick on PR #296's own review and tracked as #297; picked up separately since it edits auto-chore.yml itself, which the automation can never push (see the workflow's own header comment).
  • auto-chore.yml failed silently when a push failed, and briefly shipped with an invalid permissions: entry — found by dogfooding the workflow on a real test issue (#297) asking it to modify auto-chore.yml itself: the default GITHUB_TOKEN refused the push ("refusing to allow a GitHub App to create or update workflow ... without workflows permission"), and because that push step failed, the "comment on the issue with the outcome" step was skipped too (GitHub Actions skips subsequent steps by default after a failure) — leaving the issue with zero visibility into what happened. The comment step now runs regardless of an earlier step's outcome (!cancelled()), with a distinct message for "committed but failed to push" versus the existing "declined to automate" and "CLI itself crashed" cases. A same-day follow-up (#299) first tried adding workflows: write to the job's permissions: block — workflows is not a real GitHub Actions token permission scope, and the invalid key made the entire workflow file unparseable, breaking every chore trigger, not just ones touching workflow files. Corrected here: there is no permissions: entry that grants this — pushing changes to .github/workflows/* is a hard limit of the default GITHUB_TOKEN, now documented as a known limitation in the workflow file's header comment. The distinct failure comment above is what actually surfaces this case.
  • auto-chore.yml no longer silently swallows a Claude CLI crash — > chore-output.txt 2>&1 || true discarded the CLI's real exit code, so a crash or auth failure looked identical to Claude correctly declining to act: same green run, same generic issue comment. The step now captures the actual exit code; a nonzero exit posts a distinct 🔴 comment and fails the job outright, while a zero exit with no commit still reports the original "declined to automate" message. Also added a concurrency: auto-chore-<issue-number> group so a fast label-remove-then-reapply can't race two overlapping runs past the existing-branch/PR guard into duplicate PRs, and a ${SLUG:-untitled} fallback for issue titles with no ASCII alphanumeric characters (previously produced a branch name with a trailing dash). Found via /pr-review on #291 after it had already merged.

Added

  • CI now lints the workflow files themselves (actionlint) — an invalid key in a workflow does not fail loudly: GitHub refuses to parse the file, schedules zero jobs, and reports no error anywhere, so the run reads as "nothing to do". That has silently broken this repo twice — PR #256 (if: secrets.CLAUDE_REVIEWER_APP_ID != '', a context unavailable in step-level if:, which left a11y-audit.yml dead for weeks without ever posting a check) and PR #299 (workflows: write in a permissions: block, which killed every chore trigger until #300). Both were found only by checking gh api .../jobs by hand and noticing an empty list. The lint job now runs actionlint, pinned to v1.7.12 and SHA256-verified before it executes. Because a linter that silently stops enforcing is worse than none, npm run test:actionlint also re-runs actionlint against two fixtures reproducing those exact bugs and fails if either rule stops firing — matching on actionlint's message text rather than its rule tag, since tags have been renamed upstream before. Pure parseActionlintFindings(), interpretFixtureRun(), interpretWorkflowsRun() and interpretSelfTest() in .github/scripts/lib/actionlint-selftest.mjs, 20 unit tests that need no binary installed. Linting of run: block contents (shellcheck, pyflakes) is deliberately left off for now — that is a separate, much larger cleanup. Two actions/create-github-app-token input findings are also muted: actionlint v1.7.12 ships a stale schema for that action and reports client-id as undefined and app-id as required, when the action's own action.yml deprecates app-id in favour of client-id — acti...
Read more

v1.9.1

Choose a tag to compare

@github-actions github-actions released this 13 Aug 14:38
969d00c

Changed

  • Non-billable relabeled as "internal" — the billable/non-billable boolean already covered the distinction that mattered (client-billable vs. not), so rather than add a redundant third field, "non-billable" is now labelled "internal" everywhere it appears: the entry-row toggle, the task-row toggle, the category-manager button, and the export summary line. The underlying billable boolean and its entry → task → category inheritance (isEntryBillable() in 05-entries.js) are unchanged.

Added

  • Weekly report draft — a new "📝 weekly report" button in the export row opens a copy-to-clipboard panel summarising this calendar week (Mon–Sun) grouped by Jira ticket key, with tracked time and any notes/links per ticket, so the "what did I touch" reconstruction needed for a status report no longer means cross-referencing three systems by hand. Untracked/non-ticketed work is grouped into its own "No ticket" bucket rather than dropped; break/lunch/meeting entries are excluded, matching the gap report. This is the feature the proof-link/note fields (PR #276) were captured for. New pure buildWeeklyTicketSummary() and formatWeeklyTicketSummaryText() in pure-fns-export.js, both unit tested; new 12d-weeklyreport.js.
  • Weekly plan review checklist — planned-ahead ("upcoming") tasks sometimes turned out to already be finished elsewhere by the time their week actually arrived, since nothing ever re-checked them beforehand. A dismissible banner now appears once a new ISO week begins if any "upcoming" task is dated within it, opening a checklist (ticket key via the existing parseJiraLabel(), date, and "✓ done" / "✕ drop" actions per row) to reconcile the plan against reality. This app has no live Jira connection, so the checklist only ever prompts — it can't auto-detect completion. New pure findWeeklyPlanReviewTasks() in pure-fns-tasks.js; new file 10e-weeklyplan-review.js.
  • End-of-day export reminder — exports were always taken mid-afternoon, so the last day's JSON backup was always incomplete. A dismissible banner now appears in the export section once today's workday looks like it may be over (day-start + 8 hours, using the existing start-of-day timestamp — adapts to whenever the day actually began rather than assuming a fixed clock time), with an "🌙 end the day" action that triggers the real export flow. This is a reminder, not a silent auto-export — the app still never writes a file without an explicit click. Pure, unit-tested isWorkdayLikelyOver() in pure-fns-export.js; re-checked every 5 minutes so it can appear while idle.
  • End-of-week gap report — a new "🔍 gap report" button in the export row lists this calendar week's (Mon–Sun) finished, non-cancelled entries that have neither a proof link nor a note, so gaps in documentation surface before a weekly report is due instead of after. Clicking a flagged entry ("+ fix") jumps straight to it in the Log view with its proof-link/note editor already open. Break/lunch/meeting utility entries are excluded, since they never need documentation. New pure findGapReportEntries() in pure-fns-export.js and mondayOfWeek() in pure-fns-format.js (the latter also replaces a duplicated inline calculation in the statWeek header stat); both unit tested.
  • Timer long-running safety warning — the two longest-ever log entries (12h and 7h) both ended exactly when a JSON backup was taken, a strong signal of a timer left running unattended. The Hero Card now shows a dismissible amber warning once a running timer passes 4 hours (LONG_RUNNING_MINS in 03-timer.js), with a "stop timer" action; the threshold check is a pure, unit-tested isLongRunningTimer() in pure-fns-format.js. Separately, returning to the tab after it was hidden for more than 30 minutes while a timer was running now asks (via confirm()) whether to stop it — this only ever asks, it never changes timer data on its own. Both checks reset per timer session (startTimer()).
  • Proof link and note fields on log entries — each time-log entry can now carry a link (the concrete artefact the work touched: Confluence page id, Zephyr case/cycle key, filename, or a URL) and a note (a one-line "what I did," written at the time). Both are optional and edited via a new 📎 button on the entry row, which opens an inline panel with a link input and a note textarea — populated fields show as 🔗/📝 indicators instead, and a link starting with http(s):// renders as a clickable anchor. Entry notes are exported to the .txt timesheet alongside any plan-task note for the same task (new buildEntryNoteMap() and mergeNoteMaps() in pure-fns-export.js); the proof link is captured for now and will feed the planned JSON report-draft generator. New unit tests for validEntry's optional link/note type-checking and both new pure functions.
  • Task notes exported to the timesheet — exportTxt() now appends each task's plan note as an indented note: line under that task's entry in the .txt export, so context you jot on an in-progress task (via the existing 📝 note button) travels with the day's timesheet instead of staying trapped in the board. Matching is by date + case-insensitive task text (the same convention the app already uses to link a tracked entry back to its plan row — there is no shared taskId). New pure buildTaskNoteMap() in pure-fns-export.js, wired into formatGroupedLines()'s existing taskNotes parameter; 9 new unit tests.

Fixed

  • npm ci + commit broken on any fresh clone or worktree — @commitlint/config-conventional@21.2.0 pulls in conventional-changelog-conventionalcommits@10.x, whose package.json only exposes an ESM-only "exports" field (import/types, no require). That broke @commitlint/resolve-extends's CJS require.resolve() call with ERR_PACKAGE_PATH_NOT_EXPORTED, failing the commit-msg hook on every fresh install. It went unnoticed because the main working directory's stale node_modules still had the older, CJS-compatible 9.3.1 installed. Pinned back via a package.json overrides entry — parser.js (the file commitlint actually consumes) is byte-identical between the two versions, so the pin is behaviourally safe.
  • Log entries couldn't create a new epic — the category picker on the task board has always had a "+ new epic" control, but the equivalent picker on log entries (opened via the ● category ▾ button under a Log-view entry) never got one. In practice this meant epics could only be created from a task card, so tasks logged straight from the timer/adhoc-log input — as opposed to Jira-imported tasks, whose epics were already set up on the board — had no way to be tagged under a new epic. buildEntryCatPickerHtml() extracted into 04-render.js (matching catOpts in 10a-tasks-row.js) and given the same inline "+ new epic" creator, wired up with matching event handlers. The board's and the entry's "+ new epic" handlers previously duplicated the same create/dedupe/colour-pick logic; both now call a shared createCategory() in 01-state.js. 9 new unit tests.
  • Ad-hoc "+ log" row did nothing on a zero-entry day — render()'s empty-state branch rendered the inline quick-add row into #timeline but returned before its click/Enter bindings ran, so logging something on a fresh day (or any day with nothing logged yet) silently did nothing. Binding logic extracted into bindAdHocRow() and called from both branches; 4 new regression tests in test/unit.mjs.
  • JSON backups no longer grow without bound — the 21-day retention window was only applied to entries, while planTasks, blocks, devLog, and distractions were bundled in full. Those arrays grow every day (carried tasks, per-day time blocks, appended distraction/dev-log records), so the work-log-backup-21d-*.json file kept getting bigger despite its name. All five time-series arrays are now trimmed to the same window; categories, qpHidden, and the source-capped pomoLog are still kept whole. Retention now runs through a pure, unit-tested buildBackupPayload() in pure-fns.js (7 new tests), and the per-array count of excluded records is logged at export time.
  • Tabbed task board fills the full panel width — the To Do / In Progress / Done tab bar and the active lane (with its cards) were collapsing to content width and left-aligning, leaving the right side of the panel empty. The tabbed flex-column layout was inheriting align-items: start from the base .board-cols grid; it now resets to align-items: stretch so tabs and lane span the whole width. Regression test added in smoke-tests.cjs.
  • Manually-added tasks never showed as "In progress" on the Kanban board — the board's own "▸ track" button correctly promoted a task's status when starting its timer, but the three other ways to start tracking — the hero "WHAT'S NEXT?" composer, its recent-task chips, and the quick-capture overlay — only created a log entry and never touched the matching planTasks row, so the card stayed in To Do no matter how long the timer ran. Jira-imported tasks weren't spared either, but were rarely affected in practice since importing already seeds their status from Jira and users tend to interact with them via the board's own controls. New shared findPromotableTask() (pure, in pure-fns-tasks.js) and promoteMatchingTaskToInProgress() (10-tasks.js) wrapper, now called from all four start-tracking entry points — addEntry(), _heroHandleStart(), _heroStartFromChip(), _qcActivateRow(), and the board's own handler (deduplicating its previous inline copy of the same logic). New unit tests cover the lookup, the mutation (status flip, completedAt clearing, parent-task promotion), and that every entry point actually calls through.

Config defaults at this release

These are the compile-time defaults baked into src/js/00-config.js.
Runtime overrides (weather location etc.) are set per-machine in `config.lo...

Read more

v1.9.0

Choose a tag to compare

@github-actions github-actions released this 12 Jun 16:00
a7e1ad8

Config defaults at this release

These are the compile-time defaults baked into src/js/00-config.js.
Runtime overrides (weather location etc.) are set per-machine in config.local.ps1.

Constant Default value
WEATHER_LAT 60.1887
WEATHER_LON 24.927
WEATHER_NAME 'Helsinki'
JIRA_BASE 'https://your-instance.atlassian.net/browse'
DAILY_GOAL_MS 7.5 * 60 * 60 * 1000; // 7h 30m
AUTO_PAUSE_ON_TAB_SWITCH false

v1.8.9

Choose a tag to compare

@github-actions github-actions released this 29 May 20:58
e4d8c91

Config defaults at this release

These are the compile-time defaults baked into src/js/00-config.js.
Runtime overrides (weather location etc.) are set per-machine in config.local.ps1.

Constant Default value
WEATHER_LAT 60.1887
WEATHER_LON 24.927
WEATHER_NAME 'Helsinki'
JIRA_BASE 'https://your-instance.atlassian.net/browse'

v1.8.4

Choose a tag to compare

@github-actions github-actions released this 27 May 12:56

Config defaults at this release

These are the compile-time defaults baked into src/js/00-config.js.
Runtime overrides (weather location etc.) are set per-machine in config.local.ps1.

Constant Default value
WEATHER_LAT 60.1887
WEATHER_LON 24.927
WEATHER_NAME 'Helsinki'
JIRA_BASE 'https://your-instance.atlassian.net/browse'

v1.8.0 — Modular architecture, full JSDoc, a11y & tooling

Choose a tag to compare

@github-actions github-actions released this 25 May 13:20

v1.8.0 — Modular architecture, full JSDoc, a11y & tooling

This release marks the transition from a single-file architecture to 18 focused JS modules built at compile time — the same single work-log.html to open in your browser, but now with a proper source structure, full documentation, and a real toolchain.

Code Quality & Documentation

  • Full JSDoc coverage across all 18 JS source modules — every public function has @param, @returns, and @throws annotations
  • CITATION.cff — software citation metadata for academic/open-source use
  • DATA.md — complete localStorage schema reference (every key, field type, lifetime, and data-recovery recipe)
  • ARCHITECTURE.md — module dependency map, data flow, and integration overview
  • Key assumptions documented inline: billing rounding midpoint, billable defaults, carry-over rules

Accessibility

  • ARIA labels on all icon-only buttons
  • role="dialog" / aria-modal on all modals with focus trap (focus saved on open, restored on close; Escape closes)
  • role="region" on stat and pomodoro sections; role="list" on task lists
  • aria-live on dynamic counters; aria-expanded on collapsible headers (keyboard: Enter/Space)

Automation & Tooling

  • Husky pre-commit hook running ESLint + Prettier via lint-staged
  • Dependabot auto-update for npm dependencies
  • GitHub Releases workflow — auto-publishes release notes from CHANGELOG on tag push
  • GitHub Pages workflow — generates and publishes JSDoc reference on every push to main
  • Lighthouse CI workflow — audits a11y/best-practices/performance with thresholds
  • Branch ruleset — force-push and deletion blocked; test + lint required to merge

Configuration

  • Hardcoded config extracted to src/js/00-config.js: location coordinates, Jira base URL, calendar account labels — one file to edit when adapting for a different environment

Bug Fixes

  • Three-state checkpoint toggle (false → 'partial' → true) fixed in focus-mode overlay
  • Smoke test suite updated and expanded to 199 tests, 0 failures

Upgrading from v1.7.x

No localStorage schema changes — your existing data is fully compatible.

The built script.js and styles.css in the repo root are ready to use.
To build from source or run tests:

npm install
node build.js        # rebuilds script.js + styles.css
node smoke-tests.cjs # 199 tests

v1.7.0

Choose a tag to compare

@lupinesse lupinesse released this 20 May 13:09

v1.7.0 — May 20, 2026

Sunday, May 18

  • Calendar: read from all Outlook accounts (GetDefaultFolder + folder tree walk)
  • Calendar: TODAY'S MEETINGS section header, collapsible, matches plan-section style
  • Calendar: past meetings grey/italic, ongoing meeting pulses blue, stops when meeting ends
  • Calendar: account label per meeting ([Gofore]/[LähiTapiola])
  • Calendar: ▶ start button adds meeting to today's plan and starts timer
  • Calendar: ✕ delete button hides meeting for the day (persists in localStorage)
  • Calendar section moved above today's tasks
  • Nameday: Swedish flag SVG replaces emoji (reliable on all Windows fonts)
  • Flag days: added Kaatuneitten muistopäivä, Eino Leino, Miina Sillanpää, Swedish Heritage Day, Children's Rights Day, Finnish Nature Day
  • Nameday + calendar API proxied through local server to bypass CORS
  • CSP: allow any localhost port and 127.0.0.1 for server flexibility
  • Timesheets folder removed from git tracking, added to .gitignore
  • Fix carry-over: pending/blocked status propagates to today's copy
  • Re-implement upcoming status and 🔜 Upcoming Tasks section
  • Fix: timer input fields now have bright white text on dark background
  • EOD modal: notes-for-tomorrow section with per-task handoff inputs; note shown inline next morning

Monday, May 19

  • Task checkpoints: inline step list with progress bar, tick-off, delete, drag-to-reorder; carried forward on day rollover
  • Focus mode: checkpoints shown for active task, Pomodoro fixed below exit button, meetings visible
  • Checkpoint steps: double-click label to edit inline; Enter/blur saves, Escape cancels
  • Fix: checkpoint badge color mirrors task status (amber/purple/red) until progress begins
  • Fix: checkpoint badge status color only applied when checkpoints exist
  • Completed tasks expire at iteration boundaries instead of 14-day rolling window
  • Iteration expiry dates moved to localStorage; editable via 📅 iterations button
  • Calendar meetings sorted by start time
  • EOD handoff notes: only show tasks actually worked on today
  • Moved parked thoughts section between today's meetings and today's tasks
  • Smoke tests: fix streak assertion, fix cal-delete-btn, add tests 32–34 (checkpoints, handoff notes, parked thoughts)

Wednesday, May 20

  • Billable/non-billable: 💰/💸 toggle on all task rows + category default; retroactive update on category change
  • Billable emoji shown in time log entries; clickable to toggle matching plan task
  • Export: billable/non-billable totals in header; pasteable summary line grouped by category
  • Quick-pick: tasks past iteration boundary hidden automatically
  • Timeline + export: consecutive same-task blocks with <30min gap merged into single block

v1.6.0 — 15 May 2026

Choose a tag to compare

@lupinesse lupinesse released this 15 May 13:26

Tasks

  • Upcoming status — 🔜 moves tasks to a dedicated Upcoming Tasks section; excluded from overnight auto-carry
  • Pending & Blocked statuses — tinted section with comment history and timestamps
  • Subtasks — ⊕ split button; child tasks grouped under parent; auto-promote/complete parent
  • IDKW button — 🎲 picks a random To Do task
  • Auto-stop timer on done — marking Done stops the timer and closes the log entry

Calendar & Meetings

  • M365 calendar strip — today's meetings with time, duration, Teams Join link (local Outlook COM)
  • Past / Upcoming split — meetings grouped into sub-sections, re-evaluated every 60 seconds
  • Finnish calendar events — flag days, holidays, notable days from the official Finnish calendar API
  • Nameday — official Nimipäivärajapinta API; Finnish plain, Swedish dimmed with sv: label
  • Flag day display — SVG Finnish flag, "Next flag day:" prefix, full month name

Header & Stats

  • Streak fix — sub-stat starts from yesterday, consistent with main streak counter
  • statToday — counts unique task names

Timer

  • Parked thoughts — 💭 captures a thought mid-flow; → task or ✓ dismiss
  • safeRoundedStart() — prevents new entries overlapping the previous entry's tsEnd
  • Pomodoro in focus mode — ring visible during Emergency mode

Timeblock

  • Extended hours — grid expanded to 07:00–21:00
  • Emoji picker — ✦ button on each slot
  • Simplified editing — all blocks edited directly on the grid

Jira Integration

  • CSV importer — drag-and-drop Jira export; category mapping, select/deselect, duplicate detection
  • Clickable ticket links — AITO-XXXXX prefixes become hyperlinks

Quick Pick

  • Hide / restore tasks — × removes from recent list; persists in localStorage

Timelog

  • Inline editing — click any entry text to edit in place
  • Timelog header and wider chart labels (200 px)

Exports

  • Workday length — .txt exports include day started, ended, total duration
  • File System Access API — saves directly to timesheets/ and backups/ folders

Smoke Tests

  • 114 → 136 assertions; tests 25 (Upcoming lifecycle) and 26 (timer auto-stop) added

v1.5.0

Choose a tag to compare

@lupinesse lupinesse released this 13 May 07:07

v1.5.0

Jira CSV importer

  • New collapsible "Import from Jira" section in the plan panel
  • Drag and drop a Jira CSV export to load issues, or click to browse
  • Category mapping panel shows how Jira components map to your epics
  • Select/deselect individual tasks before importing; duplicate detection skips tasks already in today's plan

Quick pick improvements

  • Remove button on quick pick pills (hover to reveal, click ✕ to hide)
  • "Restore" link to bring back hidden items; persists across reloads

Inline entry editing

  • Click any entry text in the time log to edit it in place
  • Press Enter to save, Escape to cancel

Plan section polish

  • Section headers restyled with icon prefixes
  • Pending/Blocked tasks get a dedicated tinted section with comment history
  • Done and completed task colours improved for light and dark mode

Timeblock

  • Emoji picker added to timeblock slots
  • Meeting form removed (simplified)

Time log

  • Timelog header added
  • Chart label column widened for longer epic names
  • Day start/end times tracked in exports

v1.4.0 — Emergency mode, handoff notes, emoji, start of day, timeblock fixes

Choose a tag to compare

@lupinesse lupinesse released this 07 May 19:44

New features

🚨 Emergency / focus mode — strips UI to current task, smallest next action, and timer. Press 🚨 focus in timer bar, Escape to exit. Next action persists per task.

✍️ Transition handoff note — first Stop click shows a context field. Saves per task name, shown next time you start that task.

🌅 Start the day button — sets work start time; timeblock only shows untracked slots from that point. Sits below the ← Today → nav.

🎨 Category colour picker — click the coloured dot while renaming a category to open a native colour picker. Saves with the name.

😀 Task emoji — emoji button on each plan task opens a compact picker (45 common emojis + free text). Shown before task name in plan and completed sections.

📅 Meeting category — now light blue (#7EC8E3) with 📅 prefix in plan list.

🔔 Chime interval control — dropdown in Pomodoro: off / 15 / 30 / 45 / 60 min. Persists across reloads.

Bug fixes

  • Timer not ticking — liveEntry undefined in renderTimeblock crashed render on every log entry before setInterval could be reached
    • Day switch without restart — midnight rollover now detected in tickClock every 10s; auto-carries tasks and re-renders
    • Export banner null crash — checkNewDay/checkSnapshot referenced removed HTML; replaced with no-ops
    • _lastTickDate TDZ — declaration moved before first use in startup sequence
    • Task in both In Progress and Done — marking done now retires all older same-text versions; completed section deduplicates
    • Untracked slot boundary — tsEnd exactly on 30-min boundary mapped to next slot via Math.round in timeToSlot; fixed by subtracting 1 from slot index on boundary
    • Paused timer covers wrong slots — live block and covered-slot calc now cap at pause point (entry.ts + accumulatedMs), not Date.now()
    • Parent not promoted — promoteParents() runs at top of every renderPlan() call as an enforced invariant
    • Handoff text invisible — text colour hardcoded to rgba(255,255,255,0.9) for dark timer bar
    • Canvas null crash in setFavicon — getContext('2d') null-checked and wrapped in try/catch
    • setInterval decoupled from tickTimer — interval set up before first tick in all start paths

Tests

70 → 114 smoke tests passing. New areas: emergency mode, handoff, day-change fixes, task retirement, untracked boundary, paused block cap, parent invariant, task emoji, start of day.

Security

  • Task emoji button rendered t.emoji unescaped in innerHTML — fixed with escHtml()