mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-03 21:34:40 +00:00
* feat(web-shell): add git commit history browser Add a read-only Git Log dialog to the Web Shell, accessible via /log command or the History tab in the Changes dialog. Full-stack implementation across core, daemon, SDK, and web-shell: - core: fetchGitLog (paginated commit list) and fetchGitCommitDetail (message body + per-file numstat) with 12 integration tests - daemon: GET /workspace/git/log and /workspace/git/log/commit routes with bound + qualified dual registration - SDK: DaemonGitLog/DaemonGitCommitDetail types and client methods - web-shell: GitLogDialog with commit list, expandable details, SHA copy icon, Load more pagination, and Changes/History tab switching in both dialogs * fix(web-shell): address review feedback on git log browser - Critical: use first-parent diff for merge commits (diff-tree without -c or explicit parent outputs nothing for merges) - Remove dead embedded prop from GitDiffDialog and GitLogDialog - Replace span role=button with aria-hidden for copy icon (a11y) - Add loadMore error feedback instead of silent catch - Refresh relative timestamps every 60s (useState + interval) - Remove dead branch param from subtitle i18n call * fix(web-shell): address R2 review feedback on git log browser - Use bounded split in parseLogFields (first 7 separators) to prevent subject containing literal \x1f from shifting the parents field - Add SHA hex format validation at daemon route layer (400 for invalid) - Add rendering branch for detail.available === false (error message instead of empty content) * fix(web-shell): count renamed files in commit detail + test git-log route & dialog Follows the R2 review-feedback commit (which fixed the bounded parse, the non-hex SHA 400, and the unavailable-detail render). Remaining items: - Commit detail counts renamed files. diff-tree is plumbing and does not honour diff.renames, so a `git mv` split into a delete + add pair (or an empty-path entry) instead of one file — understating filesCount / linesAdded / linesRemoved. Run diff-tree with -M and give the inline numstat parser the same pending-rename state machine as parseGitNumstat, so a rename is one file keyed by its new path. Covered by a real-repo rename test, plus a merge-commit test that locks the first-parent diff. - Tests for the two previously-untested modules: the workspace-git-log route (list shape, pagination clamping, sha-required + non-hex 400, trust gating) and GitLogDialog (all five list state paths, load-more offset + error, detail expand + both failure branches incl. available:false, and the relative-time render). * fix(web-shell): address R3 review feedback on git log browser - Fix timeAgo '0y ago' for commits ~360-364 days old (Math.max(1, ...)) - Add .catch() to clipboard writeText to prevent unhandled rejection - Reset loadMoreError on initial re-fetch (daemon reconnect) - Preserve prev.available in loadMore merge instead of overwriting - Add ARIA tab semantics (role=tablist/tab, aria-selected) to both Changes and History tab bars * fix(web-shell): address R4 review feedback on git log browser - Fix vacuous limit-clamp test: seed 3 commits, verify limit=2 returns 2 + hasMore, limit=0 clamps to 1 - Add CSS var fallbacks for --subtle-bg and --success-bg (dialog portals outside App.module.css scope) - Extract GIT_DIALOG_SWITCH_DELAY_MS constant with doc comment - Make copy-SHA control keyboard accessible (tabIndex, onKeyDown, aria-label instead of aria-hidden) * fix(web-shell): escape apostrophe in worktree welcome string * fix(web-shell): address git history review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(web-shell): mock git diff content in app tests Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): harden git log metadata parsing Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: wenshao <wenshao@example.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
165 lines
2.4 KiB
CSS
165 lines
2.4 KiB
CSS
.content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 50vh;
|
|
}
|
|
|
|
.placeholder {
|
|
padding: 24px 12px;
|
|
text-align: center;
|
|
color: var(--muted-foreground);
|
|
}
|
|
|
|
.fileList {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
}
|
|
|
|
.file {
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.fileHeader {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
width: 100%;
|
|
padding: 6px 10px;
|
|
background: var(--subtle-bg);
|
|
border: 0;
|
|
cursor: pointer;
|
|
font: inherit;
|
|
text-align: left;
|
|
color: inherit;
|
|
}
|
|
|
|
.fileHeader:hover {
|
|
background: var(--subtle-bg-strong);
|
|
}
|
|
|
|
.fileStats {
|
|
display: inline-flex;
|
|
gap: 6px;
|
|
flex-shrink: 0;
|
|
font-size: 11px;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.statAdd {
|
|
color: var(--success-color);
|
|
}
|
|
|
|
.statDel {
|
|
color: var(--error-color);
|
|
}
|
|
|
|
.fileBinary {
|
|
color: var(--muted-foreground);
|
|
}
|
|
|
|
.filePath {
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.fileOldPath {
|
|
color: var(--muted-foreground);
|
|
}
|
|
|
|
.fileTag {
|
|
flex-shrink: 0;
|
|
padding: 0 6px;
|
|
border-radius: 4px;
|
|
background: var(--muted);
|
|
color: var(--muted-foreground);
|
|
font-size: 10px;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.fileBody {
|
|
border-top: 1px solid var(--border);
|
|
}
|
|
|
|
.filePlaceholder {
|
|
padding: 12px;
|
|
color: var(--muted-foreground);
|
|
font-size: 12px;
|
|
}
|
|
|
|
.hiddenNote {
|
|
padding: 4px 10px;
|
|
color: var(--muted-foreground);
|
|
font-size: 11px;
|
|
}
|
|
|
|
.diffLines {
|
|
max-height: 480px;
|
|
overflow: auto;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.diffLine {
|
|
display: flex;
|
|
min-height: 18px;
|
|
line-height: 18px;
|
|
}
|
|
|
|
.diffLineAdd {
|
|
background: var(--success-bg);
|
|
}
|
|
|
|
.diffLineDel {
|
|
background: var(--error-bg);
|
|
}
|
|
|
|
.diffLineContext {
|
|
background: transparent;
|
|
}
|
|
|
|
.diffLineMeta {
|
|
background: transparent;
|
|
color: var(--muted-foreground);
|
|
font-style: italic;
|
|
}
|
|
|
|
.diffOldNo,
|
|
.diffNewNo {
|
|
width: 40px;
|
|
flex-shrink: 0;
|
|
text-align: right;
|
|
padding-right: 6px;
|
|
user-select: none;
|
|
color: var(--muted-foreground);
|
|
opacity: 0.6;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.diffMarker {
|
|
width: 14px;
|
|
flex-shrink: 0;
|
|
text-align: center;
|
|
user-select: none;
|
|
color: var(--muted-foreground);
|
|
}
|
|
|
|
.diffLineAdd .diffMarker {
|
|
color: var(--success-color);
|
|
}
|
|
|
|
.diffLineDel .diffMarker {
|
|
color: var(--error-color);
|
|
}
|
|
|
|
.diffContent {
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
white-space: pre;
|
|
overflow-x: auto;
|
|
padding-right: 8px;
|
|
}
|