Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ All notable changes to GitHub Vault Relay will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Current repository evidence is 44 test files and 482 passing tests; `npm run verify` passed locally on 2026-10-08. Test totals in older release entries are historical snapshots from those releases, not current totals.
Current repository evidence is 44 test files and 483 passing tests; `npm run verify` passed locally on 2026-10-08. Test totals in older release entries are historical snapshots from those releases, not current totals.
Paths and implementation details in older entries are historical release snapshots; the current internal path is `${app.vault.configDir}/github-vault-relay/`.

---
Expand All @@ -15,12 +15,12 @@ Paths and implementation details in older entries are historical release snapsho
### UI Modernization & Architecture Consolidation
- **Unified Modal Architectures**: Consolidated duplicate Pull/Push modal pairs into parameter-driven `SyncConfirmModal` and `SyncResultModal`. Pruned 4 redundant legacy modal files (`pullConfirmModal.ts`, `pushConfirmModal.ts`, `pullResultModal.ts`, `pushResultModal.ts`), eliminating over 930 lines of boilerplate while maintaining 100% feature and telemetry parity.
- **Embedded Dashboard File Inspection**: Integrated interactive, expandable change inspection directly within `SyncDashboardModal`. Vault changes can now be filtered by category (*All, Local Only, Remote Only, Local Changed, Remote Changed, Conflicts, Deletes*) and inspected without opening separate stacking modal dialogs on mobile devices.
- **Mobile-First Stylesheet & Responsive Touch UX**: Eliminated all inline style string attributes across UI components in favor of clean semantic classes in `styles.css`. Modernized action buttons for small viewports (`@media (max-width: 480px)`) into full-width stacked layouts, preventing button line-breaks while guaranteeing minimum 44px tap targets and `safe-area-inset-bottom` padding.
- **Mobile-First Stylesheet & Responsive Touch UX**: Eliminated all inline style string attributes across UI components in favor of clean semantic classes in `styles.css`. Modernized action buttons with flex layouts, explicit gaps, and inline-flex vertical centering, preventing button overlap across both PC and mobile viewports. On small viewports (`@media (max-width: 480px)`), buttons adapt into full-width stacked layouts and two-column filter chips while guaranteeing minimum 44px tap targets and `safe-area-inset-bottom` padding.
- **SettingsTab Declarative Consolidation**: Replaced duplicate imperative DOM creation in `VaultRelaySettingTab.display()` with an iterator over `getSettingDefinitions()`, cutting ~250 lines of duplicate settings logic and strongly typing setting definitions with 0 ESLint warnings.

### Verification & Test Suite Hardening
- **Empirical Challenger & Stress Suites**: Added comprehensive adversarial stress suites (`tests/empiricalChallengerM1.test.ts` and `tests/m1ChallengerStress.test.ts`) covering 39 edge-case, reentrancy, and execution lifecycle scenarios.
- `npm run verify`: PASS — 44 test files, 482 tests passing, 0 lint warnings, clean typecheck, clean production build.
- `npm run verify`: PASS — 44 test files, 483 tests passing, 0 lint warnings, clean typecheck, clean production build.
- `verify-release.mjs`: PASS — All manifests, package, lockfile, and documentation files strictly synchronized.

## [1.1.0] - 2026-10-07
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
- Release: `1.2.0` (manifest, package, tag, and `main` agree after release).
- Minimum Obsidian version: `1.11.4`.
- Mobile support: enabled (`isDesktopOnly: false`).
- Automated evidence: 44 Vitest test files and 482 passing tests in the current tree.
- Automated evidence: 44 Vitest test files and 483 passing tests in the current tree.
- Latest local quality gate: `npm run verify` PASS on 2026-10-08 (lint, typecheck, test, build).
- The production bundle is generated by `npm run build`; `npm run verify` is the canonical gate.
- Release-gate physical acceptance is PASS on Windows and iPhone for settings, hidden paths, normal files, and restart behavior. See [the manual matrix](docs/MANUAL_TEST_MATRIX.md) for the recorded sign-off.
Expand Down
2 changes: 1 addition & 1 deletion docs/PROJECT_SOURCE_OF_TRUTH.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This is the current factual reference for Vault Relay. When this document confli
| Mobile | Supported; `isDesktopOnly: false` | `manifest.json`, `tests/manifest.test.ts` |
| Remote service | GitHub REST/Git Data API over HTTPS | `src/github/githubClient.ts` |
| Test files | `44` | `tests/*.test.ts`, current `vitest run` output |
| Passing tests | `482` | current `vitest run` output |
| Passing tests | `483` | current `vitest run` output |
| Quality gate | `PASS` (`npm run verify`, 2026-10-08) | `package.json`, local gate run |

The stable 1.2.0 release is published at `https://github.com/ankhang0704/github-vault-relay/releases/tag/1.2.0`. Provenance is generated by `.github/workflows/release.yml` from immutable tag `1.2.0`.
Expand Down
3 changes: 2 additions & 1 deletion src/ui/conflictResolutionModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ export class ConflictResolutionModal extends Modal {
text: "No active conflicts detected. All files are synchronized or safe.",
cls: "vault-relay-empty-state-text",
});
const closeBtn = contentEl.createEl("button", { text: "Close", cls: "mod-cta vault-relay-btn-lg" });
const actions = contentEl.createDiv({ cls: "vault-relay-action-row" });
const closeBtn = actions.createEl("button", { text: "Close", cls: "mod-cta vault-relay-btn-lg" });
closeBtn.onclick = () => this.close();
return;
}
Expand Down
3 changes: 2 additions & 1 deletion src/ui/syncDashboardModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ export class SyncDashboardModal extends Modal {
cls: "vault-relay-notice-box",
});

const btn = contentEl.createEl("button", { text: "Close", cls: "mod-cta vault-relay-btn-lg" });
const actions = contentEl.createDiv({ cls: "vault-relay-action-row" });
const btn = actions.createEl("button", { text: "Close", cls: "mod-cta vault-relay-btn-lg" });
btn.onclick = () => this.close();
}

Expand Down
139 changes: 127 additions & 12 deletions styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,21 @@
.vault-relay-settings button {
min-height: 44px;
min-width: 44px;
padding: 8px 12px;
white-space: normal;
padding: 8px 14px;
white-space: nowrap;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
margin: 0;
cursor: pointer;
transition: opacity 0.15s ease, background-color 0.15s ease;
}

.vault-relay-btn-lg {
min-height: 44px;
padding: 8px 16px;
font-size: 0.92em;
}

.vault-relay-token-input {
Expand Down Expand Up @@ -112,13 +119,62 @@
opacity: 0.55;
}

.vault-relay-action-row,
.vault-relay-actions-row,
.vault-relay-action-row {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: flex-end;
gap: 10px;
margin-top: 16px;
width: 100%;
max-width: 100%;
}

.vault-relay-action-row button {
margin: 0;
flex: 0 0 auto;
}

.vault-relay-actions-row {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 12px;
margin: 12px 0;
width: 100%;
max-width: 100%;
}

.vault-relay-filter-bar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 12px 0;
width: 100%;
max-width: 100%;
}

.vault-relay-filter-bar button {
min-height: 38px;
min-width: auto;
padding: 6px 12px;
font-size: 0.85em;
border-radius: 6px;
white-space: nowrap;
flex: 0 0 auto;
margin: 0;
}

.vault-relay-settings .setting-item-control,
.vault-relay-modal .setting-item-control {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}

.vault-relay-modal,
.vault-relay-modal *,
.vault-relay-settings,
Expand Down Expand Up @@ -401,9 +457,15 @@
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
margin-top: 10px;
}

.vault-relay-conflict-actions button {
margin: 0;
white-space: nowrap;
}

/* --- Stat Badges & Grids --- */
.vault-relay-stat-grid {
display: grid;
Expand Down Expand Up @@ -748,12 +810,24 @@

.vault-relay-filter-tab {
font-size: 0.78em;
padding: 4px 8px;
padding: 6px 10px;
min-height: 32px;
border-radius: 4px;
white-space: nowrap;
background: transparent;
border: none;
border: 1px solid transparent;
color: var(--text-muted);
cursor: pointer;
}

.vault-relay-filter-tab:hover {
background-color: var(--background-modifier-hover);
color: var(--text-normal);
}

.vault-relay-filter-tab.mod-cta {
background-color: var(--interactive-accent);
color: var(--text-on-accent);
}

.vault-relay-move-title {
Expand Down Expand Up @@ -866,6 +940,12 @@
gap: 6px;
}

.vault-relay-modal button,
.vault-relay-settings button {
white-space: normal;
text-align: center;
}

.vault-relay-modal .setting-item,
.vault-relay-settings .setting-item {
align-items: stretch;
Expand All @@ -874,24 +954,44 @@

.vault-relay-modal .setting-item-control,
.vault-relay-settings .setting-item-control {
flex-wrap: wrap;
display: flex;
flex-direction: column;
gap: 8px;
align-items: stretch;
justify-content: flex-start;
width: 100%;
margin-top: 8px;
}

/* Full-width stacked buttons on narrow touchscreens */
.vault-relay-action-row,
.vault-relay-actions-row,
.vault-relay-conflict-actions {
.vault-relay-action-row {
display: flex;
flex-direction: column-reverse;
gap: 10px;
width: 100%;
margin-top: 14px;
}

.vault-relay-actions-row {
display: flex;
flex-direction: column;
gap: 10px;
width: 100%;
margin: 12px 0;
}

.vault-relay-conflict-actions {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
margin-top: 10px;
}

.vault-relay-action-row button {
width: 100%;
min-height: 44px;
margin-bottom: 0;
margin: 0;
}

.vault-relay-actions-row button,
Expand All @@ -900,7 +1000,22 @@
.vault-relay-settings .setting-item-control button {
width: 100%;
min-height: 44px;
margin-bottom: 0;
margin: 0;
}

.vault-relay-filter-bar {
display: flex;
flex-wrap: wrap;
gap: 6px;
width: 100%;
}

.vault-relay-filter-bar button {
flex: 1 1 calc(50% - 6px);
min-height: 40px;
text-align: center;
justify-content: center;
margin: 0;
}

.vault-relay-file-row {
Expand Down
20 changes: 20 additions & 0 deletions tests/c5MobileAccessibility.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,24 @@ describe("C5-MOBILE: mobile-first static and accessibility audit", () => {
expect(confirmation).toContain('setButtonText("Clear Token")');
expect(confirmation).toContain("This action cannot be undone");
});

it("C5-MOBILE-006: button layouts use flex containers and explicit gaps to prevent overlapping on PC and mobile", () => {
const css = read("styles.css");

// Base button structure
expect(css).toMatch(/\.vault-relay-modal button[\s\S]*display:\s*inline-flex/);
expect(css).toMatch(/\.vault-relay-modal button[\s\S]*box-sizing:\s*border-box/);
expect(css).toMatch(/\.vault-relay-modal button[\s\S]*margin:\s*0/);

// Flex container action rows with explicit gaps
expect(css).toMatch(/\.vault-relay-action-row\s*\{[\s\S]*display:\s*flex;[\s\S]*gap:\s*\d+px;/);
expect(css).toMatch(/\.vault-relay-actions-row\s*\{[\s\S]*display:\s*flex;[\s\S]*gap:\s*\d+px;/);
expect(css).toMatch(/\.vault-relay-filter-bar\s*\{[\s\S]*display:\s*flex;[\s\S]*gap:\s*\d+px;/);
expect(css).toMatch(/\.vault-relay-conflict-actions\s*\{[\s\S]*display:\s*flex;[\s\S]*gap:\s*\d+px;/);

// Mobile media query has explicit vertical gaps and responsive sizing
expect(css).toMatch(/@media\s*\(max-width:\s*480px\)[\s\S]*\.vault-relay-action-row\s*\{[\s\S]*flex-direction:\s*column-reverse;[\s\S]*gap:\s*\d+px;/);
expect(css).toMatch(/@media\s*\(max-width:\s*480px\)[\s\S]*\.vault-relay-actions-row\s*\{[\s\S]*flex-direction:\s*column;[\s\S]*gap:\s*\d+px;/);
expect(css).toMatch(/@media\s*\(max-width:\s*480px\)[\s\S]*\.vault-relay-filter-bar button\s*\{[\s\S]*calc\(50%/);
});
});
Loading