From ac542f4d728e9bc7f024dfeba1f7681d9fc4a88d Mon Sep 17 00:00:00 2001 From: Josh Mabry Date: Sat, 12 Sep 2026 19:30:41 -0700 Subject: [PATCH] feat(view): multi-select items and copy as a Markdown list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Josh: "as a user I want to be able to multiselect items and copy them to clipboard as md list item category price". - A checkbox column with a select-all for the visible rows; selection survives filter and tab changes (kept by id, with the item records cached), so a list can span lots. - A selection bar with the count, "Copy as Markdown" and "Clear". ⌘C / Ctrl+C copies too when rows are selected and focus is not in a field or on a text selection. - One line per item: `- Name — Category — $price` (category omitted when blank; the price is the target, "—" when unpriced; whitespace in names collapsed). - Clipboard: navigator.clipboard first, execCommand("copy") fallback, and when the frame is denied both a dialog with the text pre-selected. - Tests: the node harness pins the exact list format; presence checks for the controls. Smoke-tested in the local harness with a stubbed clipboard (button and shortcut). Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_011UQ33xXfygwEkzbbJx3YwZ --- README.md | 2 +- protoagent.plugin.yaml | 2 +- pyproject.toml | 2 +- tests/test_view.py | 21 ++++++++++++++++- view.py | 51 ++++++++++++++++++++++++++++++++++++++---- 5 files changed, 70 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ad536e6..f20afc5 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ inventory_summary() | | | |---|---| | **Tools** | `inventory_summary` · `inventory_list` · `inventory_get` · `inventory_upsert_lot` · `inventory_upsert_item` · `inventory_delete_item` · `inventory_set_price` · `inventory_mark_sold` · `inventory_listing` · `inventory_import_csv` · `inventory_export_csv` · `inventory_reprice_plan` · `inventory_stale` | -| **View** | a rail panel: the item grid (double-click to edit name/category/condition/qty; status select; click a price for the Price dialog), Price / Sold / Listing / Edit / Delete per row, lots with their P&L, sales, an activity log, CSV import (file or paste, with the mapping report) and export | +| **View** | a rail panel: the item grid (double-click to edit name/category/condition/qty; status select; click a price for the Price dialog), Price / Sold / Listing / Edit / Delete per row, multi-select with **Copy as Markdown** (`- Name — Category — $price`, also ⌘C), lots with their P&L, sales, an activity log, CSV import (file or paste, with the mapping report) and export | | **API** | bearer-gated JSON under `/api/plugins/inventory` — `summary`, `lots`, `items`, `items/{id}/price`, `items/{id}/sold`, `items/{id}/listings`, `listings/{id}/end`, `sales`, `stale`, `audit`, `import`, `export` | | **Automations** | `weekly_review: true` arms a plugin-owned recurring turn (`weekly_review_cron`, default Monday 09:00 in `review_timezone`) that re-prices stale evidence from eBay sold comps through the same tools, reports stale listings with a recommendation (it never changes a listing itself), and posts the per-lot P&L. Cancelled when the plugin is disabled. | | **Events** | `inventory.item.changed`, `inventory.lot.changed`, `inventory.sale.recorded`, `inventory.imported` | diff --git a/protoagent.plugin.yaml b/protoagent.plugin.yaml index 1eef71d..d53bb26 100644 --- a/protoagent.plugin.yaml +++ b/protoagent.plugin.yaml @@ -1,6 +1,6 @@ id: inventory name: Inventory (resale source of truth) -version: 0.3.0 +version: 0.3.1 description: >- The resale inventory as a source of truth: lots (what you bought and for how much), items (what each lot split into, its condition and status), listings (where each item is diff --git a/pyproject.toml b/pyproject.toml index fb8fad0..f640574 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "inventory-plugin" -version = "0.3.0" +version = "0.3.1" description = "Resale inventory as a source of truth for protoAgent — lots, items, listings, sales, price evidence, audit" requires-python = ">=3.11" diff --git a/tests/test_view.py b/tests/test_view.py index fada9d1..afcb125 100644 --- a/tests/test_view.py +++ b/tests/test_view.py @@ -108,7 +108,8 @@ def test_the_pure_helpers_under_node(): ) probe = ( "\nconsole.log(JSON.stringify({ neg: fmt(-12.5), pos: fmt(1234.5), nul: fmt(null), zero: fmt(0), sneg: signed(-3), spos: signed(3), snul: signed(null)," - " esc: esc('&\\''), field: field('name', 'Name', '\">'), opt: field('lot_id', 'Lot', '', { type: 'select', options: [['\">', 'x