fix: update test assertion for new key escaping format
The MappingKey::escape_project now replaces / with :: so 'issue:g/p:42' becomes 'issue:g::p:42'. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@ import reactHooks from "eslint-plugin-react-hooks";
|
|||||||
import tseslint from "typescript-eslint";
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
export default tseslint.config(
|
export default tseslint.config(
|
||||||
{ ignores: ["dist", "src-tauri"] },
|
{ ignores: ["dist", "src-tauri", "src/lib/bindings.ts"] },
|
||||||
{
|
{
|
||||||
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||||
files: ["**/*.{ts,tsx}"],
|
files: ["**/*.{ts,tsx}"],
|
||||||
|
|||||||
@@ -873,7 +873,7 @@ mod tests {
|
|||||||
let result = bridge.incremental_sync(&mut map).unwrap();
|
let result = bridge.incremental_sync(&mut map).unwrap();
|
||||||
|
|
||||||
assert_eq!(result.created, 1);
|
assert_eq!(result.created, 1);
|
||||||
assert!(map.mappings.contains_key("issue:g/p:42"));
|
assert!(map.mappings.contains_key("issue:g::p:42"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- bv triage command tests --
|
// -- bv triage command tests --
|
||||||
|
|||||||
@@ -137,6 +137,23 @@ export function CommandPalette({
|
|||||||
setHighlightedIndex(-1);
|
setHighlightedIndex(-1);
|
||||||
}, [selectableOptions]);
|
}, [selectableOptions]);
|
||||||
|
|
||||||
|
const handleOptionSelect = useCallback(
|
||||||
|
(option: (typeof selectableOptions)[0]) => {
|
||||||
|
if (option.type === "command") {
|
||||||
|
// Parse the command and emit filter
|
||||||
|
if (option.id.startsWith("cmd:type:")) {
|
||||||
|
onFilter({ type: option.value as FocusItemType });
|
||||||
|
} else if (option.id.startsWith("cmd:stale:")) {
|
||||||
|
onFilter({ minAge: parseInt(option.value, 10) });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onSelect(option.value);
|
||||||
|
}
|
||||||
|
onClose();
|
||||||
|
},
|
||||||
|
[onFilter, onSelect, onClose]
|
||||||
|
);
|
||||||
|
|
||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
(e: React.KeyboardEvent) => {
|
(e: React.KeyboardEvent) => {
|
||||||
if (e.key === "ArrowDown") {
|
if (e.key === "ArrowDown") {
|
||||||
@@ -158,24 +175,7 @@ export function CommandPalette({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[selectableOptions, highlightedIndex]
|
[selectableOptions, highlightedIndex, handleOptionSelect]
|
||||||
);
|
|
||||||
|
|
||||||
const handleOptionSelect = useCallback(
|
|
||||||
(option: (typeof selectableOptions)[0]) => {
|
|
||||||
if (option.type === "command") {
|
|
||||||
// Parse the command and emit filter
|
|
||||||
if (option.id.startsWith("cmd:type:")) {
|
|
||||||
onFilter({ type: option.value as FocusItemType });
|
|
||||||
} else if (option.id.startsWith("cmd:stale:")) {
|
|
||||||
onFilter({ minAge: parseInt(option.value, 10) });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
onSelect(option.value);
|
|
||||||
}
|
|
||||||
onClose();
|
|
||||||
},
|
|
||||||
[onFilter, onSelect, onClose]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleBackdropClick = useCallback(
|
const handleBackdropClick = useCallback(
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|||||||
import { motion, AnimatePresence } from "framer-motion";
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
import { useCaptureStore } from "@/stores/capture-store";
|
import { useCaptureStore } from "@/stores/capture-store";
|
||||||
import { quickCapture } from "@/lib/tauri";
|
import { quickCapture } from "@/lib/tauri";
|
||||||
import { isMcError } from "@/lib/types";
|
|
||||||
|
|
||||||
export function QuickCapture(): React.ReactElement | null {
|
export function QuickCapture(): React.ReactElement | null {
|
||||||
const isOpen = useCaptureStore((s) => s.isOpen);
|
const isOpen = useCaptureStore((s) => s.isOpen);
|
||||||
@@ -56,9 +55,15 @@ export function QuickCapture(): React.ReactElement | null {
|
|||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
try {
|
try {
|
||||||
const result = await quickCapture(trimmed);
|
const result = await quickCapture(trimmed);
|
||||||
captureSuccess(result.bead_id);
|
if (result.status === "error") {
|
||||||
|
captureError(result.error.message);
|
||||||
|
} else {
|
||||||
|
captureSuccess(result.data.bead_id);
|
||||||
|
}
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const message = isMcError(err) ? err.message : "Capture failed";
|
// With the Result pattern, McError comes through result.error (handled above).
|
||||||
|
// This catch only fires for Tauri-level failures (e.g., IPC unavailable).
|
||||||
|
const message = err instanceof Error ? err.message : "Capture failed";
|
||||||
captureError(message);
|
captureError(message);
|
||||||
}
|
}
|
||||||
}, [value, setSubmitting, captureSuccess, captureError]);
|
}, [value, setSubmitting, captureSuccess, captureError]);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { useCallback } from "react";
|
|||||||
import { open } from "@tauri-apps/plugin-shell";
|
import { open } from "@tauri-apps/plugin-shell";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { useFocusStore } from "@/stores/focus-store";
|
import { useFocusStore } from "@/stores/focus-store";
|
||||||
import type { DeferDuration, FocusAction } from "@/lib/types";
|
import type { DeferDuration } from "@/lib/types";
|
||||||
|
|
||||||
/** Minimal item shape needed for actions */
|
/** Minimal item shape needed for actions */
|
||||||
export interface ActionItem {
|
export interface ActionItem {
|
||||||
@@ -128,7 +128,7 @@ export function useActions(): UseActionsReturn {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Convert duration to FocusAction format and advance queue
|
// Convert duration to FocusAction format and advance queue
|
||||||
const actionName: FocusAction = `defer_${duration}` as FocusAction;
|
const actionName = `defer_${duration}` as const;
|
||||||
act(actionName, reason ?? undefined);
|
act(actionName, reason ?? undefined);
|
||||||
},
|
},
|
||||||
[act]
|
[act]
|
||||||
|
|||||||
261
src/lib/bindings.ts
Normal file
261
src/lib/bindings.ts
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
|
||||||
|
// This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually.
|
||||||
|
|
||||||
|
/** user-defined commands **/
|
||||||
|
|
||||||
|
|
||||||
|
export const commands = {
|
||||||
|
/**
|
||||||
|
* Simple greeting command for testing IPC
|
||||||
|
*/
|
||||||
|
async greet(name: string) : Promise<string> {
|
||||||
|
return await TAURI_INVOKE("greet", { name });
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get the current status of lore integration by calling the real CLI.
|
||||||
|
*/
|
||||||
|
async getLoreStatus() : Promise<Result<LoreStatus, McError>> {
|
||||||
|
try {
|
||||||
|
return { status: "ok", data: await TAURI_INVOKE("get_lore_status") };
|
||||||
|
} catch (e) {
|
||||||
|
if(e instanceof Error) throw e;
|
||||||
|
else return { status: "error", error: e as any };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get the current status of the bridge (mapping counts, sync times).
|
||||||
|
*/
|
||||||
|
async getBridgeStatus() : Promise<Result<BridgeStatus, McError>> {
|
||||||
|
try {
|
||||||
|
return { status: "ok", data: await TAURI_INVOKE("get_bridge_status") };
|
||||||
|
} catch (e) {
|
||||||
|
if(e instanceof Error) throw e;
|
||||||
|
else return { status: "error", error: e as any };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Trigger an incremental sync (process since_last_check events).
|
||||||
|
*/
|
||||||
|
async syncNow() : Promise<Result<SyncResult, McError>> {
|
||||||
|
try {
|
||||||
|
return { status: "ok", data: await TAURI_INVOKE("sync_now") };
|
||||||
|
} catch (e) {
|
||||||
|
if(e instanceof Error) throw e;
|
||||||
|
else return { status: "error", error: e as any };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Trigger a full reconciliation pass.
|
||||||
|
*/
|
||||||
|
async reconcile() : Promise<Result<SyncResult, McError>> {
|
||||||
|
try {
|
||||||
|
return { status: "ok", data: await TAURI_INVOKE("reconcile") };
|
||||||
|
} catch (e) {
|
||||||
|
if(e instanceof Error) throw e;
|
||||||
|
else return { status: "error", error: e as any };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Quick-capture a thought as a new bead.
|
||||||
|
*/
|
||||||
|
async quickCapture(title: string) : Promise<Result<CaptureResult, McError>> {
|
||||||
|
try {
|
||||||
|
return { status: "ok", data: await TAURI_INVOKE("quick_capture", { title }) };
|
||||||
|
} catch (e) {
|
||||||
|
if(e instanceof Error) throw e;
|
||||||
|
else return { status: "error", error: e as any };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Read persisted frontend state from ~/.local/share/mc/state.json.
|
||||||
|
*
|
||||||
|
* Returns null if no state exists (first run).
|
||||||
|
*/
|
||||||
|
async readState() : Promise<Result<JsonValue | null, McError>> {
|
||||||
|
try {
|
||||||
|
return { status: "ok", data: await TAURI_INVOKE("read_state") };
|
||||||
|
} catch (e) {
|
||||||
|
if(e instanceof Error) throw e;
|
||||||
|
else return { status: "error", error: e as any };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Write frontend state to ~/.local/share/mc/state.json.
|
||||||
|
*
|
||||||
|
* Uses atomic rename pattern to prevent corruption.
|
||||||
|
*/
|
||||||
|
async writeState(state: JsonValue) : Promise<Result<null, McError>> {
|
||||||
|
try {
|
||||||
|
return { status: "ok", data: await TAURI_INVOKE("write_state", { state }) };
|
||||||
|
} catch (e) {
|
||||||
|
if(e instanceof Error) throw e;
|
||||||
|
else return { status: "error", error: e as any };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Clear persisted frontend state.
|
||||||
|
*/
|
||||||
|
async clearState() : Promise<Result<null, McError>> {
|
||||||
|
try {
|
||||||
|
return { status: "ok", data: await TAURI_INVOKE("clear_state") };
|
||||||
|
} catch (e) {
|
||||||
|
if(e instanceof Error) throw e;
|
||||||
|
else return { status: "error", error: e as any };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** user-defined events **/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** user-defined constants **/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** user-defined types **/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bridge status for the frontend
|
||||||
|
*/
|
||||||
|
export type BridgeStatus = {
|
||||||
|
/**
|
||||||
|
* Total mapped items
|
||||||
|
*/
|
||||||
|
mapping_count: number;
|
||||||
|
/**
|
||||||
|
* Items with pending bead creation
|
||||||
|
*/
|
||||||
|
pending_count: number;
|
||||||
|
/**
|
||||||
|
* Items flagged as suspect orphan (first strike)
|
||||||
|
*/
|
||||||
|
suspect_count: number;
|
||||||
|
/**
|
||||||
|
* Last incremental sync timestamp
|
||||||
|
*/
|
||||||
|
last_sync: string | null;
|
||||||
|
/**
|
||||||
|
* Last full reconciliation timestamp
|
||||||
|
*/
|
||||||
|
last_reconciliation: string | null }
|
||||||
|
/**
|
||||||
|
* Response from quick_capture: the bead ID created
|
||||||
|
*/
|
||||||
|
export type CaptureResult = { bead_id: string }
|
||||||
|
export type JsonValue = null | boolean | number | string | JsonValue[] | Partial<{ [key in string]: JsonValue }>
|
||||||
|
/**
|
||||||
|
* Lore sync status
|
||||||
|
*/
|
||||||
|
export type LoreStatus = { last_sync: string | null; is_healthy: boolean; message: string; summary: LoreSummaryStatus | null }
|
||||||
|
/**
|
||||||
|
* Summary counts from lore for the status response
|
||||||
|
*/
|
||||||
|
export type LoreSummaryStatus = { open_issues: number; authored_mrs: number; reviewing_mrs: number }
|
||||||
|
/**
|
||||||
|
* Structured error type for Tauri IPC commands.
|
||||||
|
*
|
||||||
|
* This replaces string-based errors (`Result<T, String>`) with typed errors
|
||||||
|
* that the frontend can handle programmatically.
|
||||||
|
*/
|
||||||
|
export type McError = {
|
||||||
|
/**
|
||||||
|
* Machine-readable error code (e.g., "LORE_UNAVAILABLE", "BRIDGE_LOCKED")
|
||||||
|
*/
|
||||||
|
code: McErrorCode;
|
||||||
|
/**
|
||||||
|
* Human-readable error message
|
||||||
|
*/
|
||||||
|
message: string;
|
||||||
|
/**
|
||||||
|
* Whether this error is recoverable (user can retry)
|
||||||
|
*/
|
||||||
|
recoverable: boolean }
|
||||||
|
/**
|
||||||
|
* Error codes for frontend handling
|
||||||
|
*/
|
||||||
|
export type McErrorCode = "LORE_UNAVAILABLE" | "LORE_UNHEALTHY" | "LORE_FETCH_FAILED" | "BRIDGE_LOCKED" | "BRIDGE_MAP_CORRUPTED" | "BRIDGE_SYNC_FAILED" | "BEADS_UNAVAILABLE" | "BEADS_CREATE_FAILED" | "BEADS_CLOSE_FAILED" | "BV_UNAVAILABLE" | "BV_TRIAGE_FAILED" | "IO_ERROR" | "INTERNAL_ERROR"
|
||||||
|
/**
|
||||||
|
* Result of a sync operation
|
||||||
|
*/
|
||||||
|
export type SyncResult = {
|
||||||
|
/**
|
||||||
|
* Number of new beads created
|
||||||
|
*/
|
||||||
|
created: number;
|
||||||
|
/**
|
||||||
|
* Number of existing items skipped (dedup)
|
||||||
|
*/
|
||||||
|
skipped: number;
|
||||||
|
/**
|
||||||
|
* Number of beads closed (two-strike)
|
||||||
|
*/
|
||||||
|
closed: number;
|
||||||
|
/**
|
||||||
|
* Number of suspect_orphan flags cleared (item reappeared)
|
||||||
|
*/
|
||||||
|
healed: number;
|
||||||
|
/**
|
||||||
|
* Errors encountered (non-fatal, processing continued)
|
||||||
|
*/
|
||||||
|
errors: string[] }
|
||||||
|
|
||||||
|
/** tauri-specta globals **/
|
||||||
|
|
||||||
|
import {
|
||||||
|
invoke as TAURI_INVOKE,
|
||||||
|
Channel as TAURI_CHANNEL,
|
||||||
|
} from "@tauri-apps/api/core";
|
||||||
|
import * as TAURI_API_EVENT from "@tauri-apps/api/event";
|
||||||
|
import { type WebviewWindow as __WebviewWindow__ } from "@tauri-apps/api/webviewWindow";
|
||||||
|
|
||||||
|
type __EventObj__<T> = {
|
||||||
|
listen: (
|
||||||
|
cb: TAURI_API_EVENT.EventCallback<T>,
|
||||||
|
) => ReturnType<typeof TAURI_API_EVENT.listen<T>>;
|
||||||
|
once: (
|
||||||
|
cb: TAURI_API_EVENT.EventCallback<T>,
|
||||||
|
) => ReturnType<typeof TAURI_API_EVENT.once<T>>;
|
||||||
|
emit: null extends T
|
||||||
|
? (payload?: T) => ReturnType<typeof TAURI_API_EVENT.emit>
|
||||||
|
: (payload: T) => ReturnType<typeof TAURI_API_EVENT.emit>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Result<T, E> =
|
||||||
|
| { status: "ok"; data: T }
|
||||||
|
| { status: "error"; error: E };
|
||||||
|
|
||||||
|
function __makeEvents__<T extends Record<string, any>>(
|
||||||
|
mappings: Record<keyof T, string>,
|
||||||
|
) {
|
||||||
|
return new Proxy(
|
||||||
|
{} as unknown as {
|
||||||
|
[K in keyof T]: __EventObj__<T[K]> & {
|
||||||
|
(handle: __WebviewWindow__): __EventObj__<T[K]>;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
get: (_, event) => {
|
||||||
|
const name = mappings[event as keyof T];
|
||||||
|
|
||||||
|
return new Proxy((() => {}) as any, {
|
||||||
|
apply: (_, __, [window]: [__WebviewWindow__]) => ({
|
||||||
|
listen: (arg: any) => window.listen(name, arg),
|
||||||
|
once: (arg: any) => window.once(name, arg),
|
||||||
|
emit: (arg: any) => window.emit(name, arg),
|
||||||
|
}),
|
||||||
|
get: (_, command: keyof __EventObj__<any>) => {
|
||||||
|
switch (command) {
|
||||||
|
case "listen":
|
||||||
|
return (arg: any) => TAURI_API_EVENT.listen(name, arg);
|
||||||
|
case "once":
|
||||||
|
return (arg: any) => TAURI_API_EVENT.once(name, arg);
|
||||||
|
case "emit":
|
||||||
|
return (arg: any) => TAURI_API_EVENT.emit(name, arg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,8 +5,9 @@
|
|||||||
* instead of browser localStorage. Falls back to localStorage in browser context.
|
* instead of browser localStorage. Falls back to localStorage in browser context.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { readState, writeState, clearState } from "./tauri";
|
||||||
import type { StateStorage } from "zustand/middleware";
|
import type { StateStorage } from "zustand/middleware";
|
||||||
|
import type { JsonValue } from "./bindings";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a storage adapter that persists to Tauri backend.
|
* Create a storage adapter that persists to Tauri backend.
|
||||||
@@ -17,11 +18,14 @@ export function createTauriStorage(): StateStorage {
|
|||||||
return {
|
return {
|
||||||
getItem: async (_name: string): Promise<string | null> => {
|
getItem: async (_name: string): Promise<string | null> => {
|
||||||
try {
|
try {
|
||||||
const state = await invoke<Record<string, unknown> | null>("read_state");
|
const result = await readState();
|
||||||
if (state === null) {
|
if (result.status === "error") {
|
||||||
|
throw new Error(result.error.message);
|
||||||
|
}
|
||||||
|
if (result.data === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return JSON.stringify(state);
|
return JSON.stringify(result.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("[tauri-storage] Failed to read state:", error);
|
console.warn("[tauri-storage] Failed to read state:", error);
|
||||||
return null;
|
return null;
|
||||||
@@ -30,8 +34,11 @@ export function createTauriStorage(): StateStorage {
|
|||||||
|
|
||||||
setItem: async (_name: string, value: string): Promise<void> => {
|
setItem: async (_name: string, value: string): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const state = JSON.parse(value) as Record<string, unknown>;
|
const state = JSON.parse(value) as JsonValue;
|
||||||
await invoke("write_state", { state });
|
const result = await writeState(state);
|
||||||
|
if (result.status === "error") {
|
||||||
|
throw new Error(result.error.message);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("[tauri-storage] Failed to write state:", error);
|
console.warn("[tauri-storage] Failed to write state:", error);
|
||||||
}
|
}
|
||||||
@@ -39,7 +46,10 @@ export function createTauriStorage(): StateStorage {
|
|||||||
|
|
||||||
removeItem: async (_name: string): Promise<void> => {
|
removeItem: async (_name: string): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await invoke("clear_state");
|
const result = await clearState();
|
||||||
|
if (result.status === "error") {
|
||||||
|
throw new Error(result.error.message);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("[tauri-storage] Failed to clear state:", error);
|
console.warn("[tauri-storage] Failed to clear state:", error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,24 @@
|
|||||||
/**
|
/**
|
||||||
* Tauri IPC wrapper.
|
* Tauri IPC wrapper.
|
||||||
*
|
*
|
||||||
* Thin layer over @tauri-apps/api invoke that provides typed
|
* Re-exports type-safe commands generated by tauri-specta.
|
||||||
* function signatures for each Rust command.
|
* The generated bindings wrap all fallible commands in Result<T, McError>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { commands } from "./bindings";
|
||||||
import type { BridgeStatus, CaptureResult, LoreStatus, SyncResult } from "./types";
|
|
||||||
|
|
||||||
export async function getLoreStatus(): Promise<LoreStatus> {
|
// Re-export all commands from generated bindings
|
||||||
return invoke<LoreStatus>("get_lore_status");
|
export const {
|
||||||
}
|
greet,
|
||||||
|
getLoreStatus,
|
||||||
|
getBridgeStatus,
|
||||||
|
syncNow,
|
||||||
|
reconcile,
|
||||||
|
quickCapture,
|
||||||
|
readState,
|
||||||
|
writeState,
|
||||||
|
clearState,
|
||||||
|
} = commands;
|
||||||
|
|
||||||
export async function getBridgeStatus(): Promise<BridgeStatus> {
|
// Re-export the Result type for consumers
|
||||||
return invoke<BridgeStatus>("get_bridge_status");
|
export type { Result } from "./bindings";
|
||||||
}
|
|
||||||
|
|
||||||
export async function syncNow(): Promise<SyncResult> {
|
|
||||||
return invoke<SyncResult>("sync_now");
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function reconcile(): Promise<SyncResult> {
|
|
||||||
return invoke<SyncResult>("reconcile");
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function quickCapture(title: string): Promise<CaptureResult> {
|
|
||||||
return invoke<CaptureResult>("quick_capture", { title });
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export function transformLoreData(data: LoreMeData): FocusItem[] {
|
|||||||
updatedAt: mr.updated_at_iso ?? null,
|
updatedAt: mr.updated_at_iso ?? null,
|
||||||
contextQuote: null,
|
contextQuote: null,
|
||||||
requestedBy: mr.author_username ?? null,
|
requestedBy: mr.author_username ?? null,
|
||||||
|
snoozedUntil: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +66,7 @@ export function transformLoreData(data: LoreMeData): FocusItem[] {
|
|||||||
updatedAt: issue.updated_at_iso ?? null,
|
updatedAt: issue.updated_at_iso ?? null,
|
||||||
contextQuote: null,
|
contextQuote: null,
|
||||||
requestedBy: null,
|
requestedBy: null,
|
||||||
|
snoozedUntil: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +82,7 @@ export function transformLoreData(data: LoreMeData): FocusItem[] {
|
|||||||
updatedAt: mr.updated_at_iso ?? null,
|
updatedAt: mr.updated_at_iso ?? null,
|
||||||
contextQuote: null,
|
contextQuote: null,
|
||||||
requestedBy: null,
|
requestedBy: null,
|
||||||
|
snoozedUntil: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ export const invoke = vi.fn(async (cmd: string, _args?: unknown) => {
|
|||||||
return { created: 0, closed: 0, skipped: 0, healed: 0, errors: [] };
|
return { created: 0, closed: 0, skipped: 0, healed: 0, errors: [] };
|
||||||
case "quick_capture":
|
case "quick_capture":
|
||||||
return { bead_id: "bd-mock-capture" };
|
return { bead_id: "bd-mock-capture" };
|
||||||
|
case "read_state":
|
||||||
|
return null;
|
||||||
|
case "write_state":
|
||||||
|
case "clear_state":
|
||||||
|
return null;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Mock not implemented for command: ${cmd}`);
|
throw new Error(`Mock not implemented for command: ${cmd}`);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user