From b37bab713407ca958a782cf9c5ff08b0c346b28a Mon Sep 17 00:00:00 2001 From: Zavian Wang <36817799+Zavianx@users.noreply.github.com> Date: Sun, 31 May 2026 00:19:34 +0800 Subject: [PATCH] feat(vault): expose approved markdown writes (#2936) Co-authored-by: Steven Enamakel --- .../intelligence/VaultPanel.test.tsx | 31 +++ .../components/intelligence/VaultPanel.tsx | 30 +++ app/src/lib/i18n/ar.ts | 10 + app/src/lib/i18n/bn.ts | 9 + app/src/lib/i18n/de.ts | 10 + app/src/lib/i18n/en.ts | 9 + app/src/lib/i18n/es.ts | 12 ++ app/src/lib/i18n/fr.ts | 12 ++ app/src/lib/i18n/hi.ts | 9 + app/src/lib/i18n/id.ts | 10 + app/src/lib/i18n/it.ts | 12 ++ app/src/lib/i18n/ko.ts | 9 + app/src/lib/i18n/pl.ts | 10 + app/src/lib/i18n/pt.ts | 10 + app/src/lib/i18n/ru.ts | 11 + app/src/lib/i18n/zh-CN.ts | 9 + app/src/utils/tauriCommands/vault.test.ts | 40 ++++ app/src/utils/tauriCommands/vault.ts | 28 +++ docs/TEST-COVERAGE-MATRIX.md | 1 + gitbooks/developing/architecture/frontend.md | 10 + src/openhuman/about_app/catalog_data.rs | 10 + src/openhuman/about_app/catalog_tests.rs | 1 + src/openhuman/tools/impl/filesystem/mod.rs | 2 + .../impl/filesystem/vault_write_markdown.rs | 177 ++++++++++++++++ src/openhuman/tools/ops.rs | 4 + src/openhuman/tools/ops_tests.rs | 38 ++++ src/openhuman/tools/user_filter.rs | 5 +- src/openhuman/vault/mod.rs | 1 + src/openhuman/vault/ops.rs | 168 +++++++++++++++- src/openhuman/vault/schemas.rs | 60 ++++++ src/openhuman/vault/store.rs | 69 ++++++- src/openhuman/vault/sync.rs | 3 + src/openhuman/vault/tests.rs | 190 +++++++++++++++++- src/openhuman/vault/types.rs | 23 +++ 34 files changed, 1024 insertions(+), 9 deletions(-) create mode 100644 src/openhuman/tools/impl/filesystem/vault_write_markdown.rs diff --git a/app/src/components/intelligence/VaultPanel.test.tsx b/app/src/components/intelligence/VaultPanel.test.tsx index e44fc4c97..96e63c2b2 100644 --- a/app/src/components/intelligence/VaultPanel.test.tsx +++ b/app/src/components/intelligence/VaultPanel.test.tsx @@ -41,6 +41,8 @@ function vault(overrides: Record = {}) { created_at: '2026-05-17T10:00:00Z', last_synced_at: null, file_count: 0, + write_state: 'writable', + write_state_reason: 'writable', ...overrides, }; } @@ -111,6 +113,35 @@ describe('', () => { expect(screen.getByText('A')).toBeTruthy(); expect(screen.getByText(/42 file/)).toBeTruthy(); expect(screen.getByText(/synced 30s ago/)).toBeTruthy(); + expect(screen.getByTestId('vault-write-state-v-A')).toHaveTextContent('Writable'); + expect(screen.getByText(/Approved markdown/)).toBeTruthy(); + }); + + it('shows read-only and unavailable vault write states with reasons', async () => { + mockList.mockResolvedValueOnce({ + result: [ + vault({ + id: 'v-readonly', + name: 'Read only', + write_state: 'read_only', + write_state_reason: 'read_only', + }), + vault({ + id: 'v-missing', + name: 'Missing', + write_state: 'unavailable', + write_state_reason: 'unavailable', + }), + ], + logs: [], + }); + render(); + await waitFor(() => screen.getByTestId('vault-list')); + + expect(screen.getByTestId('vault-write-state-v-readonly')).toHaveTextContent('Read-only'); + expect(screen.getByText('Vault folder is read-only on this device.')).toBeTruthy(); + expect(screen.getByTestId('vault-write-state-v-missing')).toHaveTextContent('Unavailable'); + expect(screen.getByText('Vault folder is not available on this device.')).toBeTruthy(); }); it('toggles the add form and creates a vault on submit', async () => { diff --git a/app/src/components/intelligence/VaultPanel.tsx b/app/src/components/intelligence/VaultPanel.tsx index 9311a732e..e85b8d064 100644 --- a/app/src/components/intelligence/VaultPanel.tsx +++ b/app/src/components/intelligence/VaultPanel.tsx @@ -359,6 +359,12 @@ export function VaultPanel({ onToast }: VaultPanelProps) {
    {vaults.map(v => { const state = busy[v.id]; + const writeStateReason = v.write_state_reason + ? t( + `vault.writeState.reasons.${v.write_state_reason}`, + t('vault.writeState.unknownReason') + ) + : t('vault.writeState.unknownReason'); return (
  • @@ -379,6 +385,17 @@ export function VaultPanel({ onToast }: VaultPanelProps) { ) : t('vault.neverSynced')}
    +
    + + {t(`vault.writeState.${v.write_state}`)} + + + {writeStateReason} + +