@youtyan/code-viewer 0.1.12 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/web/index.html CHANGED
@@ -26,6 +26,7 @@
26
26
  <nav class="app-menu" aria-label="Views">
27
27
  <a class="app-menu-item active" data-route="repo" href="/">Repository</a>
28
28
  <a class="app-menu-item active" data-route="diff" href="/todif?from=HEAD&to=worktree">Diff Viewer</a>
29
+ <a class="app-menu-item" data-route="help" href="/help">Help</a>
29
30
  </nav>
30
31
  <div class="global-actions">
31
32
  <button id="theme" title="toggle theme">🌗</button>
package/web/style.css CHANGED
@@ -22,6 +22,7 @@
22
22
  --accent-emph: #0550ae;
23
23
  --accent-subtle: #ddf4ff;
24
24
  --accent-muted: #54aeff66;
25
+ --scope-accent: #bf8700;
25
26
  /* success / danger / attention / done */
26
27
  --success: #1a7f37;
27
28
  --success-emph: #1f883d;
@@ -70,6 +71,7 @@
70
71
  --accent-emph: #1f6feb;
71
72
  --accent-subtle: rgba(56,139,253,0.10);
72
73
  --accent-muted: rgba(56,139,253,0.40);
74
+ --scope-accent: #d29922;
73
75
  --success: #3fb950;
74
76
  --success-emph: #238636;
75
77
  --success-subtle: rgba(46,160,67,0.15);
@@ -804,6 +806,18 @@ html, body {
804
806
  padding: 0 0 32px;
805
807
  z-index: 30;
806
808
  }
809
+ body[data-focus-scope="sidebar"] #sidebar {
810
+ background: color-mix(in oklab, var(--bg-soft) 97%, var(--scope-accent) 3%);
811
+ outline: 1px solid color-mix(in oklab, var(--scope-accent) 58%, transparent);
812
+ outline-offset: -1px;
813
+ box-shadow: inset -3px 0 0 var(--scope-accent);
814
+ }
815
+ body[data-focus-scope="main"] #content {
816
+ background: color-mix(in oklab, var(--bg) 98%, var(--scope-accent) 2%);
817
+ outline: 1px solid color-mix(in oklab, var(--scope-accent) 58%, transparent);
818
+ outline-offset: -1px;
819
+ box-shadow: inset 3px 0 0 var(--scope-accent);
820
+ }
807
821
  .sb-head {
808
822
  display: flex; justify-content: space-between; align-items: center;
809
823
  padding: 14px 16px 10px;
@@ -926,6 +940,140 @@ body.gdp-resizing #sidebar-resizer {
926
940
  body.gdp-resizing { cursor: col-resize !important; user-select: none; }
927
941
  body.gdp-resizing * { user-select: none !important; }
928
942
 
943
+ body.gdp-help-page #topbar,
944
+ body.gdp-help-page #sidebar,
945
+ body.gdp-help-page #sidebar-resizer {
946
+ display: none;
947
+ }
948
+ body.gdp-help-page #content {
949
+ margin-left: 0;
950
+ padding-top: calc(var(--global-header-h) + 28px);
951
+ }
952
+ .gdp-help-shell {
953
+ width: 100%;
954
+ max-width: none;
955
+ margin: 0;
956
+ }
957
+ .gdp-help-header {
958
+ display: flex;
959
+ align-items: center;
960
+ justify-content: space-between;
961
+ gap: 16px;
962
+ margin-bottom: 22px;
963
+ }
964
+ .gdp-help-header h1 {
965
+ margin: 0;
966
+ font-size: 28px;
967
+ }
968
+ .gdp-help-language {
969
+ appearance: none;
970
+ min-width: 92px;
971
+ height: 34px;
972
+ border: 1px solid var(--border);
973
+ border-radius: 6px;
974
+ background:
975
+ linear-gradient(45deg, transparent 50%, var(--fg-muted) 50%) right 15px center / 5px 5px no-repeat,
976
+ linear-gradient(135deg, var(--fg-muted) 50%, transparent 50%) right 10px center / 5px 5px no-repeat,
977
+ var(--bg);
978
+ color: var(--fg);
979
+ padding: 0 34px 0 12px;
980
+ font: inherit;
981
+ font-size: 13px;
982
+ line-height: 32px;
983
+ cursor: pointer;
984
+ }
985
+ .gdp-help-language:hover {
986
+ border-color: var(--border-strong);
987
+ }
988
+ .gdp-help-language:focus {
989
+ border-color: var(--accent);
990
+ outline: none;
991
+ box-shadow: 0 0 0 3px var(--accent-muted);
992
+ }
993
+ .gdp-help-layout {
994
+ display: grid;
995
+ grid-template-columns: 220px minmax(0, 1fr);
996
+ gap: 28px;
997
+ align-items: start;
998
+ }
999
+ .gdp-help-nav {
1000
+ position: sticky;
1001
+ top: calc(var(--global-header-h) + 20px);
1002
+ display: grid;
1003
+ gap: 4px;
1004
+ padding-right: 16px;
1005
+ border-right: 1px solid var(--border-muted);
1006
+ }
1007
+ .gdp-help-nav button {
1008
+ appearance: none;
1009
+ border: 0;
1010
+ border-left: 3px solid transparent;
1011
+ border-radius: 0 6px 6px 0;
1012
+ background: transparent;
1013
+ color: var(--fg-muted);
1014
+ padding: 8px 10px;
1015
+ text-align: left;
1016
+ font: inherit;
1017
+ cursor: pointer;
1018
+ }
1019
+ .gdp-help-nav button:hover {
1020
+ background: var(--bg-mute);
1021
+ color: var(--fg);
1022
+ }
1023
+ .gdp-help-nav button.active {
1024
+ border-left-color: var(--accent);
1025
+ background: var(--accent-subtle);
1026
+ color: var(--accent-emph);
1027
+ font-weight: 700;
1028
+ }
1029
+ .gdp-help-content h2 {
1030
+ margin: 0 0 8px;
1031
+ font-size: 24px;
1032
+ }
1033
+ .gdp-help-content > p {
1034
+ margin: 0 0 24px;
1035
+ color: var(--fg-muted);
1036
+ }
1037
+ .gdp-help-group {
1038
+ margin: 0 0 28px;
1039
+ }
1040
+ .gdp-help-group h3 {
1041
+ margin: 0 0 10px;
1042
+ font-size: 16px;
1043
+ }
1044
+ .gdp-help-group table {
1045
+ width: 100%;
1046
+ border-collapse: collapse;
1047
+ border-top: 1px solid var(--border-muted);
1048
+ }
1049
+ .gdp-help-group th,
1050
+ .gdp-help-group td {
1051
+ padding: 11px 8px;
1052
+ border-bottom: 1px solid var(--border-muted);
1053
+ vertical-align: top;
1054
+ }
1055
+ .gdp-help-group th {
1056
+ width: 230px;
1057
+ color: var(--fg);
1058
+ font-weight: 600;
1059
+ text-align: left;
1060
+ }
1061
+ .gdp-help-group td {
1062
+ color: var(--fg-muted);
1063
+ }
1064
+ .gdp-help-group kbd {
1065
+ display: inline-block;
1066
+ min-width: 24px;
1067
+ padding: 2px 6px;
1068
+ border: 1px solid var(--border);
1069
+ border-bottom-color: var(--border-strong);
1070
+ border-radius: 5px;
1071
+ background: var(--bg-soft);
1072
+ color: var(--fg);
1073
+ font: 12px "Monaspace Neon", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
1074
+ text-align: center;
1075
+ }
1076
+
929
1077
  /* ===== Tree view ===== */
930
1078
  #filelist.tree {
931
1079
  padding: 4px 0 0;
@@ -1519,6 +1667,8 @@ table.d2h-diff-table tr.gdp-diff-line-target > td:first-child {
1519
1667
  }
1520
1668
  .gdp-copy-path.copied { color: var(--success); }
1521
1669
  .gdp-copy-path.failed { color: var(--danger); }
1670
+ .gdp-copy-source.copied { color: var(--success); }
1671
+ .gdp-copy-source.failed { color: var(--danger); }
1522
1672
  .gdp-open-path.opened { color: var(--success); }
1523
1673
  .gdp-open-path.failed { color: var(--danger); }
1524
1674
  .gdp-file-unfold[aria-pressed="true"] {
@@ -1738,6 +1888,23 @@ table.d2h-diff-table tr.gdp-diff-line-target > td:first-child {
1738
1888
  color: var(--fg);
1739
1889
  border-bottom-color: var(--blob-tab-active);
1740
1890
  }
1891
+ .gdp-source-tabs .gdp-copy-source {
1892
+ margin-left: auto;
1893
+ align-self: center;
1894
+ width: 28px;
1895
+ height: 28px;
1896
+ padding: 0;
1897
+ border: 1px solid transparent;
1898
+ border-radius: 6px;
1899
+ }
1900
+ .gdp-source-virtual-copy {
1901
+ flex: 0 0 auto;
1902
+ width: 28px;
1903
+ height: 28px;
1904
+ padding: 0;
1905
+ border: 1px solid transparent;
1906
+ border-radius: 6px;
1907
+ }
1741
1908
  .gdp-source-viewer > [hidden],
1742
1909
  .gdp-markdown-layout[hidden],
1743
1910
  .gdp-source-table[hidden] {
@@ -1754,6 +1921,25 @@ table.d2h-diff-table tr.gdp-diff-line-target > td:first-child {
1754
1921
  .gdp-source-table tr.gdp-source-line-target {
1755
1922
  background: var(--line-hit-bg);
1756
1923
  }
1924
+ .gdp-source-table tr.gdp-source-cursor .gdp-source-line-number,
1925
+ .gdp-source-virtual-row.gdp-source-cursor .gdp-source-virtual-line-number {
1926
+ color: var(--fg);
1927
+ box-shadow: inset 3px 0 0 var(--scope-accent), inset -1px 0 0 var(--border-muted);
1928
+ }
1929
+ .gdp-source-table tr.gdp-source-cursor .gdp-source-line-code,
1930
+ .gdp-source-virtual-row.gdp-source-cursor .gdp-source-virtual-line-code {
1931
+ background: color-mix(in oklab, var(--bg) 90%, var(--scope-accent) 10%) !important;
1932
+ }
1933
+ .gdp-source-table tr.gdp-source-cursor .gdp-source-line-code::before,
1934
+ .gdp-source-virtual-row.gdp-source-cursor .gdp-source-virtual-line-code::before {
1935
+ content: "";
1936
+ display: inline-block;
1937
+ width: 2px;
1938
+ height: 1.05em;
1939
+ margin: 0 8px 0 -8px;
1940
+ vertical-align: -0.16em;
1941
+ background: var(--scope-accent);
1942
+ }
1757
1943
  .gdp-source-table tr.gdp-source-line-target .gdp-source-line-number,
1758
1944
  .gdp-source-table tr.gdp-source-line-target .gdp-source-line-code,
1759
1945
  .gdp-source-virtual-row.gdp-source-line-target {
@@ -1907,6 +2093,9 @@ table.d2h-diff-table tr.gdp-diff-line-target > td:first-child {
1907
2093
  border-color: var(--accent);
1908
2094
  color: var(--accent);
1909
2095
  }
2096
+ .gdp-source-virtual-copy:hover {
2097
+ border-color: var(--border);
2098
+ }
1910
2099
  .gdp-source-virtual-scroller {
1911
2100
  position: relative;
1912
2101
  overflow: auto;
package/web-src/routes.ts CHANGED
@@ -19,9 +19,10 @@ export type AppRoute =
19
19
  | { screen: 'repo'; ref: string; path: string; range: DiffRange }
20
20
  | { screen: 'diff'; range: DiffRange; path?: string; line?: SourceLineTarget }
21
21
  | { screen: 'file'; path: string; ref: string; range: DiffRange; view?: 'blob' | 'detail'; line?: SourceLineTarget }
22
+ | { screen: 'help'; range: DiffRange; lang: string; section: string }
22
23
  | { screen: 'unknown'; reason: 'unknown-pathname' | 'missing-path'; rawPathname: string; rawSearch: string; range: DiffRange };
23
24
 
24
- export const SPA_PATHS = ['/todif', '/todiff', '/file'] as const;
25
+ export const SPA_PATHS = ['/todif', '/todiff', '/file', '/help'] as const;
25
26
  export const APP_ENTRY_PATHS = ['/', '/index.html'] as const;
26
27
 
27
28
  export function assertNever(value: never): never {
@@ -89,6 +90,13 @@ export function parseRoute(pathname: string, search: string, fallbackRange: Diff
89
90
  if (!path) return { screen: 'unknown', reason: 'missing-path', rawPathname: pathname, rawSearch: search, range };
90
91
  return { screen: 'file', path, ref, range, view: target ? 'blob' : 'detail', ...(line ? { line } : {}) };
91
92
  }
93
+ case '/help':
94
+ return {
95
+ screen: 'help',
96
+ range,
97
+ lang: params.get('lang') || 'en',
98
+ section: params.get('section') || 'keybindings',
99
+ };
92
100
  default:
93
101
  return { screen: 'unknown', reason: 'unknown-pathname', rawPathname: pathname, rawSearch: search, range };
94
102
  }
@@ -119,6 +127,13 @@ export function buildRoute(route: AppRoute): string {
119
127
  '&to=' + encodeURIComponent(route.range.to || 'worktree') +
120
128
  (route.path ? '&path=' + encodeURIComponent(route.path) : '') +
121
129
  (route.line ? '&line=' + encodeURIComponent(formatLineTarget(route.line)) : '');
130
+ case 'help': {
131
+ const params = new URLSearchParams();
132
+ if (route.lang && route.lang !== 'en') params.set('lang', route.lang);
133
+ if (route.section && route.section !== 'keybindings') params.set('section', route.section);
134
+ const qs = params.toString();
135
+ return '/help' + (qs ? '?' + qs : '');
136
+ }
122
137
  case 'unknown':
123
138
  return '/todif?from=' + encodeURIComponent(route.range.from || '') +
124
139
  '&to=' + encodeURIComponent(route.range.to || 'worktree');