docs: define first alpha product UX
This commit is contained in:
parent
acabe340e9
commit
a6d349f188
|
|
@ -0,0 +1,242 @@
|
||||||
|
# First Alpha Product UX Design
|
||||||
|
|
||||||
|
## Status and scope
|
||||||
|
|
||||||
|
This is the approved product-UX tranche for the first public alpha. It covers
|
||||||
|
the Desktop application, official Activity, Journal and Browser Inbox plugins,
|
||||||
|
and the browser extension. Release packaging, public repository documentation,
|
||||||
|
licensing, and sync-server hardening are specified separately in the
|
||||||
|
`alpha-release` tranche.
|
||||||
|
|
||||||
|
The user-visible Russian term is **Дело**. Existing platform and storage names
|
||||||
|
such as `workspaceRootPath` remain internal compatibility details in this
|
||||||
|
tranche.
|
||||||
|
|
||||||
|
This document supersedes conflicting decisions in the following older narrow
|
||||||
|
designs:
|
||||||
|
|
||||||
|
- `2026-06-29-activity-worklog-suggestions-design.md` for candidate lifecycle
|
||||||
|
and background availability;
|
||||||
|
- `2026-06-29-browser-inbox-domain-binding-design.md` for the relation between
|
||||||
|
bindings, global Inbox, and browser activity;
|
||||||
|
- `2026-07-11-platform-localization-design.md` only where it would leave
|
||||||
|
developer-facing plugin IDs visible in the normal user interface.
|
||||||
|
|
||||||
|
## Product decisions
|
||||||
|
|
||||||
|
- A new Дело is created **only** by an explicit user action. Browser Inbox,
|
||||||
|
Activity, and Journal never create a case, candidate case, or implicit case.
|
||||||
|
- A manual extension send is a Browser Inbox capture. Passive browsing activity
|
||||||
|
is never an Inbox capture.
|
||||||
|
- Passive browser activity records only a normalized hostname and measured
|
||||||
|
duration. It never records a page URL, title, selection, page content,
|
||||||
|
keystrokes, or browsing history.
|
||||||
|
- Passive browser activity counts only the active tab in a focused browser
|
||||||
|
window. Background tabs and unfocused browser windows contribute no time.
|
||||||
|
- Activity can suggest a Journal record, but the user reviews and saves it.
|
||||||
|
Journal entries are never created automatically.
|
||||||
|
- An unassigned activity can be saved only after the user picks an existing
|
||||||
|
Дело. Picking it for a single Journal entry does not silently create a domain
|
||||||
|
binding.
|
||||||
|
- The first alpha retains one optional Дело assignment per Browser Inbox
|
||||||
|
capture, matching current product semantics. It does not introduce sharing a
|
||||||
|
capture between several cases.
|
||||||
|
|
||||||
|
## Browser extension: manual capture and passive activity
|
||||||
|
|
||||||
|
### Manual capture
|
||||||
|
|
||||||
|
The existing explicit popup/context-menu actions remain the only way to create
|
||||||
|
`browser.capture.*` events and Browser Inbox records. Their protocol and
|
||||||
|
retry queue remain separate from passive activity data.
|
||||||
|
|
||||||
|
### Domain activity tracker
|
||||||
|
|
||||||
|
The extension adds a small persisted tracker:
|
||||||
|
|
||||||
|
1. When an HTTP(S) page becomes the active tab of a focused browser window,
|
||||||
|
start timing its normalized lowercase hostname.
|
||||||
|
2. On tab activation, hostname change, window focus loss, or a five-minute
|
||||||
|
alarm, calculate elapsed time and add it to that hostname's local total.
|
||||||
|
3. Ignore browser-internal pages, invalid URLs, and excluded hostnames. An
|
||||||
|
exclusion `youtube.com` matches that hostname and every subdomain; the same
|
||||||
|
rule applies to `x.com`.
|
||||||
|
4. On a flush, send each accumulated hostname as a `browser.activity.domain`
|
||||||
|
record. The payload contains a schema version, idempotency ID, observed
|
||||||
|
period bounds, hostname, and `durationSeconds`; it contains no URL-like
|
||||||
|
field.
|
||||||
|
5. Remove a hostname's sent total only after Desktop confirms that particular
|
||||||
|
idempotency ID. On failure, shutdown, or service-worker suspension, preserve
|
||||||
|
the timestamp and totals in extension local storage and retry later.
|
||||||
|
|
||||||
|
The extension uses timestamp arithmetic rather than an in-memory interval, so
|
||||||
|
the behaviour survives Chromium Manifest V3 service-worker restarts. Its
|
||||||
|
manifest gains only the `windows` and `alarms` permissions needed for this
|
||||||
|
flow; existing tab access is retained. Firefox uses the equivalent WebExtension
|
||||||
|
events.
|
||||||
|
|
||||||
|
The settings page gets an **Excluded domains** list. It accepts one hostname per
|
||||||
|
item, normalizes case and leading dots, rejects a scheme/path/query, and
|
||||||
|
explains that a hostname excludes its subdomains. The default list is empty.
|
||||||
|
|
||||||
|
### Desktop receiver
|
||||||
|
|
||||||
|
Desktop exposes an authenticated activity receiver separate from the capture
|
||||||
|
receiver. It validates a bounded hostname, positive bounded duration, ISO time
|
||||||
|
fields, schema version, and idempotency ID before it publishes
|
||||||
|
`browser.activity.domain`. Invalid and duplicate records do not enter Activity.
|
||||||
|
The existing local pairing token gates the endpoint; the token is never placed
|
||||||
|
in Activity storage or UI.
|
||||||
|
|
||||||
|
The receiver annotates a valid activity with an existing exact hostname-to-Дело
|
||||||
|
binding when one exists. Bindings remain explicit: `client.example.com` does
|
||||||
|
not imply `example.com` or the reverse. This is deliberately different from
|
||||||
|
the exclusion-list suffix rule. Unbound activity is stored as global activity.
|
||||||
|
|
||||||
|
## Activity and Journal
|
||||||
|
|
||||||
|
### Background processing
|
||||||
|
|
||||||
|
Official plugins gain a lifecycle-safe background-service contribution. It is
|
||||||
|
loaded when the plugin host starts, not only while the plugin view is mounted.
|
||||||
|
The Activity service subscribes once to public events, normalizes and persists
|
||||||
|
them, rebuilds candidates, and releases subscriptions on host teardown.
|
||||||
|
|
||||||
|
The platform provides unsubscribe-capable event subscriptions. Activity writes
|
||||||
|
use the existing atomic plugin-settings update primitive, preventing concurrent
|
||||||
|
browser/file events from overwriting each other. Command registration is also
|
||||||
|
owned by the background service so commands remain available without opening
|
||||||
|
the Activity view.
|
||||||
|
|
||||||
|
### Candidate rules
|
||||||
|
|
||||||
|
Activity presents chronological sessions rather than a raw log by default. A
|
||||||
|
browser activity session displays, for example, `admin.client-site.ru · 1 ч
|
||||||
|
32 мин`; it exposes no hidden URL/title data.
|
||||||
|
|
||||||
|
A candidate is scoped to one existing Дело and local calendar day. It is ready
|
||||||
|
when either:
|
||||||
|
|
||||||
|
- meaningful events in its session cover at least ten minutes and include at
|
||||||
|
least two events; or
|
||||||
|
- it contains one or more browser-domain records totaling at least ten minutes.
|
||||||
|
|
||||||
|
`workspace.selected`, `file.opened`, and `note.opened` are diagnostic context,
|
||||||
|
not meaningful work by themselves. Candidate identity is stable by case, local
|
||||||
|
day, and source event IDs. Accepted and dismissed state is persisted, so an
|
||||||
|
already handled candidate does not reappear after a restart.
|
||||||
|
|
||||||
|
The Journal review action opens the existing journal editor with candidate
|
||||||
|
duration, date, and a concise domain/event summary. The user may edit all
|
||||||
|
content and must explicitly save. For global/unassigned activity, the review
|
||||||
|
requires choosing one existing Дело first. Missing or disabled Journal support
|
||||||
|
produces a visible, actionable message rather than failing silently.
|
||||||
|
|
||||||
|
Activity has two clear views:
|
||||||
|
|
||||||
|
- **Сессии** — default, with candidate cards and activity summaries;
|
||||||
|
- **События** — a secondary diagnostic stream for technical event inspection.
|
||||||
|
|
||||||
|
## Browser Inbox lifecycle
|
||||||
|
|
||||||
|
### Record state
|
||||||
|
|
||||||
|
A capture remains one canonical record with independent fields:
|
||||||
|
|
||||||
|
```text
|
||||||
|
globalState: active | archived
|
||||||
|
workspaceRootPath: optional existing Дело
|
||||||
|
```
|
||||||
|
|
||||||
|
Existing records migrate to `globalState: active`; their current assignment is
|
||||||
|
retained. The current test vault may be discarded, but the migration is
|
||||||
|
non-destructive for an alpha user vault.
|
||||||
|
|
||||||
|
### Actions
|
||||||
|
|
||||||
|
- **Assign to Дело** retains the capture in global Inbox and shows it in the
|
||||||
|
selected Дело's Inbox.
|
||||||
|
- **Убрать из общих входящих** changes `globalState` to `archived`. It never
|
||||||
|
removes the assignment, so an assigned capture remains available from its
|
||||||
|
Дело.
|
||||||
|
- **Открепить от дела** removes only `workspaceRootPath`. If the capture is
|
||||||
|
still globally active it returns to the global Inbox; otherwise it remains in
|
||||||
|
archive.
|
||||||
|
- **Удалить везде** removes the canonical capture and its assignment. It is a
|
||||||
|
separate destructive action and requires confirmation that names the affected
|
||||||
|
Дело when assigned.
|
||||||
|
- **Сохранить ссылку в деле** creates an independent `.url` file under the
|
||||||
|
case's `Links/` directory, creating that directory first. Success does not
|
||||||
|
depend on retaining the capture record; later Inbox deletion cannot remove
|
||||||
|
the written file.
|
||||||
|
|
||||||
|
Bulk actions operate only on the visibly filtered capture set, show the count,
|
||||||
|
and use archive rather than permanent deletion. Every permanent deletion and
|
||||||
|
Journal deletion has a cancellation-safe confirmation dialog.
|
||||||
|
|
||||||
|
When a Дело is renamed, the Inbox service migrates its capture assignments and
|
||||||
|
exact domain bindings from the old root to the new root. It does not migrate a
|
||||||
|
binding that has been manually changed to another Дело during that operation.
|
||||||
|
|
||||||
|
## Alpha interface
|
||||||
|
|
||||||
|
- Use Russian product labels consistently: **Дела**, **Входящие**,
|
||||||
|
**Активности**, and **Журнал**. User-facing dates use the local time zone.
|
||||||
|
- The overview contains only useful, properly scoped items: continuation for
|
||||||
|
the selected Дело, new Inbox records, Journal candidates, and real recent
|
||||||
|
changes. It excludes repeated `file.changed`, `workspace.selected`, and
|
||||||
|
unrelated-case events.
|
||||||
|
- Empty states give a next action, not an empty pane.
|
||||||
|
- Clear, delete, and archive actions name their scope and consequences.
|
||||||
|
|
||||||
|
Plugin IDs are diagnostic information. Normal tabs display the manifest's
|
||||||
|
human title, such as `Заметки` or `Изображение`, never
|
||||||
|
`verstak.default-editor.notes-markdown` or `verstak.file-preview.image`.
|
||||||
|
|
||||||
|
Add a persisted **Settings → Debug → Show plugin IDs** preference, defaulting
|
||||||
|
to false. The Desktop application-specific `--debug` command-line argument
|
||||||
|
enables the same display only for that run. When either is active, show the ID
|
||||||
|
adjacent to the human title and in diagnostic errors. Neither mode alters data,
|
||||||
|
plugin permissions, or release behaviour.
|
||||||
|
|
||||||
|
## Error handling and privacy
|
||||||
|
|
||||||
|
- A failed extension delivery keeps the accumulated hostname time locally and
|
||||||
|
reports a non-blocking retry state in extension settings; it never falls back
|
||||||
|
to Browser Inbox capture.
|
||||||
|
- Receiver authentication/validation errors provide a safe status to the
|
||||||
|
extension without echoing the pairing token or untrusted payload.
|
||||||
|
- Activity never manufactures a case from missing assignment data.
|
||||||
|
- User-visible errors explain the failed action and offer the next safe action.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
Automated checks must cover:
|
||||||
|
|
||||||
|
- browser tracker start/stop, focused-tab-only accounting, exclusions,
|
||||||
|
coalescing, acknowledgement-only reset, retry persistence, and payload
|
||||||
|
privacy;
|
||||||
|
- Desktop activity receiver authentication, validation, idempotency, binding,
|
||||||
|
and event publication;
|
||||||
|
- atomic Activity persistence, background subscription lifecycle, candidate
|
||||||
|
threshold/state, Journal handoff, local dates, and missing-Journal feedback;
|
||||||
|
- assigning, archiving, unlinking, permanent deletion, `.url` creation,
|
||||||
|
filtered bulk operations, and renaming an assigned Дело;
|
||||||
|
- normal and debug-mode plugin tab labels;
|
||||||
|
- the corrected frontend Wails mock, plus the end-to-end flows
|
||||||
|
activity-to-Journal and Inbox-to-Дело.
|
||||||
|
|
||||||
|
Manual GUI smoke testing verifies Russian normal-mode labels, hidden plugin
|
||||||
|
IDs, candidate review, Inbox preservation, and extension domain exclusions in
|
||||||
|
the Linux Desktop build.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
- automatic creation of a Дело;
|
||||||
|
- background-tab or browser-history tracking;
|
||||||
|
- URL/content/title collection for passive activity;
|
||||||
|
- shared/multi-case captures;
|
||||||
|
- automated Journal saving, billing, or time-sheet generation;
|
||||||
|
- browser-extension localization and a general analytics product;
|
||||||
|
- release packaging, licensing, public GitHub README, and sync-server release
|
||||||
|
security, which belong to the next alpha-release design.
|
||||||
Loading…
Reference in New Issue