feat: add timeline task context menu
|
|
@ -0,0 +1,129 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Date Selection 01 Block 01 — Scope and Baseline
|
||||
|
||||
**Status:** Planned.
|
||||
**Goal:** Verify the persistence-backed app baseline and lock the exact semantics
|
||||
for date selection before implementing UI callbacks.
|
||||
|
||||
---
|
||||
|
||||
## Block outcome
|
||||
|
||||
After this block, the repo state and product behavior for date navigation should
|
||||
be explicit. Later blocks should be able to implement date switching without
|
||||
reopening architectural decisions.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 1.1 — Confirm persistence prerequisite
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Confirm Persistence Plan 1 is complete in the current branch.
|
||||
2. Verify normal app startup no longer inserts arbitrary demo timeline tasks.
|
||||
3. Verify app startup opens an on-disk SQLite database through the approved
|
||||
runtime composition.
|
||||
4. Verify quick capture, schedule-from-backlog, complete, uncomplete, close, and
|
||||
reopen already work for the default selected date.
|
||||
5. Record any missing persistence behavior as a blocker before continuing.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Date Selection 01 does not start from demo seed runtime.
|
||||
- The app has one durable persistence path for normal startup.
|
||||
- Any missing Persistence Plan 1 behavior is documented and fixed before Block 02.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 1.2 — Reconcile current composition and controller boundaries
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Inspect the current post-persistence composition root.
|
||||
2. Identify the object that owns:
|
||||
- `GetTodayStateQuery`,
|
||||
- `V1ApplicationCommandUseCases`,
|
||||
- `V1ApplicationManagementUseCases`,
|
||||
- owner id,
|
||||
- owner timezone,
|
||||
- runtime clock,
|
||||
- SQLite lifecycle/disposal.
|
||||
3. Identify whether `DemoSchedulerComposition` still exists only for tests or
|
||||
explicit demo mode.
|
||||
4. Decide whether selected date should live in:
|
||||
- a dedicated `SelectedDateController`,
|
||||
- an expanded `TodayScreenController`, or
|
||||
- parent `FocusFlowHome` state that reconfigures date-aware controllers.
|
||||
5. Prefer the smallest design that keeps date behavior testable and does not
|
||||
force widgets to know scheduler internals.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- The selected-date owner is named in the implementation notes for this block.
|
||||
- Composition APIs can create reads/commands for arbitrary `CivilDate` values.
|
||||
- No widget is planned to instantiate repositories, Drift classes, or scheduler
|
||||
engine objects.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 1.3 — Lock selected-date semantics
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Define selected date as the currently visible planning day in the owner's
|
||||
timezone.
|
||||
2. Confirm selected date changes are read-only until the user runs a command.
|
||||
3. Confirm arrows move exactly one civil day at a time.
|
||||
4. Confirm the date picker jumps directly to the chosen civil date.
|
||||
5. Confirm command timestamps use the real runtime clock, not the selected date
|
||||
as a fake `now`.
|
||||
6. Confirm scheduling a backlog task while viewing a selected day targets that
|
||||
selected day's planning window.
|
||||
7. Confirm completing a future/past selected-day task records the actual
|
||||
completion instant from the runtime clock.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Date navigation cannot itself create, move, complete, or delete tasks.
|
||||
- Date navigation uses `CivilDate` at app boundaries.
|
||||
- Instant/date conversion happens through existing owner timezone utilities.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 1.4 — Identify tests to preserve before edits
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Locate existing Flutter widget tests for the top bar, timeline, modal, and
|
||||
Today screen controller.
|
||||
2. Locate persistence lifecycle tests added by Persistence Plan 1.
|
||||
3. Locate scheduler-core tests for `GetTodayStateQuery` and command use cases.
|
||||
4. Add a small checklist of tests that must still pass after this plan.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- The validation targets for later blocks are known before code changes begin.
|
||||
- Existing tests are not deleted or weakened to make date selection pass.
|
||||
|
||||
---
|
||||
|
||||
## Block acceptance criteria
|
||||
|
||||
- Persistence prerequisite is verified.
|
||||
- Selected-date state ownership is decided.
|
||||
- Selected-date behavior is documented.
|
||||
- Tests and validation commands for the plan are known.
|
||||
|
||||
## Validation after block
|
||||
|
||||
```sh
|
||||
scripts/test.sh
|
||||
cd apps/focus_flow_flutter
|
||||
flutter test
|
||||
```
|
||||
|
||||
If Flutter tooling is unavailable, run all non-Flutter package tests and document
|
||||
that Flutter validation is blocked by environment setup.
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Date Selection 01 Block 02 — Selected-Date State and Reads
|
||||
|
||||
**Status:** Planned.
|
||||
**Goal:** Make Today screen loading date-aware without changing task state.
|
||||
|
||||
---
|
||||
|
||||
## Block outcome
|
||||
|
||||
After this block, app/controller code can load Today data for any selected
|
||||
`CivilDate`. The visual top-bar buttons may still be disabled until Block 03.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 2.1 — Add selected-date state API
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Add the selected-date owner chosen in Block 01.
|
||||
2. Expose the current selected `CivilDate`.
|
||||
3. Expose commands to:
|
||||
- select a specific date,
|
||||
- move to previous day,
|
||||
- move to next day,
|
||||
- optionally return to current owner-local day if the current UI has a
|
||||
calendar/today affordance.
|
||||
4. Ensure repeated selection of the current date is a no-op.
|
||||
5. Ensure date changes notify exactly the controllers/widgets that need a
|
||||
reload.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Date state can be tested without rendering the full app.
|
||||
- Date mutation does not call scheduler command use cases.
|
||||
- Date mutation can trigger read refresh in one predictable place.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 2.2 — Make Today reads accept a date parameter
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Replace fixed-date read callbacks with date-aware read callbacks, for example:
|
||||
`Future<ApplicationResult<TodayState>> Function(CivilDate date)`.
|
||||
2. Update `TodayScreenController.load` or equivalent to load the selected date.
|
||||
3. Keep `GetTodayStateRequest.date` as the canonical read parameter.
|
||||
4. Preserve `includeNextFlexibleItem: true` unless a later product decision
|
||||
changes it.
|
||||
5. Avoid filtering persisted tasks in widgets to simulate a day switch.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Loading July 2 and July 3 calls the backend query twice with distinct
|
||||
`CivilDate` values.
|
||||
- The controller maps each returned `TodayState` into `TodayScreenData` normally.
|
||||
- Empty state for a selected date still shows the selected date label.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 2.3 — Sync modal selection and timeline scroll state on date changes
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Decide whether selected task modals should close on date change or resync only
|
||||
if the same task still appears on the new day.
|
||||
2. Preferred V1 behavior: clear selected card when selected date changes.
|
||||
3. Reset timeline scroll target/request state when switching days unless a
|
||||
specific target is requested after read refresh.
|
||||
4. Ensure date changes cannot leave a modal open for a task that is no longer in
|
||||
`TodayScreenData.cards`.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Open task modal closes when moving to another date.
|
||||
- Timeline cards from the previous date are not reused after reload.
|
||||
- No stale selected-card exception appears during rapid date changes.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 2.4 — Preserve loading, empty, and failure states per selected date
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Show loading while the selected date's read is in progress.
|
||||
2. Show empty state if the selected date has no timeline cards.
|
||||
3. Show failure state with the selected date unchanged if the read fails.
|
||||
4. Avoid falling back to the previous date's data after a failed new-date read.
|
||||
5. Prevent older in-flight reads from overwriting newer selected-date results.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Rapid previous/next clicks settle on the latest selected date.
|
||||
- Failure for one date does not corrupt the selected-date controller.
|
||||
- Empty selected dates remain navigable.
|
||||
|
||||
---
|
||||
|
||||
## Block acceptance criteria
|
||||
|
||||
- Today reads are selected-date aware.
|
||||
- Date changes are read-only state transitions.
|
||||
- Modal/selection state cannot leak across dates.
|
||||
- Controller tests cover previous, next, direct jump, empty date, and read
|
||||
failure.
|
||||
|
||||
## Validation after block
|
||||
|
||||
```sh
|
||||
cd apps/focus_flow_flutter
|
||||
flutter test test/controllers
|
||||
flutter test test/app
|
||||
```
|
||||
|
||||
If tests are organized differently after Persistence Plan 1, run the nearest
|
||||
controller/app tests and document the command used.
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Date Selection 01 Block 03 — Top-Bar Navigation and Date Picker
|
||||
|
||||
**Status:** Planned.
|
||||
**Goal:** Wire the visible top-bar controls to selected-date state while
|
||||
preserving the compact dark UI.
|
||||
|
||||
---
|
||||
|
||||
## Block outcome
|
||||
|
||||
After this block, the user can click previous/next day arrows and open a date
|
||||
picker from the displayed date.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 3.1 — Refactor `TopBar` callback API
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Add `onPreviousDate`, `onNextDate`, and `onDatePressed` callbacks to `TopBar`.
|
||||
2. Keep callbacks nullable only if tests/demo previews need disabled controls.
|
||||
3. Convert current placeholder `onPressed: () {}` values into real callback
|
||||
invocations.
|
||||
4. Preserve the current metrics and layout unless a callback affordance requires
|
||||
minor spacing updates.
|
||||
5. Add tooltips/accessibility labels:
|
||||
- Previous day,
|
||||
- Next day,
|
||||
- Choose date.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- `TopBar` no longer contains inert date controls in normal app mode.
|
||||
- Button hit targets remain usable at compact width.
|
||||
- Existing top-bar visual snapshots/widget tests are updated only for intentional
|
||||
changes.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 3.2 — Make the date label a button
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Replace the static date label area with an accessible button-like control.
|
||||
2. Keep the visual treatment calm and compact; avoid bright native-calendar
|
||||
styling that clashes with the dark theme.
|
||||
3. Ensure the button still scales/truncates cleanly at the current compact
|
||||
breakpoint.
|
||||
4. Preserve keyboard focus and screen-reader semantics.
|
||||
5. Keep the top-left `Today` title as the surface name unless product direction
|
||||
explicitly changes it.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Clicking the displayed date opens the picker callback.
|
||||
- Keyboard users can focus and activate the date control.
|
||||
- The date label still renders correctly for long month names.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 3.3 — Implement date picker presentation
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Use Flutter's date picker if it can be themed cleanly for the current dark UI;
|
||||
otherwise implement a small scoped picker surface.
|
||||
2. Initialize the picker to the currently selected date.
|
||||
3. On selection, convert the picked date to `CivilDate` without accidentally
|
||||
shifting it through UTC/local-time conversions.
|
||||
4. On cancel, leave selected date unchanged.
|
||||
5. Boundaries can remain broad in V1 unless existing product rules define a
|
||||
scheduling horizon.
|
||||
6. Keep date picker state outside scheduler core.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Picking a date reloads the timeline for that exact civil date.
|
||||
- Canceling the picker performs no read refresh and no task mutation.
|
||||
- Date conversion tests cover at least one non-UTC owner timezone if timezone
|
||||
test utilities are available.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 3.4 — Wire top-bar controls through the screen scaffold
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Thread date callbacks from the selected-date owner through
|
||||
`_TodayScreenScaffold` into `TopBar`.
|
||||
2. Ensure empty/loading/failure states still render top-bar date controls where
|
||||
appropriate.
|
||||
3. Ensure date switching while a modal is open clears selection before or during
|
||||
reload.
|
||||
4. Keep `TimelineView` unaware of date-picker mechanics.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Previous/next/date-picker interactions work from ready and empty states.
|
||||
- Timeline widgets continue to receive only card/range/selection inputs.
|
||||
- No scheduler/domain imports are added to presentational top-bar widgets unless
|
||||
already required by app-layer boundaries.
|
||||
|
||||
---
|
||||
|
||||
## Block acceptance criteria
|
||||
|
||||
- The top-bar date controls are interactive.
|
||||
- The date label opens a picker.
|
||||
- UI remains compact and dark-themed.
|
||||
- Date controls are accessible and test-covered.
|
||||
|
||||
## Validation after block
|
||||
|
||||
```sh
|
||||
cd apps/focus_flow_flutter
|
||||
flutter test test/widgets
|
||||
flutter test test/app
|
||||
```
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Date Selection 01 Block 04 — Date-Aware Commands and Persistence
|
||||
|
||||
**Status:** Planned.
|
||||
**Goal:** Ensure task commands run against the currently selected date and persist
|
||||
correctly across multiple days.
|
||||
|
||||
---
|
||||
|
||||
## Block outcome
|
||||
|
||||
After this block, a user can switch days, add/schedule/complete tasks on that
|
||||
visible day, close the app, reopen it, and see the expected state on the same
|
||||
selected day.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 4.1 — Make command controller date-aware
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Replace fixed `SchedulerCommandController.localDate` construction with a
|
||||
current-date provider or recreate command controllers when selected date
|
||||
changes.
|
||||
2. Ensure these commands use the selected date at execution time:
|
||||
- schedule backlog item to next available slot,
|
||||
- complete flexible task,
|
||||
- complete required visible task,
|
||||
- uncomplete task.
|
||||
3. Keep quick capture to backlog date-neutral unless the current capture UI adds
|
||||
an explicit schedule-now option.
|
||||
4. Include the selected date in command read hints when relevant.
|
||||
5. Preserve stale-revision checks by passing `expectedUpdatedAt` unchanged.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Scheduling from Backlog while viewing tomorrow places the task in tomorrow's
|
||||
planning window when a valid slot exists.
|
||||
- Completing a task while viewing a non-current day runs the command against
|
||||
that visible day's planning state.
|
||||
- Command code does not read date labels from widgets.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 4.2 — Refresh the right read models after date-aware commands
|
||||
|
||||
### Tasks
|
||||
|
||||
1. After a successful command, reload the currently selected date.
|
||||
2. If a command's read hint references another affected date, decide whether to:
|
||||
- reload only the selected date for V1, or
|
||||
- add a small future-proof invalidation hook.
|
||||
3. For this plan, prefer reloading the selected date only unless tests prove the
|
||||
UI needs immediate cross-date refresh.
|
||||
4. Make sure moving a task into a different day removes it from the current
|
||||
selected-day timeline after refresh.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- After schedule/complete/uncomplete, visible cards match persisted backend
|
||||
state for the selected date.
|
||||
- No stale card stays visible after a successful command that removes it from the
|
||||
selected day.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 4.3 — Persist tasks on previous and future days
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Add lifecycle tests using a temp SQLite file:
|
||||
- open app/runtime on date A,
|
||||
- capture a task,
|
||||
- schedule it into date A,
|
||||
- close database/runtime,
|
||||
- reopen with date A selected,
|
||||
- assert task placement remains.
|
||||
2. Repeat for a future date relative to the test clock.
|
||||
3. Repeat for a previous date if the scheduler permits scheduling into a past
|
||||
selected day; otherwise document the validation failure and expected UX.
|
||||
4. Verify Backlog remains unified across date changes.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Future-day scheduled task persists after close/reopen.
|
||||
- Previous-day task behavior is explicitly tested or explicitly rejected by
|
||||
backend validation.
|
||||
- Backlog tasks are not duplicated across date navigation.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 4.4 — Persist done/not-done state across selected dates
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Add lifecycle tests for completing a task while viewing a future date.
|
||||
2. Add lifecycle tests for completing a task while viewing a previous date if the
|
||||
backend allows it.
|
||||
3. Close and reopen the database/runtime after completion.
|
||||
4. Assert `TaskStatus.completed`, `completedAt`, actual times where present, and
|
||||
updated metadata reload.
|
||||
5. Uncomplete the same task, close/reopen, and assert planned/not-done state
|
||||
reloads.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Done state persists per task regardless of which selected date initiated the
|
||||
command.
|
||||
- Uncomplete state persists and returns the card to normal planned state.
|
||||
- Completion timestamp remains the real command clock instant.
|
||||
|
||||
---
|
||||
|
||||
## Block acceptance criteria
|
||||
|
||||
- Reads and commands use the selected date consistently.
|
||||
- Date-specific schedule placement persists across app restarts.
|
||||
- Done/not-done state persists across app restarts.
|
||||
- Backlog remains unified and durable.
|
||||
|
||||
## Validation after block
|
||||
|
||||
```sh
|
||||
cd packages/scheduler_persistence_sqlite
|
||||
dart test
|
||||
cd ../../apps/focus_flow_flutter
|
||||
flutter test
|
||||
```
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Date Selection 01 Block 05 — Completion Date Presentation
|
||||
|
||||
**Status:** Planned.
|
||||
**Goal:** Show enough completion timestamp context when the completed date and
|
||||
scheduled date differ.
|
||||
|
||||
---
|
||||
|
||||
## Block outcome
|
||||
|
||||
After this block, completed timeline cards still look compact for normal same-day
|
||||
completion, but they show date context when completion happened on a different
|
||||
local date than the scheduled slot.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 5.1 — Add completion date fields to presentation models
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Extend `TimelineCardModel` or an equivalent presentation model with one or
|
||||
more explicit completion fields:
|
||||
- completed local date,
|
||||
- completed time text,
|
||||
- display-ready completed date/time text,
|
||||
- whether date context is required.
|
||||
2. Keep raw `DateTime` handling out of leaf widgets where possible.
|
||||
3. Use owner timezone/local-date semantics rather than comparing UTC calendar
|
||||
days directly.
|
||||
4. Preserve existing `completedTimeText` behavior for same-day cards if it is
|
||||
still useful.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Presentation mapper can decide whether date context is required.
|
||||
- Widgets receive display-ready text or simple presentation flags.
|
||||
- Existing same-day completed card tests continue to pass with minimal changes.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 5.2 — Define date-context display rules
|
||||
|
||||
### Required V1 rule
|
||||
|
||||
Show date + time when:
|
||||
|
||||
1. `completedAt` local date differs from the scheduled local date, or
|
||||
2. actual completion interval local date differs from the scheduled local date.
|
||||
|
||||
### Optional V1-friendly rule
|
||||
|
||||
Also show date + time when the selected date is not the current owner-local day,
|
||||
if testing shows users need that context while reviewing previous/future days.
|
||||
Keep this optional rule behind a clearly named mapper helper so it can be changed
|
||||
without rewriting widgets.
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Implement a single helper that compares scheduled and completed local dates.
|
||||
2. Prefer exact labels like `Completed Jul 2, 8:05 AM` over vague labels like
|
||||
`Completed yesterday` for V1.
|
||||
3. Keep same-day labels compact, for example `Completed at: 8:05 AM`.
|
||||
4. Add tests for:
|
||||
- scheduled and completed same date,
|
||||
- scheduled date differs from completed date,
|
||||
- completed timestamp present but actual interval absent,
|
||||
- actual interval present but completed timestamp absent if the domain can
|
||||
produce that state.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Cross-date completion never renders as time-only.
|
||||
- Same-day completion remains compact.
|
||||
- Date formatting is deterministic in tests.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 5.3 — Update compact card and modal text
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Update timeline card subtitle text to include completion date context when
|
||||
required.
|
||||
2. Update selected-task modal metadata if it repeats completion time.
|
||||
3. Ensure long date/time strings fit in compact card constraints without
|
||||
overflowing over reward/difficulty controls.
|
||||
4. Add tooltip/semantic label if the visible compact text must be shortened.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Completed cross-date cards are readable in compact timeline mode.
|
||||
- The selected-task modal shows the same completion date truth as the card.
|
||||
- Text overflow behavior is intentional and tested.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 5.4 — Add persistence-backed presentation tests
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Create or reuse a temp SQLite runtime test that schedules a task on one day
|
||||
and completes it on another command clock day.
|
||||
2. Reopen the database/runtime.
|
||||
3. Load the scheduled day.
|
||||
4. Assert the card contains completion date + time.
|
||||
5. Load the completion day if the task also appears there by backend design;
|
||||
assert presentation remains consistent.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Cross-date completion display survives close/reopen.
|
||||
- UI presentation is based on persisted task timestamps, not transient widget
|
||||
state.
|
||||
|
||||
---
|
||||
|
||||
## Block acceptance criteria
|
||||
|
||||
- Completion-date presentation covers cross-date completion.
|
||||
- Same-day completion remains compact.
|
||||
- Card and modal text agree.
|
||||
- Presentation tests cover persisted reload.
|
||||
|
||||
## Validation after block
|
||||
|
||||
```sh
|
||||
cd apps/focus_flow_flutter
|
||||
flutter test test/models
|
||||
flutter test test/widgets
|
||||
```
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Date Selection 01 Block 06 — Validation and Handoff
|
||||
|
||||
**Status:** Planned.
|
||||
**Goal:** Prove date selection is stable, persisted, and ready for Task Pushing 01.
|
||||
|
||||
---
|
||||
|
||||
## Block outcome
|
||||
|
||||
After this block, Date Selection 01 should be complete and Task Pushing 01 can
|
||||
begin from a date-aware, persistence-backed UI.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 6.1 — Add end-to-end date navigation tests
|
||||
|
||||
### Required tests
|
||||
|
||||
1. Initial load shows the runtime-selected/current day.
|
||||
2. Previous arrow changes the date label and reloads that day.
|
||||
3. Next arrow changes the date label and reloads that day.
|
||||
4. Date picker jumps directly to a chosen date.
|
||||
5. Empty selected dates show empty state with the correct date label.
|
||||
6. Rapid navigation settles on the final selected date.
|
||||
7. Open modal closes when changing dates.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Date navigation is covered by widget/controller tests.
|
||||
- Tests do not depend on arbitrary demo seed tasks.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 6.2 — Add persistence lifecycle tests across dates
|
||||
|
||||
### Required tests
|
||||
|
||||
1. Schedule a backlog task into tomorrow, close/reopen, reload tomorrow, assert
|
||||
it remains planned there.
|
||||
2. Complete that tomorrow task, close/reopen, reload tomorrow, assert done state
|
||||
remains.
|
||||
3. Uncomplete it, close/reopen, reload tomorrow, assert planned state remains.
|
||||
4. Schedule or load a previous-day task if supported, close/reopen, assert its
|
||||
state remains.
|
||||
5. Verify Backlog is still unified and not duplicated by date navigation.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Multi-day persistence is proven using on-disk temp SQLite files.
|
||||
- The test explicitly closes database/runtime handles before reopen assertions.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 6.3 — Run validation gates
|
||||
|
||||
### Commands
|
||||
|
||||
Run the strongest available validation set:
|
||||
|
||||
```sh
|
||||
scripts/test.sh
|
||||
cd packages/scheduler_core
|
||||
dart analyze
|
||||
dart test
|
||||
cd ../scheduler_persistence_sqlite
|
||||
dart analyze
|
||||
dart test
|
||||
cd ../../apps/focus_flow_flutter
|
||||
flutter analyze
|
||||
flutter test
|
||||
```
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- All available gates pass.
|
||||
- If a command is unavailable in the environment, record the exact command and
|
||||
failure reason in the plan handoff notes.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 6.4 — Update minimal handoff/status docs
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Update the plan summary/status to indicate completion.
|
||||
2. Move the plan folder only if the project's completed-plan workflow calls for
|
||||
it.
|
||||
3. Update `Codex Documentation/Current Software Plan/README.md` so Task Pushing
|
||||
01 is the next incomplete plan.
|
||||
4. Do not create a broad overall report.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Handoff docs identify Date Selection 01 as complete.
|
||||
- Task Pushing 01 is clearly next.
|
||||
- No unrelated planning docs are rewritten.
|
||||
|
||||
---
|
||||
|
||||
## Final plan acceptance criteria
|
||||
|
||||
- Previous/next/date-picker controls work.
|
||||
- Selected date drives Today reads and relevant commands.
|
||||
- Multi-day schedule and done state persist across restarts.
|
||||
- Cross-date completion displays date + time.
|
||||
- UI remains backend-driven.
|
||||
- Validation status is documented.
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Date Selection 01 Execution Order
|
||||
|
||||
**Status:** Planned.
|
||||
|
||||
Run these files in order. All chunks are intended for `XHIGH` execution unless a
|
||||
chunk explicitly says otherwise.
|
||||
|
||||
1. `DATE_SELECTION_01_BLOCK_01_SCOPE_AND_BASELINE.md`
|
||||
- Confirm Persistence Plan 1 is complete.
|
||||
- Reconcile current app composition after persistence wiring.
|
||||
- Lock selected-date semantics before touching UI code.
|
||||
|
||||
2. `DATE_SELECTION_01_BLOCK_02_SELECTED_DATE_STATE_AND_READS.md`
|
||||
- Add selected-date state/controller behavior.
|
||||
- Make Today reads date-aware.
|
||||
- Clear or resync selected card state when the visible day changes.
|
||||
|
||||
3. `DATE_SELECTION_01_BLOCK_03_TOP_BAR_NAVIGATION_AND_PICKER.md`
|
||||
- Wire previous/next day buttons.
|
||||
- Convert the date label into an accessible date-picker button.
|
||||
- Preserve compact dark-theme visuals.
|
||||
|
||||
4. `DATE_SELECTION_01_BLOCK_04_DATE_AWARE_COMMANDS_AND_PERSISTENCE.md`
|
||||
- Pass the selected date into command use cases.
|
||||
- Validate add/schedule/complete/uncomplete across multiple persisted days.
|
||||
- Ensure close/reopen loads the same date-specific state.
|
||||
|
||||
5. `DATE_SELECTION_01_BLOCK_05_COMPLETION_DATE_PRESENTATION.md`
|
||||
- Add completion date context to read/presentation models.
|
||||
- Show date + time when completed local date differs from scheduled local
|
||||
date.
|
||||
- Keep same-day completion compact.
|
||||
|
||||
6. `DATE_SELECTION_01_BLOCK_06_VALIDATION_HANDOFF.md`
|
||||
- Add widget/controller/lifecycle tests.
|
||||
- Run validation gates.
|
||||
- Update only handoff/status docs required by this plan.
|
||||
|
||||
Do not begin Task Pushing 01 until this plan passes its validation handoff.
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Date Selection 01 Summary — Day Navigation and Date Picker
|
||||
|
||||
**Status:** Planned.
|
||||
**Scope level:** XHIGH implementation plan.
|
||||
**Primary outcome:** The Flutter desktop app can switch the visible planning day,
|
||||
load the correct persisted timeline for that day, and keep date-specific task
|
||||
state durable across app restarts.
|
||||
|
||||
---
|
||||
|
||||
## Current repo facts this plan is based on
|
||||
|
||||
1. The compact Flutter UI already has top-bar calendar, left-arrow, and
|
||||
right-arrow controls, but their callbacks are placeholders.
|
||||
2. The top bar currently receives only a display-ready `dateLabel`.
|
||||
3. `TodayScreenController` currently reads one fixed day through a callback.
|
||||
4. The composition currently owns a single `CivilDate` and creates command
|
||||
controllers with that fixed local date.
|
||||
5. `GetTodayStateQuery.execute` already accepts a requested `CivilDate`.
|
||||
6. Scheduler commands such as scheduling, completing, and uncompleting already
|
||||
accept a local date for planning context.
|
||||
7. After Persistence Plan 1, app state should be backed by an on-disk SQLite
|
||||
`ApplicationUnitOfWork`, not static demo seed data.
|
||||
|
||||
---
|
||||
|
||||
## Product behavior
|
||||
|
||||
1. Clicking the left arrow moves the selected date back one day.
|
||||
2. Clicking the right arrow moves the selected date forward one day.
|
||||
3. Clicking the displayed date opens a date picker.
|
||||
4. Choosing a date immediately reloads the Today timeline for that date.
|
||||
5. Date switching does not mutate tasks by itself.
|
||||
6. Scheduling a backlog task while viewing a future or past date schedules into
|
||||
the selected date's planning window, subject to backend scheduling rules.
|
||||
7. Completing a task uses the real current clock for completion metadata, not
|
||||
the selected date as fake time.
|
||||
8. Date-specific task placement and done/not-done state survive close/reopen.
|
||||
9. If completion happened on a different local date than the scheduled slot, the
|
||||
card must show the completion date, not only the completion time.
|
||||
|
||||
---
|
||||
|
||||
## Definition of done
|
||||
|
||||
Date Selection 01 is complete when all of the following are true:
|
||||
|
||||
1. `TopBar` exposes working previous-day, next-day, and date-picker callbacks.
|
||||
2. The displayed date is an accessible button, not static text.
|
||||
3. The selected date is owned by controller/composition state and is not hardcoded
|
||||
into demo composition.
|
||||
4. Switching dates reloads `GetTodayStateQuery` with the selected `CivilDate`.
|
||||
5. Command controllers run schedule/complete/uncomplete actions against the
|
||||
current selected date.
|
||||
6. Any selected modal card is cleared or resynced safely when the date changes.
|
||||
7. Tasks scheduled on tomorrow or a previous day persist and reload on that day.
|
||||
8. Completed state persists when completion happens from a non-current selected
|
||||
day.
|
||||
9. Completion presentation includes date context whenever completed local date
|
||||
differs from scheduled local date.
|
||||
10. Widgets do not contain Drift, SQL, repository, or scheduling-engine logic.
|
||||
11. Existing compact timeline visuals remain stable except for the intended date
|
||||
controls and completion-date text.
|
||||
12. Backend, Flutter, and lifecycle validation gates pass, or unavailable gates
|
||||
are documented.
|
||||
|
||||
---
|
||||
|
||||
## Non-goals
|
||||
|
||||
1. No week/month calendar view.
|
||||
2. No drag-and-drop scheduling.
|
||||
3. No recurring task support.
|
||||
4. No calendar sync.
|
||||
5. No changes to the scheduling algorithm except bug fixes discovered while
|
||||
validating selected-date command behavior.
|
||||
6. No broad redesign of the compact timeline.
|
||||
7. No task-pushing menu; that is handled by Task Pushing 01.
|
||||
8. No backup/restore or export/import UI.
|
||||
|
||||
---
|
||||
|
||||
## Architecture rule
|
||||
|
||||
Date selection is UI/session state. Scheduling truth remains in the backend
|
||||
read/command layer:
|
||||
|
||||
```text
|
||||
TopBar date controls
|
||||
-> selected-date controller/state
|
||||
-> Today read controller executes GetTodayStateQuery(date)
|
||||
-> command controller passes selected CivilDate to use cases
|
||||
-> ApplicationUnitOfWork persists domain changes
|
||||
-> UI refreshes from read models
|
||||
```
|
||||
|
||||
The selected date may choose which read model to request. It must not cause UI
|
||||
widgets to compute task placement or conflict resolution.
|
||||
|
||||
---
|
||||
|
||||
## Important implementation decision
|
||||
|
||||
Do not make `DemoSchedulerComposition.date` the durable date-navigation model.
|
||||
After Persistence Plan 1, the app should have a real runtime composition that can
|
||||
create read and command controllers for whichever date is currently selected.
|
||||
Use a selected-date controller or date-aware Today controller so date changes are
|
||||
explicit, testable, and not hidden in widget-local rebuilds.
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Date Selection 01 — Day Navigation and Date Picker
|
||||
|
||||
**Status:** Planned. Execute after `Persistence Plan 1 - SQLite Runtime Persistence`.
|
||||
**Scope level:** XHIGH implementation plan.
|
||||
|
||||
This plan makes the compact Today timeline date-aware. The top-bar arrows move
|
||||
backward and forward by day. The displayed date becomes a button that opens a
|
||||
date picker so the user can jump directly to another day.
|
||||
|
||||
The selected day must flow through the existing backend read and command
|
||||
boundaries. The UI must not duplicate scheduling rules or create a second source
|
||||
of task state.
|
||||
|
||||
## Files in this plan
|
||||
|
||||
1. `DATE_SELECTION_01_SUMMARY.md`
|
||||
2. `DATE_SELECTION_01_EXECUTION_ORDER.md`
|
||||
3. `DATE_SELECTION_01_BLOCK_01_SCOPE_AND_BASELINE.md`
|
||||
4. `DATE_SELECTION_01_BLOCK_02_SELECTED_DATE_STATE_AND_READS.md`
|
||||
5. `DATE_SELECTION_01_BLOCK_03_TOP_BAR_NAVIGATION_AND_PICKER.md`
|
||||
6. `DATE_SELECTION_01_BLOCK_04_DATE_AWARE_COMMANDS_AND_PERSISTENCE.md`
|
||||
7. `DATE_SELECTION_01_BLOCK_05_COMPLETION_DATE_PRESENTATION.md`
|
||||
8. `DATE_SELECTION_01_BLOCK_06_VALIDATION_HANDOFF.md`
|
||||
|
||||
## Dependency
|
||||
|
||||
Persistence Plan 1 must be complete first so this plan can validate real
|
||||
close/reopen behavior across multiple dates instead of demo seed data.
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Task Pushing 01 — Past Task Push Controls
|
||||
|
||||
**Status:** Planned. Execute after Date Selection 01.
|
||||
**Scope level:** XHIGH implementation plan.
|
||||
|
||||
This plan adds the push affordance for scheduled tasks that are in the past and
|
||||
not complete. The push button replaces hover quick actions while visible and
|
||||
opens a destination menu with:
|
||||
|
||||
1. Push to next,
|
||||
2. Push to tomorrow,
|
||||
3. Push to backlog.
|
||||
|
||||
Push behavior must go through the existing scheduler command/use-case layer and
|
||||
persist through SQLite. The UI must not compute or save canonical schedule
|
||||
placement.
|
||||
|
||||
## Files in this plan
|
||||
|
||||
1. `TASK_PUSHING_01_SUMMARY.md`
|
||||
2. `TASK_PUSHING_01_EXECUTION_ORDER.md`
|
||||
3. `TASK_PUSHING_01_BLOCK_01_SCOPE_AND_DOMAIN_SEMANTICS.md`
|
||||
4. `TASK_PUSHING_01_BLOCK_02_PAST_TASK_READ_MODEL.md`
|
||||
5. `TASK_PUSHING_01_BLOCK_03_PUSH_BUTTON_CARD_UI.md`
|
||||
6. `TASK_PUSHING_01_BLOCK_04_PUSH_MENU_DESTINATIONS.md`
|
||||
7. `TASK_PUSHING_01_BLOCK_05_COMMAND_WIRING_AND_PERSISTENCE.md`
|
||||
8. `TASK_PUSHING_01_BLOCK_06_DESTINATION_SCHEDULING_RULES.md`
|
||||
9. `TASK_PUSHING_01_BLOCK_07_BACKLOG_FRESHNESS_RESET.md`
|
||||
10. `TASK_PUSHING_01_BLOCK_08_VALIDATION_HANDOFF.md`
|
||||
|
||||
## Dependency
|
||||
|
||||
Task Pushing 01 requires:
|
||||
|
||||
1. Persistence Plan 1 complete.
|
||||
2. Date Selection 01 complete.
|
||||
|
||||
The push menu depends on multi-day reads because push destinations can move a
|
||||
task away from the currently visible day.
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Task Pushing 01 Block 01 — Scope and Domain Semantics
|
||||
|
||||
**Status:** Planned.
|
||||
**Goal:** Confirm the exact pushable task scope and lock destination semantics
|
||||
before adding visible controls.
|
||||
|
||||
---
|
||||
|
||||
## Block outcome
|
||||
|
||||
After this block, the implementation should know which cards can show `Push`,
|
||||
what `past` means, and what each destination must do.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 1.1 — Confirm prerequisites
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Confirm Persistence Plan 1 is complete.
|
||||
2. Confirm Date Selection 01 is complete.
|
||||
3. Verify the app can load arbitrary selected dates from persisted SQLite state.
|
||||
4. Verify command controllers refresh the selected date after mutations.
|
||||
5. Verify task completion and uncompletion persist across close/reopen.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Task Pushing 01 starts from a date-aware, persistence-backed UI.
|
||||
- Any missing prerequisite behavior is fixed before continuing.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 1.2 — Verify pushable task types
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Inspect current scheduler-core command support for push destinations.
|
||||
2. Confirm that V1 push support is planned flexible tasks only unless explicitly
|
||||
expanded in this block.
|
||||
3. Decide how to present past incomplete non-flexible tasks for this plan:
|
||||
- preferred V1: keep normal non-push UI and do not show the Push button,
|
||||
- alternative: add backend-supported required-task push semantics with tests
|
||||
before exposing UI.
|
||||
4. Document the decision in the block handoff notes.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- The UI never calls a flexible-task-only command for non-flexible tasks.
|
||||
- Unsupported task types do not show the Push button.
|
||||
- Any expanded support is backed by scheduler-core tests first.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 1.3 — Define `past incomplete` precisely
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Use owner-local current time as the clock reference.
|
||||
2. A planned flexible task is past/incomplete when:
|
||||
- it is not completed,
|
||||
- it has scheduled placement,
|
||||
- its scheduled end is before the current owner-local instant, or the selected
|
||||
date is before the current owner-local date and the scheduled task remains
|
||||
incomplete.
|
||||
3. Future selected-day tasks are not past unless the real current time is already
|
||||
after their scheduled end.
|
||||
4. A completed task is never in push-button state.
|
||||
5. A backlog task is never in push-button state because it is not on the
|
||||
timeline.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Detection works for earlier-today tasks.
|
||||
- Detection works for previous-day incomplete tasks.
|
||||
- Detection does not trigger for future tasks.
|
||||
- Detection clears immediately after completion refresh.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 1.4 — Lock push destination semantics
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Define Push to next:
|
||||
- schedule the task into the next valid no-overlap slot after current
|
||||
owner-local time,
|
||||
- use later today when a valid slot exists,
|
||||
- otherwise move to the next future valid slot allowed by backend rules.
|
||||
2. Define Push to tomorrow:
|
||||
- target the next owner-local civil day relative to current owner-local date,
|
||||
- schedule into the first valid no-overlap slot for that day.
|
||||
3. Define Push to backlog:
|
||||
- set task status to Backlog,
|
||||
- clear scheduled start/end,
|
||||
- reset backlog freshness anchor to current time,
|
||||
- preserve original creation timestamp.
|
||||
4. Confirm no destination uses widget-side slot calculation.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Destination semantics are testable in core/application tests.
|
||||
- Current selected date does not override the meaning of current time.
|
||||
- Push to tomorrow is relative to the real owner-local current date, not the
|
||||
historical selected date being reviewed.
|
||||
|
||||
---
|
||||
|
||||
## Block acceptance criteria
|
||||
|
||||
- Pushable scope is explicit.
|
||||
- Past/incomplete detection rules are explicit.
|
||||
- Destination semantics are explicit.
|
||||
- Any required core behavior changes are identified before UI work.
|
||||
|
||||
## Validation after block
|
||||
|
||||
```sh
|
||||
cd packages/scheduler_core
|
||||
dart analyze
|
||||
dart test
|
||||
```
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Task Pushing 01 Block 02 — Past Task Read Model
|
||||
|
||||
**Status:** Planned.
|
||||
**Goal:** Add presentation-model state that tells widgets when to show the full
|
||||
Push button.
|
||||
|
||||
---
|
||||
|
||||
## Block outcome
|
||||
|
||||
After this block, `TimelineCardModel` or its successor exposes a clear push-state
|
||||
field. Leaf widgets should not recompute past/incomplete scheduling conditions.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 2.1 — Add push-state presentation fields
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Add a compact, explicit card field such as:
|
||||
- `pastActionState`,
|
||||
- `showPastTaskPushButton`, or
|
||||
- `pushControlState`.
|
||||
2. Include enough information for UI callbacks:
|
||||
- task id,
|
||||
- expected updated timestamp/revision input if available,
|
||||
- enabled/disabled state,
|
||||
- semantic label.
|
||||
3. Keep the field false/null for completed tasks.
|
||||
4. Keep the field false/null for unsupported task types.
|
||||
5. Keep the field false/null for backlog/free-slot/locked cards.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Widgets can render the Push button from presentation data alone.
|
||||
- Presentation data does not expose Drift rows or repository records.
|
||||
- Completed cards cannot accidentally show the Push button.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 2.2 — Provide clock and selected-date context to the mapper
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Update Today-state-to-card mapping to receive current owner-local clock
|
||||
context, if it does not already have it.
|
||||
2. Avoid using `DateTime.now()` directly inside presentation mappers unless a
|
||||
testable clock is injected.
|
||||
3. Compare scheduled instants with current owner-local time using existing time
|
||||
conversion utilities.
|
||||
4. Make selected date available to the mapper only if needed for previous-day
|
||||
detection.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Mapper tests can freeze time.
|
||||
- Past detection is deterministic.
|
||||
- UTC/local date boundaries are handled through project time semantics.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 2.3 — Add mapper tests
|
||||
|
||||
### Required tests
|
||||
|
||||
1. Flexible planned task earlier today and incomplete shows Push state.
|
||||
2. Flexible planned task later today does not show Push state.
|
||||
3. Flexible planned task on a previous selected date and incomplete shows Push
|
||||
state.
|
||||
4. Flexible planned task on a future selected date does not show Push state.
|
||||
5. Completed flexible task in the past does not show Push state.
|
||||
6. Required/locked/free-slot/surprise cards do not show Push state unless Block
|
||||
01 explicitly expanded support.
|
||||
7. Task without valid schedule placement does not show Push state.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Tests cover each state branch.
|
||||
- No widget test is needed to prove core push-state calculation.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 2.4 — Connect push state to quick-action suppression
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Update `TimelineCardModel.showsQuickActions` or equivalent to return false
|
||||
when the full Push button should show.
|
||||
2. Ensure hover actions remain available for normal planned flexible tasks.
|
||||
3. Ensure completed cards continue to use completed behavior, not push behavior.
|
||||
4. Keep the selected-task modal behavior unchanged unless the modal also needs a
|
||||
push menu later.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Past incomplete cards expose Push state and suppress hover actions.
|
||||
- Non-past flexible cards keep existing hover quick actions.
|
||||
- Completed cards do not show push controls or hover quick actions intended for
|
||||
planned tasks.
|
||||
|
||||
---
|
||||
|
||||
## Block acceptance criteria
|
||||
|
||||
- Past-task push state exists in presentation models.
|
||||
- Detection is deterministic and test-covered.
|
||||
- Hover quick-action suppression is model-driven.
|
||||
|
||||
## Validation after block
|
||||
|
||||
```sh
|
||||
cd apps/focus_flow_flutter
|
||||
flutter test test/models
|
||||
```
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Task Pushing 01 Block 03 — Push Button Card UI
|
||||
|
||||
**Status:** Planned.
|
||||
**Goal:** Render the full right-side Push button on past incomplete cards.
|
||||
|
||||
---
|
||||
|
||||
## Block outcome
|
||||
|
||||
After this block, past incomplete planned flexible cards visibly show a `Push`
|
||||
button beside the reward/difficulty icons. The menu may still be stubbed until
|
||||
Block 04.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 3.1 — Place the Push button in trailing controls
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Add a compact `Push` button component in the timeline card trailing-controls
|
||||
area.
|
||||
2. Position it next to reward and difficulty icons as requested.
|
||||
3. Give it visual priority over time text if the layout overlaps.
|
||||
4. Keep card height and density consistent with the compact timeline.
|
||||
5. Use calm styling that reads as actionable without becoming visually loud.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Push button appears at the right side of eligible cards.
|
||||
- Reward/difficulty icons remain visible and aligned.
|
||||
- The time label does not visually cover the button.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 3.2 — Implement z-order and hover suppression
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Ensure the Push button is painted above any time text or hover affordance that
|
||||
would otherwise overlap it.
|
||||
2. Disable hover quick-action rendering while the Push button is visible.
|
||||
3. Confirm normal hover actions still appear on non-past eligible flexible tasks.
|
||||
4. Confirm the Push button remains clickable when the card is selected/hovered.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- The Push button wins hit testing in its visible area.
|
||||
- Hover controls do not appear over or under the Push button.
|
||||
- No double-action UI appears for the same card.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 3.3 — Add accessibility and keyboard behavior
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Add a tooltip and semantic label such as `Push overdue task`.
|
||||
2. Ensure the button can receive keyboard focus.
|
||||
3. Ensure pressing Enter/Space triggers the same callback as clicking.
|
||||
4. Ensure disabled/unavailable push state, if any, is announced correctly.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Keyboard and screen-reader users can discover and activate Push.
|
||||
- The button label is specific enough to distinguish it from hover quick actions.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 3.4 — Add visual/widget tests
|
||||
|
||||
### Required tests
|
||||
|
||||
1. Past incomplete flexible card renders Push.
|
||||
2. Completed past flexible card does not render Push.
|
||||
3. Future flexible card does not render Push.
|
||||
4. Past incomplete flexible card does not render hover quick actions.
|
||||
5. Normal planned flexible card still renders hover quick actions when hovered.
|
||||
6. Push button callback fires once when tapped.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- UI behavior is covered without requiring real SQLite.
|
||||
- Tests use presentation models rather than hand-building domain scheduler state
|
||||
inside widgets.
|
||||
|
||||
---
|
||||
|
||||
## Block acceptance criteria
|
||||
|
||||
- Push button renders in the correct card state.
|
||||
- Push button placement and z-order satisfy the visual requirement.
|
||||
- Hover quick actions are suppressed only while Push is visible.
|
||||
- Accessibility behavior is covered.
|
||||
|
||||
## Validation after block
|
||||
|
||||
```sh
|
||||
cd apps/focus_flow_flutter
|
||||
flutter test test/widgets/timeline
|
||||
```
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Task Pushing 01 Block 04 — Push Menu Destinations
|
||||
|
||||
**Status:** Planned.
|
||||
**Goal:** Open a destination menu from the Push button and route each choice to a
|
||||
callback.
|
||||
|
||||
---
|
||||
|
||||
## Block outcome
|
||||
|
||||
After this block, clicking `Push` opens a menu with the three requested choices.
|
||||
The choices may call stubbed/controller callbacks until Block 05 wires real
|
||||
commands.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 4.1 — Add destination menu UI
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Add a small menu anchored to the Push button.
|
||||
2. Include exactly these visible labels:
|
||||
- `Push to next`,
|
||||
- `Push to tomorrow`,
|
||||
- `Push to backlog`.
|
||||
3. Keep menu styling consistent with the dark compact UI.
|
||||
4. Use enough spacing for desktop pointer targets without making the card feel
|
||||
heavy.
|
||||
5. Ensure the menu can open above/below the card depending on available space.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Menu opens from the Push button.
|
||||
- Menu contains all three requested labels.
|
||||
- Menu does not obscure the button in a way that prevents closing/reopening.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 4.2 — Add destination callback surface
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Add callback signatures to the card/timeline path for:
|
||||
- push to next,
|
||||
- push to tomorrow,
|
||||
- push to backlog.
|
||||
2. Pass task id and expected update metadata required for stale-write protection.
|
||||
3. Keep callbacks nullable only for tests/previews that intentionally disable
|
||||
command wiring.
|
||||
4. Do not pass raw domain `Task` objects into leaf widgets unless existing
|
||||
architecture already does.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Each menu item calls the correct destination callback.
|
||||
- Callback payload includes enough task identity/update metadata to run a safe
|
||||
command.
|
||||
- Widgets do not know command-use-case class names.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 4.3 — Handle menu dismissal and command-running state
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Close the menu when a destination is selected.
|
||||
2. Close the menu on outside click or Escape.
|
||||
3. Decide whether command-running state disables menu re-entry globally or per
|
||||
card.
|
||||
4. Prevent double-submission if the user rapidly clicks a destination twice.
|
||||
5. Show existing command failure/success UI if available; do not add a new
|
||||
notification framework in this plan.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Destination selection is one-shot until the command result returns.
|
||||
- Cancel/dismiss performs no task mutation.
|
||||
- Existing command state surfaces are reused.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 4.4 — Add menu widget tests
|
||||
|
||||
### Required tests
|
||||
|
||||
1. Tap Push opens menu.
|
||||
2. Menu has exactly the three destination labels.
|
||||
3. Tapping each item invokes the matching callback.
|
||||
4. Escape/outside click closes menu without callback.
|
||||
5. Rapid double tap does not invoke duplicate commands if command-running state
|
||||
is available at this layer.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Destination menu behavior is covered before backend wiring.
|
||||
- Tests do not require a live scheduler engine.
|
||||
|
||||
---
|
||||
|
||||
## Block acceptance criteria
|
||||
|
||||
- Push menu appears and dismisses correctly.
|
||||
- Each destination has a distinct callback path.
|
||||
- No backend scheduling logic is introduced into widgets.
|
||||
|
||||
## Validation after block
|
||||
|
||||
```sh
|
||||
cd apps/focus_flow_flutter
|
||||
flutter test test/widgets/timeline
|
||||
```
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Task Pushing 01 Block 05 — Command Wiring and Persistence
|
||||
|
||||
**Status:** Planned.
|
||||
**Goal:** Connect push menu destinations to application commands and prove their
|
||||
results persist.
|
||||
|
||||
---
|
||||
|
||||
## Block outcome
|
||||
|
||||
After this block, selecting a push destination runs the backend command through
|
||||
`SchedulerCommandController`, refreshes the selected date, and persists the
|
||||
result through SQLite.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 5.1 — Add command-controller push methods
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Add command-controller methods for:
|
||||
- `pushTaskToNextAvailableSlot`,
|
||||
- `pushTaskToTomorrow`,
|
||||
- `pushTaskToBacklog`.
|
||||
2. Use current owner-local time from the runtime clock for command context.
|
||||
3. Pass selected/current planning dates according to Block 01 semantics.
|
||||
4. Preserve stale-write checks using card metadata where available.
|
||||
5. Reuse existing `_run` command-state handling and read refresh behavior.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Push methods expose no Flutter widget dependencies.
|
||||
- Push methods call application use cases, not scheduler engine directly.
|
||||
- Push command failures flow through existing command failure state.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 5.2 — Wire menu callbacks through the screen tree
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Thread push callbacks from `FocusFlowHome` or equivalent through
|
||||
`_TodayScreenScaffold`, `TimelineView`, and card widgets.
|
||||
2. Keep callback names destination-specific.
|
||||
3. Refresh the currently selected date after a successful command.
|
||||
4. Clear selected card/modal if the pushed task leaves the selected day.
|
||||
5. Keep Backlog refresh behavior aligned with existing Backlog controller if one
|
||||
is visible in this UI slice.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Each menu item reaches the correct command-controller method.
|
||||
- The visible timeline refreshes after success.
|
||||
- A task pushed out of the current date disappears or moves according to backend
|
||||
read state.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 5.3 — Use existing application use cases where semantics match
|
||||
|
||||
### Tasks
|
||||
|
||||
1. For Push to next, prefer `pushFlexibleToNextAvailableSlot` if Block 06
|
||||
confirms it honors no-before-now semantics.
|
||||
2. For Push to tomorrow, prefer `pushFlexibleToTomorrowTopOfQueue` if Block 06
|
||||
confirms it places into the first valid no-overlap slot on the next day.
|
||||
3. For Push to backlog, choose one path and document it:
|
||||
- use existing `moveFlexibleToBacklog`, or
|
||||
- add a command that routes through `PushDestination.backlog` for consistent
|
||||
push-destination accounting.
|
||||
4. Do not add duplicate command implementations if existing use cases already
|
||||
satisfy the product behavior.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Command wiring uses the smallest backend surface that satisfies the requested
|
||||
behavior.
|
||||
- Any semantic gap is moved to Block 06 or Block 07 rather than patched in UI.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 5.4 — Add close/reopen persistence tests for destinations
|
||||
|
||||
### Required tests
|
||||
|
||||
1. Push to next:
|
||||
- start with an incomplete past flexible task,
|
||||
- push to next,
|
||||
- close/reopen,
|
||||
- assert the task is planned in the persisted new slot.
|
||||
2. Push to tomorrow:
|
||||
- start with an incomplete past flexible task,
|
||||
- push to tomorrow,
|
||||
- close/reopen,
|
||||
- load tomorrow,
|
||||
- assert the task is planned there.
|
||||
3. Push to backlog:
|
||||
- start with an incomplete past flexible task,
|
||||
- push to backlog,
|
||||
- close/reopen,
|
||||
- assert status is Backlog and schedule fields are cleared.
|
||||
4. Completion after push:
|
||||
- push a task,
|
||||
- complete it,
|
||||
- assert Push button disappears after refresh.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Every destination persists through SQLite.
|
||||
- Tests use on-disk temp SQLite and close handles before reopen assertions.
|
||||
- UI/controller tests and backend persistence tests agree on destination results.
|
||||
|
||||
---
|
||||
|
||||
## Block acceptance criteria
|
||||
|
||||
- Push menu destinations are wired to backend commands.
|
||||
- Results persist and reload.
|
||||
- Selected-date read refresh shows the backend result.
|
||||
- No widget computes or saves canonical schedule placement.
|
||||
|
||||
## Validation after block
|
||||
|
||||
```sh
|
||||
cd packages/scheduler_persistence_sqlite
|
||||
dart test
|
||||
cd ../../apps/focus_flow_flutter
|
||||
flutter test
|
||||
```
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Task Pushing 01 Block 06 — Destination Scheduling Rules
|
||||
|
||||
**Status:** Planned.
|
||||
**Goal:** Verify or adjust backend push scheduling so destination behavior exactly
|
||||
matches the requested product rules.
|
||||
|
||||
---
|
||||
|
||||
## Block outcome
|
||||
|
||||
After this block, the scheduler/application layer should guarantee no-overlap,
|
||||
no-before-now push placement. UI tests should not need to compensate for backend
|
||||
semantics.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 6.1 — Verify Push to next behavior
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Add scheduler-core/application tests for a past incomplete flexible task with
|
||||
an open slot later today.
|
||||
2. Assert Push to next places the task in the later-today slot.
|
||||
3. Add a test where no later-today slot exists.
|
||||
4. Assert the result is either the next backend-supported future slot or a clear
|
||||
no-fit result, depending on confirmed scheduler horizon rules.
|
||||
5. Assert placement never overlaps required, locked, or already-planned tasks.
|
||||
6. Assert placement never lands before current owner-local time.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Push to next satisfies same-day-if-available behavior.
|
||||
- Push to next never moves into the past.
|
||||
- Any no-fit response keeps the original task safe and persisted.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 6.2 — Verify Push to tomorrow behavior
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Add tests for targeting the next owner-local civil day relative to current
|
||||
time.
|
||||
2. Assert placement uses the first valid no-overlap slot on that day.
|
||||
3. Add locked/required-time conflicts and assert they are skipped.
|
||||
4. Add a no-fit tomorrow case and assert the task remains safe with a clear
|
||||
failure/no-fit result.
|
||||
5. Confirm selected historical/future date does not accidentally redefine
|
||||
`tomorrow` for overdue recovery.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Push to tomorrow targets real owner-local tomorrow.
|
||||
- Push to tomorrow avoids overlaps.
|
||||
- Push to tomorrow does not depend on the currently selected review date unless
|
||||
Block 01 deliberately chose that behavior.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 6.3 — Adjust backend only if tests reveal semantic gaps
|
||||
|
||||
### Tasks
|
||||
|
||||
1. If existing commands already pass the tests, do not rewrite them.
|
||||
2. If Push to next can place before current time, add an application/core anchor
|
||||
parameter rather than filtering in UI.
|
||||
3. If Push to tomorrow means top-of-queue but not first open slot, update core
|
||||
semantics or add a clearly named command matching the requested behavior.
|
||||
4. If no-fit behavior is ambiguous, return an `ApplicationFailure`/notice that
|
||||
the existing UI can show calmly.
|
||||
5. Update command/read-hint behavior only as needed to refresh affected dates.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Any semantic fix lives in scheduler/application code, not widgets.
|
||||
- Existing push tests still pass or are updated for the new explicit semantics.
|
||||
- New behavior is covered before UI integration is considered complete.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 6.4 — Verify scheduling-change accounting
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Assert manually pushed selected task records the correct activity/statistics.
|
||||
2. Assert automatically moved downstream tasks record automatic-push activity.
|
||||
3. Assert Push to backlog records moved-to-backlog activity and any explicit
|
||||
freshness reset metadata added in Block 07.
|
||||
4. Ensure duplicate operation ids remain idempotent/rejected according to the
|
||||
application command contract.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Push activity/statistics remain durable and consistent.
|
||||
- Downstream movement is auditable.
|
||||
- Duplicate operation behavior remains safe across close/reopen.
|
||||
|
||||
---
|
||||
|
||||
## Block acceptance criteria
|
||||
|
||||
- Push destination scheduling rules are guaranteed by backend tests.
|
||||
- No-overlap and no-before-now behavior is proven.
|
||||
- No-fit behavior keeps tasks safe.
|
||||
- Activity/statistics accounting remains consistent.
|
||||
|
||||
## Validation after block
|
||||
|
||||
```sh
|
||||
cd packages/scheduler_core
|
||||
dart analyze
|
||||
dart test
|
||||
cd ../scheduler_persistence_sqlite
|
||||
dart test
|
||||
```
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Task Pushing 01 Block 07 — Backlog Freshness Reset
|
||||
|
||||
**Status:** Planned.
|
||||
**Goal:** Make Push to backlog reset the backlog freshness timer without losing
|
||||
original task creation history.
|
||||
|
||||
---
|
||||
|
||||
## Block outcome
|
||||
|
||||
After this block, a task pushed to Backlog appears fresh in Backlog even if it
|
||||
was originally created long ago.
|
||||
|
||||
---
|
||||
|
||||
## Current issue
|
||||
|
||||
Current backlog staleness behavior uses task creation age. That cannot satisfy
|
||||
`Push to backlog` freshness reset because changing `createdAt` would erase useful
|
||||
history/reporting truth.
|
||||
|
||||
This block should introduce or verify a separate backlog freshness anchor.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 7.1 — Choose freshness-anchor model
|
||||
|
||||
### Preferred model
|
||||
|
||||
Add an explicit domain/app field such as `backlogEnteredAt` or
|
||||
`backlogFreshnessAnchorAt`.
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Choose a name that expresses UI freshness without pretending to be original
|
||||
creation time.
|
||||
2. Store the value when:
|
||||
- task is quick-captured into Backlog,
|
||||
- scheduled task is pushed/moved to Backlog,
|
||||
- any future command deliberately re-enters Backlog.
|
||||
3. Clear or ignore the value when a task is planned/completed, depending on the
|
||||
simplest domain invariant.
|
||||
4. Update `BacklogView` and staleness marker logic to use the freshness anchor
|
||||
when present, falling back to `createdAt` for older data.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Original `createdAt` remains unchanged when pushing to Backlog.
|
||||
- Backlog staleness marker can become fresh after push-to-backlog.
|
||||
- Older persisted tasks without the new field still load safely.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 7.2 — Update persistence mapping and migrations
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Add the freshness-anchor field to persistence contracts/mappers.
|
||||
2. If schema v1 has not shipped yet, include the field in the current task schema
|
||||
with tests.
|
||||
3. If schema v1 has already landed, add a Drift migration to the next schema
|
||||
version.
|
||||
4. Add round-trip tests for:
|
||||
- null freshness anchor,
|
||||
- quick-captured freshness anchor,
|
||||
- push-to-backlog freshness anchor.
|
||||
5. Ensure JSON/CSV export behavior is not silently broken if export packages map
|
||||
task fields.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- SQLite persists the freshness anchor.
|
||||
- Migration or schema tests cover existing databases.
|
||||
- Domain-to-row and row-to-domain mapping are symmetric.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 7.3 — Update transition services
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Update quick capture to initialize the freshness anchor to capture time for
|
||||
backlog tasks.
|
||||
2. Update move/push-to-backlog transitions to set freshness anchor to command
|
||||
time.
|
||||
3. Update restore-from-backlog/scheduling transitions to clear or preserve the
|
||||
anchor according to the invariant chosen in Chunk 7.1.
|
||||
4. Ensure accounting/statistics still records moved-to-backlog separately from
|
||||
freshness.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Push to backlog sets freshness anchor to current command time.
|
||||
- Repeated push-to-backlog operations do not corrupt task statistics.
|
||||
- Scheduling a backlog task still clears schedule/backlog state consistently.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 7.4 — Add freshness tests
|
||||
|
||||
### Required tests
|
||||
|
||||
1. Old task created 60 days ago is stale in Backlog before push-reset behavior.
|
||||
2. Same old task pushed to Backlog now becomes fresh.
|
||||
3. Close/reopen preserves the fresh marker.
|
||||
4. Original `createdAt` is unchanged after push to Backlog.
|
||||
5. Sorting/filtering by age uses the chosen freshness behavior consistently.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Backlog marker and stale filter do not disagree.
|
||||
- Freshness reset survives persistence.
|
||||
- No report/history field is overwritten to fake freshness.
|
||||
|
||||
---
|
||||
|
||||
## Block acceptance criteria
|
||||
|
||||
- Push to Backlog resets backlog freshness.
|
||||
- Original creation time is preserved.
|
||||
- Persistence and migrations support the freshness anchor.
|
||||
- Backlog read models use the new freshness semantics consistently.
|
||||
|
||||
## Validation after block
|
||||
|
||||
```sh
|
||||
cd packages/scheduler_core
|
||||
dart test
|
||||
cd ../scheduler_persistence_sqlite
|
||||
dart test
|
||||
cd ../scheduler_export
|
||||
dart test
|
||||
```
|
||||
|
||||
Run export package tests only if task-field mapping is touched there.
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Task Pushing 01 Block 08 — Validation and Handoff
|
||||
|
||||
**Status:** Planned.
|
||||
**Goal:** Prove task pushing works end to end and close the plan cleanly.
|
||||
|
||||
---
|
||||
|
||||
## Block outcome
|
||||
|
||||
After this block, Task Pushing 01 should be complete. Past incomplete planned
|
||||
flexible tasks can be pushed to next, tomorrow, or Backlog with durable results.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 8.1 — Add end-to-end UI flow tests
|
||||
|
||||
### Required tests
|
||||
|
||||
1. Load a selected date with an incomplete past flexible task.
|
||||
2. Assert the card shows `Push` and suppresses hover quick actions.
|
||||
3. Open the Push menu and choose `Push to next`.
|
||||
4. Assert the selected-date timeline refreshes with the backend result.
|
||||
5. Repeat for `Push to tomorrow` and assert the task appears when tomorrow is
|
||||
selected.
|
||||
6. Repeat for `Push to backlog` and assert the task leaves the timeline and
|
||||
appears in Backlog as fresh where Backlog UI is covered.
|
||||
7. Complete a past task and assert the Push button disappears.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- UI flow tests cover all three destinations.
|
||||
- Tests use command-controller seams or a temp runtime, not widget-side schedule
|
||||
mutation.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 8.2 — Add lifecycle persistence tests
|
||||
|
||||
### Required tests
|
||||
|
||||
1. Push to next, close/reopen, load destination date, assert task placement.
|
||||
2. Push to tomorrow, close/reopen, load tomorrow, assert task placement.
|
||||
3. Push to backlog, close/reopen, load Backlog, assert status, cleared schedule,
|
||||
and fresh staleness marker.
|
||||
4. Attempt no-fit push, close/reopen, assert the original task remains safe and
|
||||
unchanged except for any explicitly intended notice/operation record.
|
||||
5. Attempt duplicate operation id after reopen and assert duplicate behavior is
|
||||
safe.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Persistence is proven through on-disk temp SQLite files.
|
||||
- Database/runtime handles are explicitly closed before reopen assertions.
|
||||
- No destination relies on in-memory-only state.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 8.3 — Run validation gates
|
||||
|
||||
### Commands
|
||||
|
||||
Run the strongest available validation set:
|
||||
|
||||
```sh
|
||||
scripts/test.sh
|
||||
cd packages/scheduler_core
|
||||
dart analyze
|
||||
dart test
|
||||
cd ../scheduler_persistence_sqlite
|
||||
dart analyze
|
||||
dart test
|
||||
cd ../../apps/focus_flow_flutter
|
||||
flutter analyze
|
||||
flutter test
|
||||
```
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- All available gates pass.
|
||||
- If a command is unavailable in the environment, record the exact command and
|
||||
failure reason in the plan handoff notes.
|
||||
|
||||
---
|
||||
|
||||
## Chunk 8.4 — Update minimal handoff/status docs
|
||||
|
||||
### Tasks
|
||||
|
||||
1. Update this plan's summary/status when complete.
|
||||
2. Move the plan folder only if the project's completed-plan workflow calls for
|
||||
it.
|
||||
3. Update `Codex Documentation/Current Software Plan/README.md` to identify the
|
||||
next planned implementation area, if known.
|
||||
4. Do not create a broad overall report.
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- The handoff records changed files, decisions, validation, and unverified gates.
|
||||
- No unrelated docs are rewritten.
|
||||
|
||||
---
|
||||
|
||||
## Final plan acceptance criteria
|
||||
|
||||
- Past incomplete planned flexible cards show a full Push button.
|
||||
- Push button suppresses hover quick actions.
|
||||
- Push menu exposes the three requested destinations.
|
||||
- Push destinations go through backend commands.
|
||||
- Push results persist through SQLite.
|
||||
- Push to Backlog resets freshness without rewriting creation history.
|
||||
- Validation status is documented.
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Task Pushing 01 Execution Order
|
||||
|
||||
**Status:** Planned.
|
||||
|
||||
Run these files in order. All chunks are intended for `XHIGH` execution unless a
|
||||
chunk explicitly says otherwise.
|
||||
|
||||
1. `TASK_PUSHING_01_BLOCK_01_SCOPE_AND_DOMAIN_SEMANTICS.md`
|
||||
- Confirm Date Selection 01 and persistence prerequisites.
|
||||
- Verify flexible-task-only scope or explicitly expand backend support.
|
||||
- Lock current-time and destination semantics.
|
||||
|
||||
2. `TASK_PUSHING_01_BLOCK_02_PAST_TASK_READ_MODEL.md`
|
||||
- Add past/incomplete push state to read/presentation models.
|
||||
- Keep detection out of leaf widgets.
|
||||
- Add mapper tests for today, previous day, future day, completed, and
|
||||
unsupported task types.
|
||||
|
||||
3. `TASK_PUSHING_01_BLOCK_03_PUSH_BUTTON_CARD_UI.md`
|
||||
- Add the full right-side Push button.
|
||||
- Place it beside reward/difficulty icons with higher z priority than time
|
||||
text.
|
||||
- Suppress hover quick actions while visible.
|
||||
|
||||
4. `TASK_PUSHING_01_BLOCK_04_PUSH_MENU_DESTINATIONS.md`
|
||||
- Add the destination menu.
|
||||
- Wire menu selection to controller callbacks.
|
||||
- Preserve keyboard and click-away behavior.
|
||||
|
||||
5. `TASK_PUSHING_01_BLOCK_05_COMMAND_WIRING_AND_PERSISTENCE.md`
|
||||
- Add Flutter command-controller push methods.
|
||||
- Call application use cases through the selected-date/runtime context.
|
||||
- Validate close/reopen persistence after each destination.
|
||||
|
||||
6. `TASK_PUSHING_01_BLOCK_06_DESTINATION_SCHEDULING_RULES.md`
|
||||
- Verify or adjust core command behavior for next/tomorrow placement.
|
||||
- Ensure no-overlap and no-before-now behavior.
|
||||
- Add backend tests before relying on UI tests.
|
||||
|
||||
7. `TASK_PUSHING_01_BLOCK_07_BACKLOG_FRESHNESS_RESET.md`
|
||||
- Add or verify a separate backlog freshness anchor.
|
||||
- Reset it when pushing to Backlog.
|
||||
- Preserve original task creation time for history/reporting.
|
||||
|
||||
8. `TASK_PUSHING_01_BLOCK_08_VALIDATION_HANDOFF.md`
|
||||
- Add end-to-end widget/controller/lifecycle tests.
|
||||
- Run validation gates.
|
||||
- Update minimal status docs.
|
||||
|
||||
Do not implement broad task editing, drag/drop, Shield, or week/month planning in
|
||||
this plan.
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
<!-- SPDX-FileCopyrightText: 2026 FocusFlow contributors -->
|
||||
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
|
||||
|
||||
|
||||
# Task Pushing 01 Summary — Past Task Push Controls
|
||||
|
||||
**Status:** Planned.
|
||||
**Scope level:** XHIGH implementation plan.
|
||||
**Primary outcome:** Past, incomplete scheduled tasks expose a persistent push
|
||||
control that can move the task to the next available slot, tomorrow's first open
|
||||
slot, or Backlog.
|
||||
|
||||
---
|
||||
|
||||
## Current repo facts this plan is based on
|
||||
|
||||
1. Scheduler core already has flexible-task push destinations:
|
||||
`nextAvailableSlot`, `tomorrowTopOfQueue`, and `backlog`.
|
||||
2. `V1ApplicationCommandUseCases` already exposes push-related commands for
|
||||
planned flexible tasks:
|
||||
- `pushFlexibleToNextAvailableSlot`,
|
||||
- `pushFlexibleToTomorrowTopOfQueue`,
|
||||
- `moveFlexibleToBacklog`.
|
||||
3. The Flutter command controller currently wires completion and scheduling, but
|
||||
push methods are not exposed to card widgets.
|
||||
4. Timeline cards already have reward and difficulty controls in the trailing
|
||||
area.
|
||||
5. Timeline cards already have `showsQuickActions`, but the new past-task push
|
||||
state must suppress hover actions while the full push button is visible.
|
||||
6. Current backlog staleness uses task creation age; this does not satisfy the
|
||||
requested behavior that pushing to Backlog resets the backlog freshness timer.
|
||||
7. Date Selection 01 should make selected-day reloads and cross-date persistence
|
||||
reliable before this plan starts.
|
||||
|
||||
---
|
||||
|
||||
## Product behavior
|
||||
|
||||
1. A task that is scheduled in the past and not complete still uses its normal
|
||||
card styling.
|
||||
2. While in this past/incomplete state, the card shows a right-side `Push`
|
||||
button next to reward and difficulty icons.
|
||||
3. The push button has visual priority over the card time text and sits on a
|
||||
higher layer if layout overlaps.
|
||||
4. Hover quick-action buttons do not show while the full `Push` button is shown.
|
||||
5. Marking the task complete removes the push button and returns the card to
|
||||
normal completed behavior.
|
||||
6. Clicking `Push` opens a menu with:
|
||||
- `Push to next`,
|
||||
- `Push to tomorrow`,
|
||||
- `Push to backlog`.
|
||||
7. Push to next moves the task to the next available no-overlap slot after the
|
||||
current owner-local time. If a valid slot exists later today, use that.
|
||||
8. Push to tomorrow moves the task to the first valid no-overlap slot on the next
|
||||
owner-local day.
|
||||
9. Push to backlog removes schedule placement and marks the backlog freshness
|
||||
timer as fresh regardless of the task's original age.
|
||||
10. All push results persist and survive close/reopen.
|
||||
|
||||
---
|
||||
|
||||
## Scope assumption to verify in Block 01
|
||||
|
||||
V1 core push commands are currently flexible-task oriented. This plan should
|
||||
apply the push button to planned flexible tasks first. Required, locked,
|
||||
surprise, and free-slot cards must not receive unsupported push commands unless
|
||||
Block 01 deliberately expands backend support with tests.
|
||||
|
||||
---
|
||||
|
||||
## Definition of done
|
||||
|
||||
Task Pushing 01 is complete when all of the following are true:
|
||||
|
||||
1. The read/presentation model can identify a past, incomplete, planned flexible
|
||||
task.
|
||||
2. Past incomplete planned flexible cards show a full `Push` button beside the
|
||||
reward/difficulty icons.
|
||||
3. The full push button suppresses hover quick actions.
|
||||
4. Completed cards never show the full push button.
|
||||
5. Clicking the push button opens a menu with exactly the three requested
|
||||
destinations.
|
||||
6. Push to next uses backend scheduling and never places the task before current
|
||||
owner-local time.
|
||||
7. Push to next uses a same-day slot when one is available later today.
|
||||
8. Push to tomorrow uses the next owner-local day and places into the first valid
|
||||
no-overlap slot.
|
||||
9. Push to backlog clears scheduled start/end and resets backlog freshness to
|
||||
fresh without rewriting original task creation truth.
|
||||
10. Push commands are persisted through SQLite and survive close/reopen.
|
||||
11. UI widgets do not contain scheduling, Drift, SQL, or repository logic.
|
||||
12. Validation gates pass, or unavailable commands are documented.
|
||||
|
||||
---
|
||||
|
||||
## Non-goals
|
||||
|
||||
1. No drag-and-drop push behavior.
|
||||
2. No schedule review modal unless existing backend result UX already requires
|
||||
it.
|
||||
3. No week/month views.
|
||||
4. No Shield/Recovery flow.
|
||||
5. No recurring task rollover.
|
||||
6. No calendar sync.
|
||||
7. No redesign of reward/difficulty icons beyond layout needed for the Push
|
||||
button.
|
||||
8. No frontend-only task movement.
|
||||
|
||||
---
|
||||
|
||||
## Architecture rule
|
||||
|
||||
The push flow must be command-driven:
|
||||
|
||||
```text
|
||||
Past-task presentation flag
|
||||
-> card Push button
|
||||
-> push destination menu
|
||||
-> SchedulerCommandController method
|
||||
-> V1ApplicationCommandUseCases
|
||||
-> scheduling core / ApplicationUnitOfWork
|
||||
-> SQLite persistence
|
||||
-> selected-date read refresh
|
||||
```
|
||||
|
||||
The card may decide whether to show a button from presentation flags. It must not
|
||||
calculate canonical new slots or mutate task times.
|
||||
30
README.md
|
|
@ -130,6 +130,36 @@ The provisional UI app lives outside the root Dart workspace at
|
|||
`apps/focus_flow_flutter` so backend package gates can stay Dart-only while the
|
||||
Flutter foundation settles.
|
||||
|
||||
From the repository root, start the desktop UI with the default local SQLite
|
||||
database:
|
||||
|
||||
```sh
|
||||
scripts/bootstrap_dev.sh
|
||||
cd apps/focus_flow_flutter
|
||||
flutter pub get
|
||||
flutter run -d linux
|
||||
```
|
||||
|
||||
Replace `linux` with `macos` or `windows` on those hosts. Use `flutter devices`
|
||||
to list available device IDs. If Flutter reports that the desktop project is
|
||||
not configured for that platform, generate the missing runner from
|
||||
`apps/focus_flow_flutter`:
|
||||
|
||||
```sh
|
||||
flutter create --platforms=linux .
|
||||
```
|
||||
|
||||
Use `--platforms=macos` or `--platforms=windows` for those hosts.
|
||||
|
||||
For a disposable dev database, pass an explicit SQLite path:
|
||||
|
||||
```sh
|
||||
flutter run -d linux --dart-define=SCHEDULER_SQLITE_PATH=/tmp/focus_flow_dev.sqlite
|
||||
```
|
||||
|
||||
The default database path is `~/ADHD_Scheduler/scheduler.sqlite`. To reset local
|
||||
UI data, close the app and delete the SQLite file you used.
|
||||
|
||||
```sh
|
||||
cd apps/focus_flow_flutter
|
||||
flutter analyze
|
||||
|
|
|
|||
|
|
@ -18,12 +18,6 @@ migration:
|
|||
- platform: linux
|
||||
create_revision: e1fd963c6f6922bd32afde2e9698a363cd0406d2
|
||||
base_revision: e1fd963c6f6922bd32afde2e9698a363cd0406d2
|
||||
- platform: macos
|
||||
create_revision: e1fd963c6f6922bd32afde2e9698a363cd0406d2
|
||||
base_revision: e1fd963c6f6922bd32afde2e9698a363cd0406d2
|
||||
- platform: windows
|
||||
create_revision: e1fd963c6f6922bd32afde2e9698a363cd0406d2
|
||||
base_revision: e1fd963c6f6922bd32afde2e9698a363cd0406d2
|
||||
|
||||
# User provided section
|
||||
|
||||
|
|
|
|||
|
|
@ -81,8 +81,36 @@ only a dev reset; backup/restore UI remains out of scope.
|
|||
|
||||
## Commands
|
||||
|
||||
Run `scripts/bootstrap_dev.sh` once from the repository root to create the
|
||||
default SQLite directory.
|
||||
|
||||
Run the desktop UI from this directory:
|
||||
|
||||
```sh
|
||||
flutter pub get
|
||||
flutter run -d linux
|
||||
```
|
||||
|
||||
Replace `linux` with `macos` or `windows` on those hosts. The default database
|
||||
path is `~/ADHD_Scheduler/scheduler.sqlite`. If Flutter reports that the
|
||||
desktop project is not configured for that platform, generate the missing
|
||||
runner from this directory:
|
||||
|
||||
```sh
|
||||
flutter create --platforms=linux .
|
||||
```
|
||||
|
||||
Use `--platforms=macos` or `--platforms=windows` for those hosts.
|
||||
|
||||
Use a disposable dev database when needed:
|
||||
|
||||
```sh
|
||||
flutter run -d linux --dart-define=SCHEDULER_SQLITE_PATH=/tmp/focus_flow_dev.sqlite
|
||||
```
|
||||
|
||||
Check the app from this directory:
|
||||
|
||||
```sh
|
||||
flutter analyze
|
||||
flutter test
|
||||
```
|
||||
|
|
|
|||
|
|
@ -52,6 +52,11 @@ class _FocusFlowHomeState extends State<FocusFlowHome> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Adds a generic filler task from the timeline context menu.
|
||||
Future<void> _addGenericFlexibleTask() async {
|
||||
await commandController.addGenericFlexibleTask();
|
||||
}
|
||||
|
||||
/// Builds the widget subtree for this component.
|
||||
/// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
|
||||
@override
|
||||
|
|
@ -77,6 +82,7 @@ class _FocusFlowHomeState extends State<FocusFlowHome> {
|
|||
selectedCard: controller.selectedCard,
|
||||
onSelectCard: controller.selectCard,
|
||||
onCompleteCard: _toggleCardCompletion,
|
||||
onAddTask: _addGenericFlexibleTask,
|
||||
onClearSelection: controller.clearSelection,
|
||||
),
|
||||
TodayScreenReady(:final data) => _TodayScreenScaffold(
|
||||
|
|
@ -84,6 +90,7 @@ class _FocusFlowHomeState extends State<FocusFlowHome> {
|
|||
selectedCard: controller.selectedCard,
|
||||
onSelectCard: controller.selectCard,
|
||||
onCompleteCard: _toggleCardCompletion,
|
||||
onAddTask: _addGenericFlexibleTask,
|
||||
onClearSelection: controller.clearSelection,
|
||||
),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class _TodayScreenScaffold extends StatefulWidget {
|
|||
required this.selectedCard,
|
||||
required this.onSelectCard,
|
||||
required this.onCompleteCard,
|
||||
required this.onAddTask,
|
||||
required this.onClearSelection,
|
||||
});
|
||||
|
||||
|
|
@ -32,6 +33,10 @@ class _TodayScreenScaffold extends StatefulWidget {
|
|||
/// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior.
|
||||
final Future<void> Function(TimelineCardModel card) onCompleteCard;
|
||||
|
||||
/// Stores the `onAddTask` value for this object.
|
||||
/// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior.
|
||||
final Future<void> Function() onAddTask;
|
||||
|
||||
/// Stores the `onClearSelection` value for this object.
|
||||
/// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior.
|
||||
final VoidCallback onClearSelection;
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ class _TodayScreenScaffoldState extends State<_TodayScreenScaffold> {
|
|||
scrollTargetCardId: _timelineScrollTargetCardId,
|
||||
scrollRequest: _timelineScrollRequest,
|
||||
onCardSelected: widget.onSelectCard,
|
||||
onAddTask: widget.onAddTask,
|
||||
onCardCompleted: widget.onCompleteCard,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -33,6 +33,12 @@ class SchedulerCommandController extends ChangeNotifier {
|
|||
/// Clock used by direct UI commands such as marking a task complete.
|
||||
final DateTime Function() now;
|
||||
|
||||
/// Filler title used by the temporary timeline add-task action.
|
||||
static const _genericFlexibleTaskTitle = 'New flexible task';
|
||||
|
||||
/// Filler duration used by the temporary timeline add-task action.
|
||||
static const _genericFlexibleTaskDurationMinutes = 30;
|
||||
|
||||
/// Private state stored as `_sequence` for this implementation.
|
||||
/// The field is intentionally scoped to this library so only the owning type can change the related scheduler or UI behavior.
|
||||
var _sequence = 0;
|
||||
|
|
@ -60,6 +66,24 @@ class SchedulerCommandController extends ChangeNotifier {
|
|||
);
|
||||
}
|
||||
|
||||
/// Adds a generic flexible task to the next available Today slot.
|
||||
Future<void> addGenericFlexibleTask() async {
|
||||
await _run(
|
||||
label: 'Adding task',
|
||||
successMessage: 'Task added',
|
||||
refreshAfterSuccess: true,
|
||||
action: (operationId) {
|
||||
return commands.quickCaptureToNextAvailableSlot(
|
||||
context: contextFor(operationId),
|
||||
localDate: localDate,
|
||||
title: _genericFlexibleTaskTitle,
|
||||
durationMinutes: _genericFlexibleTaskDurationMinutes,
|
||||
projectId: quickCaptureProjectId,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Schedules a backlog task into the next available slot.
|
||||
Future<void> scheduleBacklogItem({
|
||||
required String taskId,
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ class Sidebar extends StatelessWidget {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const _WindowControls(),
|
||||
const SizedBox(height: 34),
|
||||
const _BrandRow(),
|
||||
const SizedBox(height: 34),
|
||||
_NavItem(
|
||||
|
|
@ -51,51 +49,6 @@ class Sidebar extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
/// Private implementation type for `_WindowControls` in this library.
|
||||
/// It keeps supporting state and behavior scoped to the owning file so the public API stays focused on the scheduler concepts that callers use directly.
|
||||
class _WindowControls extends StatelessWidget {
|
||||
/// Creates a `_WindowControls` instance with the values required by its invariants.
|
||||
/// Callers should pass already-normalized scheduler data because validation and defaulting belong at the model or service boundary, not in arbitrary UI code.
|
||||
const _WindowControls();
|
||||
|
||||
/// Builds the widget subtree for this component.
|
||||
/// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Row(
|
||||
children: [
|
||||
_WindowDot(color: Color(0xFFFF5F57)),
|
||||
SizedBox(width: 12),
|
||||
_WindowDot(color: Color(0xFFFFBD2E)),
|
||||
SizedBox(width: 12),
|
||||
_WindowDot(color: Color(0xFF28C840)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Private implementation type for `_WindowDot` in this library.
|
||||
/// It keeps supporting state and behavior scoped to the owning file so the public API stays focused on the scheduler concepts that callers use directly.
|
||||
class _WindowDot extends StatelessWidget {
|
||||
/// Creates a `_WindowDot` instance with the values required by its invariants.
|
||||
/// Callers should pass already-normalized scheduler data because validation and defaulting belong at the model or service boundary, not in arbitrary UI code.
|
||||
const _WindowDot({required this.color});
|
||||
|
||||
/// Stores the `color` value for this object.
|
||||
/// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior.
|
||||
final Color color;
|
||||
|
||||
/// Builds the widget subtree for this component.
|
||||
/// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
|
||||
child: const SizedBox(width: 16, height: 16),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Private implementation type for `_BrandRow` in this library.
|
||||
/// It keeps supporting state and behavior scoped to the owning file so the public API stays focused on the scheduler concepts that callers use directly.
|
||||
class _BrandRow extends StatelessWidget {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ class TimelineView extends StatefulWidget {
|
|||
required this.cards,
|
||||
required this.range,
|
||||
required this.onCardSelected,
|
||||
this.onAddTask,
|
||||
this.onCardCompleted,
|
||||
this.now,
|
||||
this.scrollTargetCardId,
|
||||
|
|
@ -40,6 +41,9 @@ class TimelineView extends StatefulWidget {
|
|||
/// Callback invoked when a card is selected.
|
||||
final ValueChanged<TimelineCardModel> onCardSelected;
|
||||
|
||||
/// Callback invoked when the timeline context menu requests a new task.
|
||||
final Future<void> Function()? onAddTask;
|
||||
|
||||
/// Callback invoked when a task card status control requests completion.
|
||||
final ValueChanged<TimelineCardModel>? onCardCompleted;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,12 @@
|
|||
|
||||
part of '../timeline_view.dart';
|
||||
|
||||
/// Actions exposed by the timeline context menu.
|
||||
enum _TimelineMenuAction {
|
||||
/// Adds a generic flexible task to the timeline.
|
||||
addTask,
|
||||
}
|
||||
|
||||
/// Private implementation type for `_TimelineViewState` in this library.
|
||||
/// It keeps supporting state and behavior scoped to the owning file so the public API stays focused on the scheduler concepts that callers use directly.
|
||||
class _TimelineViewState extends State<TimelineView> {
|
||||
|
|
@ -83,6 +89,11 @@ class _TimelineViewState extends State<TimelineView> {
|
|||
_scheduleTargetScroll(_geometry);
|
||||
return ScrollConfiguration(
|
||||
behavior: const _TimelineScrollBehavior(),
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onSecondaryTapDown: widget.onAddTask == null
|
||||
? null
|
||||
: _showTimelineMenu,
|
||||
child: SingleChildScrollView(
|
||||
controller: _scrollController,
|
||||
child: SizedBox(
|
||||
|
|
@ -141,11 +152,38 @@ class _TimelineViewState extends State<TimelineView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Shows the timeline context menu and dispatches the selected action.
|
||||
Future<void> _showTimelineMenu(TapDownDetails details) async {
|
||||
final overlay = Overlay.of(context).context.findRenderObject();
|
||||
if (overlay is! RenderBox) {
|
||||
return;
|
||||
}
|
||||
final position = RelativeRect.fromRect(
|
||||
details.globalPosition & Size.zero,
|
||||
Offset.zero & overlay.size,
|
||||
);
|
||||
final action = await showMenu<_TimelineMenuAction>(
|
||||
context: context,
|
||||
position: position,
|
||||
items: const [
|
||||
PopupMenuItem<_TimelineMenuAction>(
|
||||
value: _TimelineMenuAction.addTask,
|
||||
child: Text('Add new task'),
|
||||
),
|
||||
],
|
||||
);
|
||||
if (!mounted || action != _TimelineMenuAction.addTask) {
|
||||
return;
|
||||
}
|
||||
await widget.onAddTask?.call();
|
||||
}
|
||||
|
||||
/// Runs the `_updateLayoutCache` helper used inside this library.
|
||||
/// The helper centralizes a small piece of transformation or validation so higher-level scheduler flow remains easier to read.
|
||||
void _updateLayoutCache() {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@
|
|||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin");
|
||||
sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
sqlite3_flutter_libs
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
|
|
|||
7
apps/focus_flow_flutter/macos/.gitignore
vendored
|
|
@ -1,7 +0,0 @@
|
|||
# Flutter-related
|
||||
**/Flutter/ephemeral/
|
||||
**/Pods/
|
||||
|
||||
# Xcode-related
|
||||
**/dgph
|
||||
**/xcuserdata/
|
||||
|
|
@ -1 +0,0 @@
|
|||
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||
|
|
@ -1 +0,0 @@
|
|||
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
}
|
||||
|
|
@ -1,729 +0,0 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXAggregateTarget section */
|
||||
33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
|
||||
isa = PBXAggregateTarget;
|
||||
buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
|
||||
buildPhases = (
|
||||
33CC111E2044C6BF0003C045 /* ShellScript */,
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "Flutter Assemble";
|
||||
productName = FLX;
|
||||
};
|
||||
/* End PBXAggregateTarget section */
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
|
||||
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
|
||||
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
|
||||
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
|
||||
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
|
||||
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
|
||||
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 33CC10EC2044A3C60003C045;
|
||||
remoteInfo = Runner;
|
||||
};
|
||||
33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 33CC111A2044C6BA0003C045;
|
||||
remoteInfo = FLX;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
33CC110E2044A8840003C045 /* Bundle Framework */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
);
|
||||
name = "Bundle Framework";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
||||
33CC10ED2044A3C60003C045 /* focus_flow_flutter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "focus_flow_flutter.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
||||
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
|
||||
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
|
||||
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
|
||||
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
|
||||
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
|
||||
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
|
||||
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
|
||||
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
|
||||
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
331C80D2294CF70F00263BE5 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
33CC10EA2044A3C60003C045 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
331C80D6294CF71000263BE5 /* RunnerTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
331C80D7294CF71000263BE5 /* RunnerTests.swift */,
|
||||
);
|
||||
path = RunnerTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33BA886A226E78AF003329D5 /* Configs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33E5194F232828860026EE4D /* AppInfo.xcconfig */,
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
|
||||
);
|
||||
path = Configs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33CC10E42044A3C60003C045 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33FAB671232836740065AC1E /* Runner */,
|
||||
33CEB47122A05771004F2AC0 /* Flutter */,
|
||||
331C80D6294CF71000263BE5 /* RunnerTests */,
|
||||
33CC10EE2044A3C60003C045 /* Products */,
|
||||
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33CC10EE2044A3C60003C045 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33CC10ED2044A3C60003C045 /* focus_flow_flutter.app */,
|
||||
331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33CC11242044D66E0003C045 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33CC10F22044A3C60003C045 /* Assets.xcassets */,
|
||||
33CC10F42044A3C60003C045 /* MainMenu.xib */,
|
||||
33CC10F72044A3C60003C045 /* Info.plist */,
|
||||
);
|
||||
name = Resources;
|
||||
path = ..;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33CEB47122A05771004F2AC0 /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */,
|
||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
|
||||
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
|
||||
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
|
||||
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
|
||||
);
|
||||
path = Flutter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
33FAB671232836740065AC1E /* Runner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */,
|
||||
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
|
||||
33E51913231747F40026EE4D /* DebugProfile.entitlements */,
|
||||
33E51914231749380026EE4D /* Release.entitlements */,
|
||||
33CC11242044D66E0003C045 /* Resources */,
|
||||
33BA886A226E78AF003329D5 /* Configs */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
331C80D4294CF70F00263BE5 /* RunnerTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||
buildPhases = (
|
||||
331C80D1294CF70F00263BE5 /* Sources */,
|
||||
331C80D2294CF70F00263BE5 /* Frameworks */,
|
||||
331C80D3294CF70F00263BE5 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
331C80DA294CF71000263BE5 /* PBXTargetDependency */,
|
||||
);
|
||||
name = RunnerTests;
|
||||
productName = RunnerTests;
|
||||
productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
33CC10EC2044A3C60003C045 /* Runner */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
33CC10E92044A3C60003C045 /* Sources */,
|
||||
33CC10EA2044A3C60003C045 /* Frameworks */,
|
||||
33CC10EB2044A3C60003C045 /* Resources */,
|
||||
33CC110E2044A8840003C045 /* Bundle Framework */,
|
||||
3399D490228B24CF009A79C7 /* ShellScript */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
33CC11202044C79F0003C045 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Runner;
|
||||
packageProductDependencies = (
|
||||
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
|
||||
);
|
||||
productName = Runner;
|
||||
productReference = 33CC10ED2044A3C60003C045 /* focus_flow_flutter.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
33CC10E52044A3C60003C045 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastSwiftUpdateCheck = 0920;
|
||||
LastUpgradeCheck = 1510;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
331C80D4294CF70F00263BE5 = {
|
||||
CreatedOnToolsVersion = 14.0;
|
||||
TestTargetID = 33CC10EC2044A3C60003C045;
|
||||
};
|
||||
33CC10EC2044A3C60003C045 = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
LastSwiftMigration = 1100;
|
||||
ProvisioningStyle = Automatic;
|
||||
SystemCapabilities = {
|
||||
com.apple.Sandbox = {
|
||||
enabled = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
33CC111A2044C6BA0003C045 = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 33CC10E42044A3C60003C045;
|
||||
packageReferences = (
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
|
||||
);
|
||||
productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
33CC10EC2044A3C60003C045 /* Runner */,
|
||||
331C80D4294CF70F00263BE5 /* RunnerTests */,
|
||||
33CC111A2044C6BA0003C045 /* Flutter Assemble */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
331C80D3294CF70F00263BE5 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
33CC10EB2044A3C60003C045 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
|
||||
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
|
||||
};
|
||||
33CC111E2044C6BF0003C045 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
Flutter/ephemeral/FlutterInputs.xcfilelist,
|
||||
);
|
||||
inputPaths = (
|
||||
Flutter/ephemeral/tripwire,
|
||||
);
|
||||
outputFileListPaths = (
|
||||
Flutter/ephemeral/FlutterOutputs.xcfilelist,
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
331C80D1294CF70F00263BE5 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
33CC10E92044A3C60003C045 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
|
||||
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
|
||||
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 33CC10EC2044A3C60003C045 /* Runner */;
|
||||
targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
|
||||
};
|
||||
33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
|
||||
targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
33CC10F52044A3C60003C045 /* Base */,
|
||||
);
|
||||
name = MainMenu.xib;
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
331C80DB294CF71000263BE5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.focusFlowFlutter.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/focus_flow_flutter.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/focus_flow_flutter";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
331C80DC294CF71000263BE5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.focusFlowFlutter.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/focus_flow_flutter.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/focus_flow_flutter";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
331C80DD294CF71000263BE5 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.focusFlowFlutter.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/focus_flow_flutter.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/focus_flow_flutter";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
338D0CE9231458BD00FA5F75 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
338D0CEA231458BD00FA5F75 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
338D0CEB231458BD00FA5F75 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
33CC10F92044A3C60003C045 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
33CC10FA2044A3C60003C045 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
33CC10FC2044A3C60003C045 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
33CC10FD2044A3C60003C045 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
33CC111C2044C6BA0003C045 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
33CC111D2044C6BA0003C045 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
331C80DB294CF71000263BE5 /* Debug */,
|
||||
331C80DC294CF71000263BE5 /* Release */,
|
||||
331C80DD294CF71000263BE5 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
33CC10F92044A3C60003C045 /* Debug */,
|
||||
33CC10FA2044A3C60003C045 /* Release */,
|
||||
338D0CE9231458BD00FA5F75 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
33CC10FC2044A3C60003C045 /* Debug */,
|
||||
33CC10FD2044A3C60003C045 /* Release */,
|
||||
338D0CEA231458BD00FA5F75 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
33CC111C2044C6BA0003C045 /* Debug */,
|
||||
33CC111D2044C6BA0003C045 /* Release */,
|
||||
338D0CEB231458BD00FA5F75 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCLocalSwiftPackageReference section */
|
||||
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
|
||||
isa = XCLocalSwiftPackageReference;
|
||||
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
|
||||
};
|
||||
/* End XCLocalSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = FlutterGeneratedPluginSwiftPackage;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = 33CC10E52044A3C60003C045 /* Project object */;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<PreActions>
|
||||
<ExecutionAction
|
||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||
<ActionContent
|
||||
title = "Run Prepare Flutter Framework Script"
|
||||
scriptText = ""$FLUTTER_ROOT"/packages/flutter_tools/bin/macos_assemble.sh prepare ">
|
||||
<EnvironmentBuildable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "focus_flow_flutter.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</EnvironmentBuildable>
|
||||
</ActionContent>
|
||||
</ExecutionAction>
|
||||
</PreActions>
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "focus_flow_flutter.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "focus_flow_flutter.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "331C80D4294CF70F00263BE5"
|
||||
BuildableName = "RunnerTests.xctest"
|
||||
BlueprintName = "RunnerTests"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
enableGPUValidationMode = "1"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "focus_flow_flutter.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Profile"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "focus_flow_flutter.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
import Cocoa
|
||||
import FlutterMacOS
|
||||
|
||||
@main
|
||||
class AppDelegate: FlutterAppDelegate {
|
||||
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "16x16",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_16.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "16x16",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_32.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "32x32",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_32.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "32x32",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_64.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "128x128",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_128.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "128x128",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_256.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "256x256",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_256.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "256x256",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_512.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "512x512",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_512.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "512x512",
|
||||
"idiom" : "mac",
|
||||
"filename" : "app_icon_1024.png",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 520 B |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
|
@ -1,343 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Runner" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="applicationMenu" destination="uQy-DD-JDr" id="XBo-yE-nKs"/>
|
||||
<outlet property="mainFlutterWindow" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||
<items>
|
||||
<menuItem title="APP_NAME" id="1Xt-HY-uBw">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="APP_NAME" systemMenu="apple" id="uQy-DD-JDr">
|
||||
<items>
|
||||
<menuItem title="About APP_NAME" id="5kV-Vb-QxS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
|
||||
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
|
||||
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
|
||||
<menuItem title="Services" id="NMo-om-nkz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
|
||||
<menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
|
||||
<connections>
|
||||
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Show All" id="Kd2-mp-pUS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
|
||||
<menuItem title="Quit APP_NAME" keyEquivalent="q" id="4sb-4s-VLi">
|
||||
<connections>
|
||||
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Edit" id="5QF-Oa-p0T">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
|
||||
<items>
|
||||
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
|
||||
<connections>
|
||||
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
|
||||
<connections>
|
||||
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
|
||||
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
|
||||
<connections>
|
||||
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
|
||||
<connections>
|
||||
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
|
||||
<connections>
|
||||
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Delete" id="pa3-QI-u2k">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
|
||||
<connections>
|
||||
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
|
||||
<menuItem title="Find" id="4EN-yA-p0u">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Find" id="1b7-l0-nxx">
|
||||
<items>
|
||||
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
|
||||
<connections>
|
||||
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
|
||||
<items>
|
||||
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
|
||||
<connections>
|
||||
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
|
||||
<connections>
|
||||
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
|
||||
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Substitutions" id="9ic-FL-obx">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
|
||||
<items>
|
||||
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
|
||||
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Links" id="cwL-P1-jid">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Data Detectors" id="tRr-pd-1PS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Transformations" id="2oI-Rn-ZJC">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
|
||||
<items>
|
||||
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Speech" id="xrE-MZ-jX0">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
|
||||
<items>
|
||||
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="View" id="H8h-7b-M4v">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="View" id="HyV-fh-RgO">
|
||||
<items>
|
||||
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Window" id="aUF-d1-5bR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
|
||||
<items>
|
||||
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
|
||||
<connections>
|
||||
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Zoom" id="R4o-n2-Eq4">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
|
||||
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Help" id="EPT-qC-fAb">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Help" systemMenu="help" id="rJ0-wn-3NY"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
<point key="canvasLocation" x="142" y="-258"/>
|
||||
</menu>
|
||||
<window title="APP_NAME" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MainFlutterWindow" customModule="Runner" customModuleProvider="target">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<rect key="contentRect" x="335" y="390" width="800" height="600"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</view>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
// Application-level settings for the Runner target.
|
||||
//
|
||||
// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
|
||||
// future. If not, the values below would default to using the project name when this becomes a
|
||||
// 'flutter create' template.
|
||||
|
||||
// The application's name. By default this is also the title of the Flutter window.
|
||||
PRODUCT_NAME = focus_flow_flutter
|
||||
|
||||
// The application's bundle identifier
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.focusFlowFlutter
|
||||
|
||||
// The copyright displayed in application information
|
||||
PRODUCT_COPYRIGHT = Copyright © 2026 com.example. All rights reserved.
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
#include "../../Flutter/Flutter-Debug.xcconfig"
|
||||
#include "Warnings.xcconfig"
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
#include "../../Flutter/Flutter-Release.xcconfig"
|
||||
#include "Warnings.xcconfig"
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES
|
||||
CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
|
||||
CLANG_WARN_PRAGMA_PACK = YES
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES
|
||||
CLANG_WARN_COMMA = YES
|
||||
GCC_WARN_STRICT_SELECTOR_MATCH = YES
|
||||
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
|
||||
GCC_WARN_SHADOW = YES
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>$(PRODUCT_COPYRIGHT)</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
import Cocoa
|
||||
import FlutterMacOS
|
||||
|
||||
class MainFlutterWindow: NSWindow {
|
||||
override func awakeFromNib() {
|
||||
let flutterViewController = FlutterViewController()
|
||||
let windowFrame = self.frame
|
||||
self.contentViewController = flutterViewController
|
||||
self.setFrame(windowFrame, display: true)
|
||||
|
||||
RegisterGeneratedPlugins(registry: flutterViewController)
|
||||
|
||||
super.awakeFromNib()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
import Cocoa
|
||||
import FlutterMacOS
|
||||
import XCTest
|
||||
|
||||
class RunnerTests: XCTestCase {
|
||||
|
||||
func testExample() {
|
||||
// If you add code to the Runner application, consider adding tests here.
|
||||
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -276,6 +276,32 @@ void main() {
|
|||
expect(scrollable.position.pixels, greaterThan(beforeDrag + 80));
|
||||
});
|
||||
|
||||
testWidgets('timeline context menu adds a generic flexible task', (
|
||||
tester,
|
||||
) async {
|
||||
final composition = _composition();
|
||||
await _pumpPlanApp(tester, composition: composition);
|
||||
|
||||
await tester.tapAt(
|
||||
tester.getCenter(find.byType(TimelineView)),
|
||||
buttons: kSecondaryMouseButton,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Add new task'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Add new task'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final addedTask = composition.store.currentTasks.singleWhere(
|
||||
(task) => task.title == 'New flexible task',
|
||||
);
|
||||
expect(addedTask.type, TaskType.flexible);
|
||||
expect(addedTask.status, TaskStatus.planned);
|
||||
expect(addedTask.durationMinutes, 30);
|
||||
expect(find.text('New flexible task'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('timeline splits partial overlaps and reuses empty lanes', (
|
||||
tester,
|
||||
) async {
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 958 KiB After Width: | Height: | Size: 958 KiB |
|
Before Width: | Height: | Size: 1,011 KiB After Width: | Height: | Size: 1,011 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |