mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-11 10:01:30 -10:00
fix(tui): pin marketplace sources and keep transcript drags out of the composer
Install sources in the bundled first-party marketplace snapshot pointed at the moving `refs/heads/main` archive while the catalog displayed a reviewed revision, so the bytes a user installed were not the bytes the snapshot described and `/plugin update` re-downloaded a whole-repository tarball that changed with every unrelated commit. Pin each source to the snapshot's revision; freshness comes from bumping the pin, which the marketplace-sync workflow already reports weekly, and `update` now finds no change until the pin moves. The store test and acceptance fixture contracts are unchanged. A transcript selection or scrollbar drag released over the composer was consumed by the composer handler: the caret moved, a composer selection started and the transcript drag flag never cleared. Let the surface that started the drag finish it. Document local-time and DST evaluation, missed-run coalescing, overlap protection and approval behavior for automations. Validation: tui::mouse_ui tests and plugins::marketplace tests passed (0 failed); the regenerated snapshot is byte-identical to the sync script's output for the pinned revision. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
d1545dfcfa
commit
080cae8492
5 files changed
+77
-7
No files matched your search
@@ -8,28 +8,28 @@
|
||||
"plugins": [
|
||||
{
|
||||
"name": "computer-use",
|
||||
"source": "https://codeload.github.com/Hmbown/codewhale-plugin-marketplace/tar.gz/refs/heads/main#path=plugins/computer-use",
|
||||
"source": "https://codeload.github.com/Hmbown/codewhale-plugin-marketplace/tar.gz/0b7f680c7e74762e0bf369717f3d44cd137603d4#path=plugins/computer-use",
|
||||
"version": "0.2.1",
|
||||
"description": "See the screen and operate it: accessibility-first control, screenshots and zoom on macOS, Windows and Linux, plus macOS recording and HarmonyOS devices over hdc.",
|
||||
"homepage": "https://github.com/Hmbown/codewhale-plugin-marketplace"
|
||||
},
|
||||
{
|
||||
"name": "whalewiki",
|
||||
"source": "https://codeload.github.com/Hmbown/codewhale-plugin-marketplace/tar.gz/refs/heads/main#path=plugins/whalewiki",
|
||||
"source": "https://codeload.github.com/Hmbown/codewhale-plugin-marketplace/tar.gz/0b7f680c7e74762e0bf369717f3d44cd137603d4#path=plugins/whalewiki",
|
||||
"version": "0.1.0",
|
||||
"description": "An evidence-bound wiki for the repo: generated pages sealed against the source files they were written from, computed staleness, a zero-install CI drift gate, and a read-only MCP surface.",
|
||||
"homepage": "https://github.com/Hmbown/codewhale-plugin-marketplace"
|
||||
},
|
||||
{
|
||||
"name": "cloudflare-docs",
|
||||
"source": "https://codeload.github.com/Hmbown/codewhale-plugin-marketplace/tar.gz/refs/heads/main#path=plugins/cloudflare-docs",
|
||||
"source": "https://codeload.github.com/Hmbown/codewhale-plugin-marketplace/tar.gz/0b7f680c7e74762e0bf369717f3d44cd137603d4#path=plugins/cloudflare-docs",
|
||||
"version": "0.1.0",
|
||||
"description": "Cloudflare's official remote docs MCP: current Workers/Pages/storage/platform documentation search. No credentials required.",
|
||||
"homepage": "https://github.com/Hmbown/codewhale-plugin-marketplace"
|
||||
},
|
||||
{
|
||||
"name": "codewhale-skills",
|
||||
"source": "https://codeload.github.com/Hmbown/codewhale-plugin-marketplace/tar.gz/refs/heads/main#path=skills",
|
||||
"source": "https://codeload.github.com/Hmbown/codewhale-plugin-marketplace/tar.gz/0b7f680c7e74762e0bf369717f3d44cd137603d4#path=skills",
|
||||
"version": "1.0.0",
|
||||
"description": "The Codewhale skill set as one reviewed plugin: implement, debug, test, review, verify, webapp-testing, release, handoff, research and the rest of the bundled workflow skills. Each arrives namespaced as codewhale-skills:<name>. These skills also ship inside the Codewhale binary; install this bundle to carry a reviewed copy to a host that only loads plugins.",
|
||||
"homepage": "https://github.com/Hmbown/codewhale-plugin-marketplace"
|
||||
|
||||
@@ -409,6 +409,17 @@ pub(crate) fn handle_composer_mouse(app: &mut App, mouse: MouseEvent) -> bool {
|
||||
if !app.view_stack.is_empty() {
|
||||
return false;
|
||||
}
|
||||
// A transcript selection or scrollbar drag that ends over the composer
|
||||
// belongs to the surface that started it: the transcript handler must
|
||||
// still see the release to clear its drag state and publish the text.
|
||||
if matches!(
|
||||
mouse.kind,
|
||||
MouseEventKind::Drag(MouseButton::Left) | MouseEventKind::Up(MouseButton::Left)
|
||||
) && (app.viewport.transcript_selection.dragging
|
||||
|| app.viewport.transcript_scrollbar_dragging)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Use outer area for hit-testing (includes border).
|
||||
let Some(area) = app.viewport.last_composer_area else {
|
||||
return false;
|
||||
|
||||
@@ -163,3 +163,49 @@ fn middle_click_respects_overlays_missing_composer_and_remote_clipboard() {
|
||||
assert_eq!(app.status_message, status, "{mode}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transcript_drag_released_over_composer_keeps_caret_and_clears_drag() {
|
||||
let (mut app, text) = composer();
|
||||
app.input = "draft".into();
|
||||
app.cursor_position = 5;
|
||||
app.history = vec![HistoryCell::User {
|
||||
content: "SELECTED_LINE".into(),
|
||||
}];
|
||||
app.resync_history_revisions();
|
||||
app.viewport.transcript_cache.ensure(
|
||||
&app.history,
|
||||
&app.history_revisions,
|
||||
80,
|
||||
app.transcript_render_options(),
|
||||
);
|
||||
app.viewport.transcript_selection.anchor = Some(TranscriptSelectionPoint {
|
||||
line_index: 0,
|
||||
column: 0,
|
||||
});
|
||||
app.viewport.transcript_selection.head = Some(TranscriptSelectionPoint {
|
||||
line_index: 0,
|
||||
column: 80,
|
||||
});
|
||||
app.viewport.transcript_selection.dragging = true;
|
||||
let expected = selection_to_text(&app).unwrap();
|
||||
// Dragging across the composer must not move its caret or start a
|
||||
// composer selection.
|
||||
handle_mouse_event(
|
||||
&mut app,
|
||||
event(MouseEventKind::Drag(MouseButton::Left), text.x + 2, text.y),
|
||||
);
|
||||
assert_eq!(app.cursor_position, 5);
|
||||
assert!(app.selection_anchor.is_none());
|
||||
assert!(app.viewport.transcript_selection.dragging);
|
||||
// Releasing over the composer finishes the transcript drag.
|
||||
handle_mouse_event(
|
||||
&mut app,
|
||||
event(MouseEventKind::Up(MouseButton::Left), text.x + 2, text.y),
|
||||
);
|
||||
assert!(!app.viewport.transcript_selection.dragging);
|
||||
assert_eq!(app.cursor_position, 5);
|
||||
assert_eq!(app.input, "draft");
|
||||
assert_eq!(app.clipboard.read_primary_text(), Some(expected));
|
||||
assert_eq!(app.clipboard.last_written_text(), Some("REGULAR_CLIPBOARD"));
|
||||
}
|
||||
@@ -2661,6 +2661,16 @@ has no scheduled next run. **Cancel** (`Esc`) discards the draft. Editing keeps
|
||||
existing permission settings, custom schedules, and additional workspace
|
||||
entries unless the corresponding supported field is explicitly changed.
|
||||
|
||||
Schedules are evaluated in the machine's local time zone against the wall
|
||||
clock. A wall time that does not exist on a spring-forward day is skipped and
|
||||
an ambiguous fall-back time fires once. Occurrences missed while Codewhale was
|
||||
closed, asleep, or still running the previous occurrence are coalesced: the
|
||||
next start runs one catch-up occurrence and then continues from the next
|
||||
future slot, never replaying every missed slot. An occurrence never starts
|
||||
while an earlier run of the same automation is still queued or running. Each
|
||||
run is recorded durably with its status, timing and error; a run that needs a
|
||||
tool approval has no operator to ask and fails once the approval wait expires.
|
||||
|
||||
Choosing a concrete model pins both the model and its exact configured
|
||||
provider, including named custom routes. Later changes to the active provider
|
||||
do not move that automation's pin. The default-model choice and legacy
|
||||
|
||||
@@ -26,9 +26,12 @@ for candidate in catalog["plugins"]:
|
||||
relative = spec[5:]
|
||||
if any(part in ("", ".", "..") or not all(c.isascii() and (c.isalnum() or c in "-_.") for c in part) for part in relative.split("/")):
|
||||
raise SystemExit(f"unsafe bundle path: {relative}")
|
||||
# Mutable update channel; installed content is hash-bound and re-reviewed
|
||||
# when changed. The catalog snapshot itself records its exact source SHA.
|
||||
candidate["source"] = f"https://codeload.github.com/Hmbown/codewhale-plugin-marketplace/tar.gz/refs/heads/main#path={relative}"
|
||||
# Pin every install source to the reviewed marketplace revision so the
|
||||
# bytes a user installs are the bytes this snapshot describes. Freshness
|
||||
# comes from bumping the pin (the marketplace-sync workflow reports drift
|
||||
# against `main` weekly); `/plugin update` re-downloads the same archive
|
||||
# and reports no change until the pin moves.
|
||||
candidate["source"] = f"https://codeload.github.com/Hmbown/codewhale-plugin-marketplace/tar.gz/{revision}#path={relative}"
|
||||
snapshot = {"repository": REPOSITORY, "revision": revision, "catalog": catalog}
|
||||
rendered = json.dumps(snapshot, indent=2, ensure_ascii=False) + "\n"
|
||||
output = ROOT / "crates/tui/assets/first-party-marketplace.json"
|
||||
|
||||
Reference in new issue
Block a user