@arshdelight/practi 0.9.5 → 0.11.0
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/README.md +51 -14
- package/dist/client.js +49 -6
- package/dist/client.js.map +1 -1
- package/dist/cmd/blob.js +10 -1
- package/dist/cmd/blob.js.map +1 -1
- package/dist/cmd/clone.js +13 -7
- package/dist/cmd/clone.js.map +1 -1
- package/dist/cmd/comment.js +50 -15
- package/dist/cmd/comment.js.map +1 -1
- package/dist/cmd/config.js +1 -1
- package/dist/cmd/config.js.map +1 -1
- package/dist/cmd/edit.js +80 -8
- package/dist/cmd/edit.js.map +1 -1
- package/dist/cmd/gc.js +77 -0
- package/dist/cmd/gc.js.map +1 -0
- package/dist/cmd/lifecycle.js +63 -13
- package/dist/cmd/lifecycle.js.map +1 -1
- package/dist/cmd/login.js +13 -4
- package/dist/cmd/login.js.map +1 -1
- package/dist/cmd/ls.js +41 -2
- package/dist/cmd/ls.js.map +1 -1
- package/dist/cmd/migrate.js +3 -0
- package/dist/cmd/migrate.js.map +1 -1
- package/dist/cmd/new.js +50 -3
- package/dist/cmd/new.js.map +1 -1
- package/dist/cmd/note.js +160 -0
- package/dist/cmd/note.js.map +1 -0
- package/dist/cmd/push.js +6 -1
- package/dist/cmd/push.js.map +1 -1
- package/dist/cmd/remote.js +15 -8
- package/dist/cmd/remote.js.map +1 -1
- package/dist/cmd/remove.js +42 -0
- package/dist/cmd/remove.js.map +1 -0
- package/dist/cmd/repair.js +1 -1
- package/dist/cmd/repair.js.map +1 -1
- package/dist/cmd/search.js +154 -57
- package/dist/cmd/search.js.map +1 -1
- package/dist/cmd/show.js +97 -9
- package/dist/cmd/show.js.map +1 -1
- package/dist/index.js +134 -36
- package/dist/index.js.map +1 -1
- package/dist/notes.js +121 -0
- package/dist/notes.js.map +1 -0
- package/dist/oauth.js +3 -0
- package/dist/oauth.js.map +1 -1
- package/dist/state.js +2 -1
- package/dist/state.js.map +1 -1
- package/dist/web.js +128 -13
- package/dist/web.js.map +1 -1
- package/package.json +1 -1
- package/skills/use-practi/SKILL.md +52 -15
- package/web-default/app.js +2 -2
- package/web-default/detail.html +6 -1
- package/web-default/detail.js +510 -15
- package/web-default/highlight.min.js +1262 -0
- package/web-default/lang.js +19 -7
- package/web-default/style.css +121 -10
package/web-default/lang.js
CHANGED
|
@@ -10,12 +10,12 @@ var POP_I18N = (function () {
|
|
|
10
10
|
// 字典值:字符串,或函数(复数/拼接类文案按参数生成)
|
|
11
11
|
var M = {
|
|
12
12
|
zh: {
|
|
13
|
-
settings: '设置', language: '语言', done: '完成',
|
|
13
|
+
settings: '设置', language: '语言', done: '完成', copied: '已复制', back: '返回', metaInfo: '文档信息',
|
|
14
14
|
tabWizard: '步骤', tabSv: '标准视图', tabDoc: '文档树',
|
|
15
|
-
labelDocJson: '文档 JSON', labelSvJson: '
|
|
15
|
+
labelDocJson: '文档 JSON', labelSvJson: '标准视图 JSON',
|
|
16
16
|
// 目录页
|
|
17
17
|
pageTitle: '我的实践',
|
|
18
|
-
thDocument: '文档',
|
|
18
|
+
thDocument: '文档', thNodes: '节点', thAdded: '添加时间',
|
|
19
19
|
emptyLead: '还没有记录。写下第一手实践经验,成为一份 POP 文档——哈希可验证,永久保存。',
|
|
20
20
|
emptyHint: '用 <code>practi new</code> 创建',
|
|
21
21
|
countDocs: function (n) { return n + ' 篇文档'; },
|
|
@@ -23,7 +23,7 @@ var POP_I18N = (function () {
|
|
|
23
23
|
pagerPrev: '上一页', pagerNext: '下一页',
|
|
24
24
|
loadFail: '加载失败(/api/directs)',
|
|
25
25
|
// 详情页
|
|
26
|
-
|
|
26
|
+
nodeCount: function (n) { return n + ' 个节点'; },
|
|
27
27
|
loading: '加载中…',
|
|
28
28
|
opPar: '以下任务并行执行——顺序无关',
|
|
29
29
|
opChoice: '选择一个分支继续',
|
|
@@ -41,22 +41,28 @@ var POP_I18N = (function () {
|
|
|
41
41
|
endPractice: '实践完结',
|
|
42
42
|
revisions: function (n) { return '修订 (' + n + ')'; },
|
|
43
43
|
refines: function (n) { return '改进 (' + n + ')'; },
|
|
44
|
+
secNotes: function (n) { return '笔记 (' + n + ')'; },
|
|
45
|
+
notePanel: '笔记',
|
|
46
|
+
noteNew: '新建',
|
|
47
|
+
noteDelete: '删除',
|
|
48
|
+
noteEmpty: '这条节点还没有笔记,点上方「新建」写一条。',
|
|
49
|
+
noteServerStale: '服务器像是旧构建(前端新、服务器旧)——重启 practi web 再试',
|
|
44
50
|
docFail: function (s) { return '文档加载失败(' + s + ')'; },
|
|
45
51
|
backMine: '← 我的实践'
|
|
46
52
|
},
|
|
47
53
|
en: {
|
|
48
|
-
settings: 'Settings', language: 'Language', done: 'Done',
|
|
54
|
+
settings: 'Settings', language: 'Language', done: 'Done', copied: 'Copied', back: 'Back', metaInfo: 'Document info',
|
|
49
55
|
tabWizard: 'Todo', tabSv: 'StandardView', tabDoc: 'Document',
|
|
50
56
|
labelDocJson: 'Document JSON', labelSvJson: 'StandardView JSON',
|
|
51
57
|
pageTitle: 'My practices',
|
|
52
|
-
thDocument: 'Document',
|
|
58
|
+
thDocument: 'Document', thNodes: 'Nodes', thAdded: 'Added',
|
|
53
59
|
emptyLead: 'Nothing recorded yet. Write first-hand experience as a POP document — verifiable by hash, forever.',
|
|
54
60
|
emptyHint: 'create one with <code>practi new</code>',
|
|
55
61
|
countDocs: function (n) { return n + (n === 1 ? ' document' : ' documents'); },
|
|
56
62
|
pageOf: function (a, b) { return 'page ' + a + ' of ' + b; },
|
|
57
63
|
pagerPrev: 'Previous', pagerNext: 'Next',
|
|
58
64
|
loadFail: 'failed to load /api/directs',
|
|
59
|
-
|
|
65
|
+
nodeCount: function (n) { return n + (n === 1 ? ' node' : ' nodes'); },
|
|
60
66
|
loading: 'loading…',
|
|
61
67
|
opPar: 'the tasks below run in parallel — order does not matter',
|
|
62
68
|
opChoice: 'pick one branch to continue',
|
|
@@ -74,6 +80,12 @@ var POP_I18N = (function () {
|
|
|
74
80
|
endPractice: 'end of practice',
|
|
75
81
|
revisions: function (n) { return 'revisions (' + n + ')'; },
|
|
76
82
|
refines: function (n) { return 'refines (' + n + ')'; },
|
|
83
|
+
secNotes: function (n) { return 'notes (' + n + ')'; },
|
|
84
|
+
notePanel: 'Notes',
|
|
85
|
+
noteNew: 'New',
|
|
86
|
+
noteDelete: 'Delete',
|
|
87
|
+
noteEmpty: 'No notes on this node yet — click New above to write one.',
|
|
88
|
+
noteServerStale: 'the server looks like an older build — restart practi web and retry',
|
|
77
89
|
docFail: function (s) { return 'failed to load document (' + s + ')'; },
|
|
78
90
|
backMine: '← My practices'
|
|
79
91
|
}
|
package/web-default/style.css
CHANGED
|
@@ -47,13 +47,13 @@ a:hover { text-decoration: underline; }
|
|
|
47
47
|
fixed 布局是必须的:hash 是无空格长串,auto 布局下 max-width 失效会把表撑出横向滚动 */
|
|
48
48
|
.doc-table { width: 100%; border-collapse: collapse; font-size: 14px; table-layout: fixed; }
|
|
49
49
|
.doc-table th { padding: 8px 16px; font-size: 12px; font-weight: 500; color: var(--brand-50); text-align: left; white-space: nowrap; border-bottom: 1px solid var(--border); }
|
|
50
|
-
.doc-table th.col-
|
|
50
|
+
.doc-table th.col-nodes { width: 8ch; }
|
|
51
51
|
.doc-table th.col-added { width: 22ch; }
|
|
52
52
|
.doc-table td { padding: 12px 16px; vertical-align: middle; border-bottom: 1px solid var(--border-light); overflow: hidden; text-overflow: ellipsis; }
|
|
53
53
|
.doc-table tr:last-child td { border-bottom: none; }
|
|
54
54
|
.doc-name { font-weight: 500; color: var(--brand); }
|
|
55
55
|
.doc-desc { font-size: 12px; color: var(--brand-40); margin: 2px 0 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
56
|
-
.doc-
|
|
56
|
+
.doc-nodes { font-size: 12px; color: var(--brand-40); font-variant-numeric: tabular-nums; }
|
|
57
57
|
.doc-date { font-size: 12px; color: var(--brand-40); white-space: nowrap; font-variant-numeric: tabular-nums; }
|
|
58
58
|
.doc-date.derived { opacity: .6; }
|
|
59
59
|
.count-row { display: flex; justify-content: space-between; align-items: center; margin-top: 24px; font-size: 14px; color: var(--brand-60); }
|
|
@@ -67,7 +67,11 @@ a:hover { text-decoration: underline; }
|
|
|
67
67
|
.empty code { font-family: ui-monospace, "Cascadia Code", Consolas, monospace; }
|
|
68
68
|
|
|
69
69
|
/* ── 详情页(向导模式):一次一节点,op 决定呈现 ── */
|
|
70
|
-
|
|
70
|
+
/* 内容列=详情页唯一的滚动容器(应用壳布局);gutter 常驻防长文档切换时内容左右微移 */
|
|
71
|
+
/*.detail 是滚动容器且拉满全宽——滚动条在窗口最右而非内容列右缘;
|
|
72
|
+
内容限宽由子元素承担(.nav 是 fixed 定位,排除在限宽外)*/
|
|
73
|
+
.detail { max-width: none; overflow-y: auto; scrollbar-gutter: stable; padding-bottom: 110px; }
|
|
74
|
+
.detail > *:not(.nav) { max-width: 48rem; margin-inline: auto; }
|
|
71
75
|
.node-title { font-size: 22px; font-weight: 700; color: var(--brand); margin: 8px 0 4px; }
|
|
72
76
|
.node-desc { color: var(--brand-60); margin: 0 0 12px; }
|
|
73
77
|
.op-note { font-size: 13px; color: var(--brand-40); font-style: italic; margin: 0 0 8px; }
|
|
@@ -76,6 +80,30 @@ a:hover { text-decoration: underline; }
|
|
|
76
80
|
.section-sm .prose p:first-child { margin-top: 0; }
|
|
77
81
|
.section-sm .prose p:last-child { margin-bottom: 0; }
|
|
78
82
|
.prose pre { background: #fff; border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; overflow-x: auto; font-size: 13px; margin: 12px 0; }
|
|
83
|
+
/* 围栏代码块深色卡(detail.js enhanceCodeBlocks 生成,与 hub PopCodeBlock 统一:
|
|
84
|
+
GitHub Dark 底 #0d1117 + 顶栏语言标签/复制按钮);未增强的 pre 仍走上方白底规则 */
|
|
85
|
+
.codeblock { margin: 12px 0; border-radius: 12px; overflow: hidden; background: #0d1117; }
|
|
86
|
+
.codeblock-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; background: #161b22; border-bottom: 1px solid #262c36; padding: 4px 12px; font-family: ui-monospace, "Cascadia Code", Consolas, monospace; font-size: 10px; color: #94a3b8; user-select: none; }
|
|
87
|
+
.codeblock-copy { display: flex; align-items: center; gap: 4px; border: none; background: none; color: #94a3b8; cursor: pointer; padding: 2px 6px; border-radius: 6px; font-size: 10px; font-family: inherit; }
|
|
88
|
+
.codeblock-copy:hover { color: #f1f5f9; background: rgba(255, 255, 255, .1); }
|
|
89
|
+
.codeblock pre { margin: 0; border: none; border-radius: 0; background: #0d1117; color: #c9d1d9; padding: 14px 16px; font-size: 13px; }
|
|
90
|
+
.codeblock pre code { background: transparent; border: none; padding: 0; font-size: inherit; color: inherit; }
|
|
91
|
+
/* hljs token 色板:与 hub globals.css 同源(github-dark),两处同步改 */
|
|
92
|
+
.codeblock .hljs { color: #c9d1d9; }
|
|
93
|
+
.codeblock .hljs-doctag, .codeblock .hljs-keyword, .codeblock .hljs-meta .hljs-keyword, .codeblock .hljs-template-tag, .codeblock .hljs-template-variable, .codeblock .hljs-type, .codeblock .hljs-variable.language_ { color: #ff7b72; }
|
|
94
|
+
.codeblock .hljs-title, .codeblock .hljs-title.class_, .codeblock .hljs-title.class_.inherited__, .codeblock .hljs-title.function_ { color: #d2a8ff; }
|
|
95
|
+
.codeblock .hljs-attr, .codeblock .hljs-attribute, .codeblock .hljs-literal, .codeblock .hljs-meta, .codeblock .hljs-number, .codeblock .hljs-operator, .codeblock .hljs-variable, .codeblock .hljs-selector-attr, .codeblock .hljs-selector-class, .codeblock .hljs-selector-id { color: #79c0ff; }
|
|
96
|
+
.codeblock .hljs-regexp, .codeblock .hljs-string, .codeblock .hljs-meta .hljs-string { color: #a5d6ff; }
|
|
97
|
+
.codeblock .hljs-built_in, .codeblock .hljs-symbol { color: #ffa657; }
|
|
98
|
+
.codeblock .hljs-comment, .codeblock .hljs-code, .codeblock .hljs-formula { color: #8b949e; }
|
|
99
|
+
.codeblock .hljs-name, .codeblock .hljs-quote, .codeblock .hljs-selector-tag, .codeblock .hljs-selector-pseudo { color: #7ee787; }
|
|
100
|
+
.codeblock .hljs-subst { color: #c9d1d9; }
|
|
101
|
+
.codeblock .hljs-section { color: #1f6feb; font-weight: bold; }
|
|
102
|
+
.codeblock .hljs-bullet { color: #f2cc60; }
|
|
103
|
+
.codeblock .hljs-emphasis { color: #c9d1d9; font-style: italic; }
|
|
104
|
+
.codeblock .hljs-strong { color: #c9d1d9; font-weight: bold; }
|
|
105
|
+
.codeblock .hljs-addition { color: #aff5b4; background-color: #033a16; }
|
|
106
|
+
.codeblock .hljs-deletion { color: #ffdcd7; background-color: #67060c; }
|
|
79
107
|
.prose figure { margin: 14px 0; }
|
|
80
108
|
.prose img { max-width: 100%; border: 1px solid var(--border); border-radius: 12px; box-shadow: 0 1px 2px rgba(0, 0, 0, .08); }
|
|
81
109
|
.prose figcaption { font-size: 12px; color: var(--brand-40); text-align: center; margin-top: 4px; }
|
|
@@ -120,8 +148,13 @@ ol.io-list li { padding: 2px 0; }
|
|
|
120
148
|
.io-ref:hover { color: var(--brand); text-decoration: none; }
|
|
121
149
|
.section-sm { margin-top: 18px; }
|
|
122
150
|
.section-sm .label { font-size: 12px; font-weight: 600; color: var(--brand-50); margin-bottom: 6px; }
|
|
151
|
+
/* JSON 视图 label 行:label 居左 + 复制按钮居右(按钮复用 .codeblock-copy 深色样式,在浅底上取中性色) */
|
|
152
|
+
.label-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }
|
|
153
|
+
.label-row .label { margin-bottom: 0; }
|
|
154
|
+
.label-row .json-copy { color: var(--brand-40); }
|
|
155
|
+
.label-row .json-copy:hover { color: var(--brand); background: rgba(0, 51, 102, .06); }
|
|
123
156
|
.att-list li { padding: 4px 0; }
|
|
124
|
-
.child-card { border: 1px solid var(--border); border-radius: 10px; padding: 10px 14px; margin-bottom: 8px; font-size: 14px; color: var(--brand-60); background: #fff; }
|
|
157
|
+
.child-card { display: block; border: 1px solid var(--border); border-radius: 10px; padding: 10px 14px; margin-bottom: 8px; font-size: 14px; color: var(--brand-60); background: #fff; }
|
|
125
158
|
button.child-card { width: 100%; text-align: left; cursor: pointer; font: inherit; }
|
|
126
159
|
button.child-card:hover { border-color: var(--brand); color: var(--brand); }
|
|
127
160
|
.child-desc { font-size: 12px; color: var(--brand-40); margin-top: 2px; display: block; }
|
|
@@ -130,7 +163,9 @@ button.child-card:hover { border-color: var(--brand); color: var(--brand); }
|
|
|
130
163
|
.choice-card:hover { border-color: var(--brand); background: rgba(0, 51, 102, .04); }
|
|
131
164
|
/* 底部导航固定在视口底部:位置恒定,不随内容高度漂移;内行与正文列同宽对齐 */
|
|
132
165
|
/* 底部操作区:固定视口底悬浮于内容列上方,无分界线(仅极轻投影托起),行与正文列同宽 */
|
|
133
|
-
.nav { position: fixed; left: 0; right: 0; bottom: 0; z-index: 10; background: #fff; padding: 10px 24px; box-shadow: 0 -1px 8px rgba(0, 51, 102, .03); }
|
|
166
|
+
.nav { position: fixed; left: 0; right: 0; bottom: 0; z-index: 10; background: #fff; padding: 10px 24px; box-shadow: 0 -1px 8px rgba(0, 51, 102, .03); transition: right .25s ease; }
|
|
167
|
+
/* 笔记栏展开时操作区同步让位(与内容列居中一起平滑重排) */
|
|
168
|
+
body.note-open .nav { right: 18rem; }
|
|
134
169
|
.nav-row { max-width: 48rem; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; gap: 12px; }
|
|
135
170
|
.btn { padding: 8px 16px; border: 1px solid #d1d5db; border-radius: 8px; background: #fff; color: var(--brand-60); font-size: 14px; cursor: pointer; text-decoration: none; display: inline-block; }
|
|
136
171
|
.btn:hover { background: var(--hover-bg); text-decoration: none; }
|
|
@@ -138,9 +173,13 @@ button.child-card:hover { border-color: var(--brand); color: var(--brand); }
|
|
|
138
173
|
.btn-primary:hover { background: #1f2937; }
|
|
139
174
|
.nav-hint { font-size: 13px; color: var(--brand-40); }
|
|
140
175
|
.finish { font-size: 13px; color: var(--brand-40); }
|
|
141
|
-
/* ──
|
|
142
|
-
.
|
|
143
|
-
.
|
|
176
|
+
/* ── 详情页外壳:应用壳布局——整页不滚(滚动只属于中间内容列),header 常驻、左右栏满高 ── */
|
|
177
|
+
body.detail-page { height: 100dvh; overflow: hidden; display: flex; flex-direction: column; }
|
|
178
|
+
body.detail-page > header { flex: none; }
|
|
179
|
+
.shell { display: flex; align-items: stretch; flex: 1; min-height: 0; position: relative; }
|
|
180
|
+
/* main 右缘留 26px 舌片竖轨:#note-toggle 固定钉右缘,不留轨会盖住 main 自己的滚动条
|
|
181
|
+
(面板展开时同理——轨在 main 与面板之间) */
|
|
182
|
+
.shell main { flex: 1; min-width: 0; margin-right: 26px; }
|
|
144
183
|
.side { display: none; }
|
|
145
184
|
.side-count { font-size: 12px; font-weight: 500; color: var(--brand-40); margin: 0 0 8px; }
|
|
146
185
|
.side-item { display: block; border-radius: 6px; padding: 6px 10px; font-size: 13px; line-height: 1.45; color: var(--brand-60); }
|
|
@@ -159,7 +198,7 @@ a.side-note:hover { color: var(--brand); text-decoration: none; background: rgba
|
|
|
159
198
|
/* 档案底仓:钉在侧栏最底(flex 末位),大纲长过侧栏时随滚动自然跟在内容后 */
|
|
160
199
|
.side-foot { margin-top: auto; padding-top: 4px; }
|
|
161
200
|
@media (min-width: 1024px) {
|
|
162
|
-
.side { display: flex; flex-direction: column;
|
|
201
|
+
.side { display: flex; flex-direction: column; overflow-y: auto; width: 16rem; flex: none; border-right: 1px solid var(--border); background: #f8fafc; padding: 12px 14px 24px; }
|
|
163
202
|
/* 操作区只覆盖内容列:left 让出侧栏,侧栏档案区完整可见 */
|
|
164
203
|
.nav { left: 16rem; }
|
|
165
204
|
}
|
|
@@ -171,7 +210,7 @@ a.side-note:hover { color: var(--brand); text-decoration: none; background: rgba
|
|
|
171
210
|
.side-tab:hover { color: var(--brand); }
|
|
172
211
|
.side-tab.on { background: rgba(0, 51, 102, .1); color: var(--brand); font-weight: 500; }
|
|
173
212
|
/* JSON 视图:pretty 渲染,当前节点/其子树 steps 命中行整行铺浅蓝底(编辑器选中行形态) */
|
|
174
|
-
.json-pre { margin: 0; padding: 12px 14px; border: 1px solid var(--border); border-radius: 10px; background: #fff; font-family: ui-monospace, "Cascadia Code", Consolas, monospace; font-size: 13px; line-height: 1.6; color: var(--brand); overflow
|
|
213
|
+
.json-pre { margin: 0; padding: 12px 14px; border: 1px solid var(--border); border-radius: 10px; background: #fff; font-family: ui-monospace, "Cascadia Code", Consolas, monospace; font-size: 13px; line-height: 1.6; color: var(--brand); overflow: auto; white-space: pre; max-height: calc(100dvh - 260px); }
|
|
175
214
|
.json-pre .jk { color: var(--brand-60); }
|
|
176
215
|
/* 行级块:短行铺满可视宽、长行铺到内容末尾,高亮行首尾与缩进、逗号全部入色带 */
|
|
177
216
|
.json-pre .ln { display: block; width: max-content; min-width: 100%; }
|
|
@@ -179,4 +218,76 @@ a.side-note:hover { color: var(--brand); text-decoration: none; background: rgba
|
|
|
179
218
|
/* revisions 折叠:节点自己的变更史,默认收起,好奇点开 */
|
|
180
219
|
/* revisions 折叠已删:revisions/refines 汇总挪到左侧大纲底部(.side-sec) */
|
|
181
220
|
|
|
221
|
+
/* 学习笔记:只住右侧笔记栏(内容区不出现);note-time 由右栏条目复用 */
|
|
222
|
+
.note-time { display: block; font-size: 12px; color: var(--brand-40); font-variant-numeric: tabular-nums; margin-bottom: 2px; }
|
|
223
|
+
/* 侧栏大纲的笔记数徽标:实心、可点(跳到该节点并展开笔记栏),
|
|
224
|
+
边框/文字继承所在条目的颜色(currentColor)——普通态/选中态/根标题自动同色 */
|
|
225
|
+
.note-badge { display: inline-block; min-width: 14px; height: 13px; line-height: 13px; padding: 0 4px; border-radius: 999px; background: var(--primary); color: #f8fafc; font-size: 9px; font-weight: 600; text-align: center; margin-left: 5px; vertical-align: middle; cursor: pointer; }
|
|
226
|
+
.note-badge:hover { background: #374151; }
|
|
227
|
+
|
|
228
|
+
/* ── 右侧笔记栏:布局列(非悬浮)——宽度 0↔18rem 平滑过渡,内容列随之在两栏之间保持居中 ── */
|
|
229
|
+
#note-side { flex: none; width: 0; overflow: hidden; align-self: stretch; transition: width .25s ease; }
|
|
230
|
+
#note-side.open { width: 18rem; }
|
|
231
|
+
/* 内层定宽:动画期间内容不被挤压,从右缘逐步露出(抽屉观感);边框/投影挂内层,
|
|
232
|
+
收起宽度 0 时被 overflow 裁掉,不留残线 */
|
|
233
|
+
#note-list { width: 18rem; height: 100%; overflow-y: auto; overflow-x: hidden; display: flex; flex-direction: column; background: #f8fafc; border-left: 1px solid var(--border); box-shadow: 0 0 24px rgba(0, 51, 102, .08); padding: 14px 14px 16px; }
|
|
234
|
+
/* 展开收起舌片:钉在面板左缘垂直居中(面板的相邻兄弟,right 与面板宽度同参数过渡即贴合);
|
|
235
|
+
收起后停在屏幕右缘仍可点 */
|
|
236
|
+
#note-toggle { position: absolute; right: 0; top: 50%; transform: translateY(-50%); width: 26px; height: 56px; display: flex; align-items: center; justify-content: center; border: 1px solid var(--border); border-right: none; border-radius: 8px 0 0 8px; background: #f8fafc; color: var(--brand-40); cursor: pointer; z-index: 5; transition: right .25s ease; }
|
|
237
|
+
#note-side.open + #note-toggle { right: 18rem; }
|
|
238
|
+
#note-toggle:hover { color: var(--brand); }
|
|
239
|
+
/* 箭头语义=面板去向:收起态朝左(展开=面板从右缘向左滑出),展开态朝右(收起=向右滑走) */
|
|
240
|
+
#note-toggle svg { width: 14px; height: 14px; transition: transform .25s ease; transform: rotate(180deg); }
|
|
241
|
+
#note-side.open + #note-toggle svg { transform: rotate(0deg); }
|
|
242
|
+
.nside-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
|
|
243
|
+
.nside-title { margin: 0; font-size: 12px; font-weight: 600; color: var(--brand-50); }
|
|
244
|
+
/* 新建按钮:面板头部的小胶囊 */
|
|
245
|
+
.nside-new { flex: none; display: inline-flex; align-items: center; justify-content: center; border: none; background: transparent; color: var(--brand-40); cursor: pointer; padding: 2px; }
|
|
246
|
+
.nside-new svg { width: 12px; height: 12px; }
|
|
247
|
+
.nside-new:hover { color: var(--brand); }
|
|
248
|
+
/* 头部贴面板顶+右缘:气泡朝下开、右缘对齐,避免被 #note-list 的 overflow 裁掉 */
|
|
249
|
+
.nside-new[data-tip]::after { left: auto; right: 0; bottom: auto; top: calc(100% + 6px); transform: none; }
|
|
250
|
+
.nside-empty { font-size: 12px; line-height: 1.6; color: var(--brand-40); margin: 0 0 10px; }
|
|
251
|
+
.nside-err { font-size: 12px; line-height: 1.5; color: #b91c1c; background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 6px 10px; margin: 0 0 10px; overflow-wrap: break-word; }
|
|
252
|
+
.nside-item { background: #fff; border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px; margin-bottom: 8px; }
|
|
253
|
+
.nside-meta { display: flex; align-items: center; justify-content: space-between; gap: 6px; margin-bottom: 3px; }
|
|
254
|
+
.nside-acts { display: inline-flex; gap: 2px; }
|
|
255
|
+
.nside-act { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border: none; border-radius: 6px; background: transparent; color: var(--brand-30); cursor: pointer; }
|
|
256
|
+
.nside-act svg { width: 13px; height: 13px; }
|
|
257
|
+
.nside-act:hover { background: var(--hover-bg); color: var(--brand); }
|
|
258
|
+
.nside-item .note-time { font-size: 11px; }
|
|
259
|
+
/* 行内编辑态与底部新增框共用输入框形态 */
|
|
260
|
+
.nside-input { display: block; width: 100%; border: none; background: transparent; resize: none; overflow: hidden; padding: 0; margin: 0; color: var(--brand-60); font-family: inherit; font-size: 13px; line-height: 1.6; cursor: default; caret-color: transparent; transition: color .15s ease; }
|
|
261
|
+
/* 支持 field-sizing 的浏览器:输入框只占文字本身宽度——点卡片空白处不会出现光标,
|
|
262
|
+
点文字才进编辑且光标落在点击处;autoGrow 在 JS 侧对支持的浏览器跳过 */
|
|
263
|
+
@supports (field-sizing: content) {
|
|
264
|
+
.nside-input { field-sizing: content; width: auto; min-width: 24px; max-width: 100%; min-height: 0; }
|
|
265
|
+
}
|
|
266
|
+
.nside-input:focus { outline: none; cursor: text; caret-color: auto; color: var(--primary); }
|
|
267
|
+
|
|
182
268
|
/* LR kick */
|
|
269
|
+
|
|
270
|
+
/* 复制 toast:对齐 hub sonner(richColors, position=top-center)——白底细边浅投影、
|
|
271
|
+
绿色对勾圆标居左,顶部居中下滑淡入 */
|
|
272
|
+
.web-toast { position: fixed; top: 16px; left: 50%; transform: translateX(-50%) translateY(-8px); background: #fff; color: #1f2937; font-size: 13px; padding: 10px 14px; border: 1px solid #e5e7eb; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, .08); display: flex; align-items: center; gap: 8px; opacity: 0; pointer-events: none; transition: opacity .2s, transform .2s; z-index: 60; }
|
|
273
|
+
.web-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
|
|
274
|
+
|
|
275
|
+
/* ── 内容列顶栏(hub 同款):返回居左、Info 居右;.detail > * 规则天然把它限宽居中 ── */
|
|
276
|
+
.detail-topbar { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 18px; }
|
|
277
|
+
.topbar-btn { display: inline-flex; align-items: center; gap: 6px; border: 1px solid #e5e7eb; background: #fff; color: rgba(0, 51, 102, .6); border-radius: 8px; height: 32px; padding: 0 12px; font-size: 12px; font-weight: 500; font-family: inherit; cursor: pointer; transition: color .15s, border-color .15s, background .15s; }
|
|
278
|
+
.topbar-btn:hover { color: var(--brand); border-color: #d1d5db; background: #f9fafb; }
|
|
279
|
+
.topbar-icon { width: 32px; padding: 0; justify-content: center; }
|
|
280
|
+
/* 文档信息弹窗(hub InfoDialog 同构:白卡、居中、遮罩) */
|
|
281
|
+
#info-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, .4); z-index: 50; display: flex; align-items: center; justify-content: center; }
|
|
282
|
+
.info-card { width: 320px; max-width: calc(100vw - 32px); background: #fff; border-radius: 8px; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, .1); padding: 24px; }
|
|
283
|
+
.info-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
|
|
284
|
+
.info-title { font-size: 17px; font-weight: 700; color: var(--brand); }
|
|
285
|
+
.info-close { border: none; background: none; color: var(--brand-40); cursor: pointer; padding: 2px; display: flex; }
|
|
286
|
+
.info-close:hover { color: var(--brand); }
|
|
287
|
+
.info-row { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; font-size: 13px; color: var(--brand); }
|
|
288
|
+
.info-row .info-key { color: var(--brand-60); }
|
|
289
|
+
/* 哈希整行按钮(hub InfoDialog 同款):指纹图标+等宽哈希+复制图标,hover 沉到全色 */
|
|
290
|
+
.info-hash-btn { display: flex; align-items: center; gap: 6px; width: 100%; text-align: left; border: 1px solid #e5e7eb; background: #fff; border-radius: 8px; padding: 8px 10px; font-family: ui-monospace, "Cascadia Code", Consolas, monospace; font-size: 12px; color: rgba(0, 51, 102, .6); cursor: pointer; transition: color .15s, border-color .15s; }
|
|
291
|
+
.info-hash-btn:hover { color: var(--brand); border-color: #d1d5db; }
|
|
292
|
+
.info-hash-btn .info-hash { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
|
|
293
|
+
.info-hash-btn svg:last-child { opacity: .4; flex-shrink: 0; }
|