dsh-ide-git 0.13.1 → 0.13.2
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/dsh.plugin.json +1 -1
- package/package.json +1 -1
- package/src/client.js +119 -7
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-ide-git",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"description": "IDE 级 Git 操作面板:分支树 + 提交图谱 + 变更/暂存/提交 + 提交详情。装了 dsh-better-sidebar 时挂成它的原生 tab(右侧栏 + 底部工作台);没装时直接挂 DSH 原生右侧栏,单独安装即可用。JetBrains 风格 Git 工具窗口 for DeepSeek Harness.",
|
|
6
6
|
"engines": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-ide-git",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"description": "DSH plugin: an IDE-grade Git tool window as a native dsh-better-sidebar tab — branch tree, commit graph, changes, commit details, JetBrains-style actions, in the right sidebar and the bottom panel. | DSH 插件:IDE 级 Git 工具窗口,以 dsh-better-sidebar 原生 Tab 挂载——分支树 / 提交图谱 / 变更与提交详情 / JetBrains 风格操作,右侧栏与底部面板双布局自适应。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
package/src/client.js
CHANGED
|
@@ -28,6 +28,12 @@ window.__ModuleLoader__.load({
|
|
|
28
28
|
const useCallback = React.useCallback
|
|
29
29
|
const useLayoutEffect = React.useLayoutEffect
|
|
30
30
|
const memo = React.memo
|
|
31
|
+
/* Measure-and-derive effects run here for the layout timing (the chrome and
|
|
32
|
+
the rail capacity derive from these numbers — a paint-then-correct cycle
|
|
33
|
+
read as buttons lighting up one by one on every seat switch). Environments
|
|
34
|
+
without a frame loop (the SSR self-check's shims) get the plain effect —
|
|
35
|
+
there is no paint to sync with there, and no server warning either. */
|
|
36
|
+
const useMeasureLayoutEffect = typeof requestAnimationFrame === 'function' ? useLayoutEffect : useEffect
|
|
31
37
|
|
|
32
38
|
/* API base is MOUNT-RELATIVE (no leading slash). dsh 0.1.7 serves the
|
|
33
39
|
shell with <base href="./">, so document.baseURI is the mount the page
|
|
@@ -4282,6 +4288,38 @@ window.__ModuleLoader__.load({
|
|
|
4282
4288
|
|
|
4283
4289
|
/* ============================== storage ============================== */
|
|
4284
4290
|
|
|
4291
|
+
/* ---- tree UI state (v0.13.1): the session-scoped seats remount on every
|
|
4292
|
+
session switch, and component state died with them — branch sections
|
|
4293
|
+
snapped back to defaults and folders re-folded. One localStorage home,
|
|
4294
|
+
GLOBAL by design: which sections you keep folded is a habit, not a
|
|
4295
|
+
property of the repository. ---- */
|
|
4296
|
+
const TREE_KEY = 'dsh-ide-git.tree.v1'
|
|
4297
|
+
|
|
4298
|
+
function readTreeState() {
|
|
4299
|
+
try {
|
|
4300
|
+
const raw = window.localStorage.getItem(TREE_KEY)
|
|
4301
|
+
if (raw === null || raw === '') return null
|
|
4302
|
+
const parsed = JSON.parse(raw)
|
|
4303
|
+
return parsed !== null && typeof parsed === 'object' ? parsed : null
|
|
4304
|
+
} catch (error) { void error }
|
|
4305
|
+
return null
|
|
4306
|
+
}
|
|
4307
|
+
|
|
4308
|
+
function writeTreeState(state) {
|
|
4309
|
+
try { window.localStorage.setItem(TREE_KEY, JSON.stringify(state)) } catch (error) { void error }
|
|
4310
|
+
}
|
|
4311
|
+
|
|
4312
|
+
function treeSectionDefaults() {
|
|
4313
|
+
return { favorites: false, remote: false, tags: true }
|
|
4314
|
+
}
|
|
4315
|
+
|
|
4316
|
+
function treeSectionsOf(stored) {
|
|
4317
|
+
const sections = stored !== null && typeof stored === 'object' && stored.sections !== null && typeof stored.sections === 'object'
|
|
4318
|
+
? stored.sections
|
|
4319
|
+
: {}
|
|
4320
|
+
return Object.assign(treeSectionDefaults(), sections)
|
|
4321
|
+
}
|
|
4322
|
+
|
|
4285
4323
|
/* ---- ai commit pickers (issue #7): the model catalog and per-model
|
|
4286
4324
|
reasoning efforts, served by the host's commit-models / commit-efforts
|
|
4287
4325
|
routes. Cached for a short window so the settings card can mount twice
|
|
@@ -6022,9 +6060,23 @@ window.__ModuleLoader__.load({
|
|
|
6022
6060
|
function BranchTree(props) {
|
|
6023
6061
|
const t = props.t
|
|
6024
6062
|
const [filter, setFilter] = useState('')
|
|
6025
|
-
const [collapsed, setCollapsed] = useState(
|
|
6063
|
+
const [collapsed, setCollapsed] = useState(() => treeSectionsOf(readTreeState()))
|
|
6026
6064
|
const [picked, setPicked] = useState('')
|
|
6027
|
-
const [foldedFolders, setFoldedFolders] = useState({
|
|
6065
|
+
const [foldedFolders, setFoldedFolders] = useState(() => {
|
|
6066
|
+
const stored = readTreeState()
|
|
6067
|
+
return stored !== null && typeof stored === 'object' && stored.folders !== null && typeof stored.folders === 'object'
|
|
6068
|
+
? stored.folders
|
|
6069
|
+
: {}
|
|
6070
|
+
})
|
|
6071
|
+
/* Survive the remount a session switch performs (v0.13.1). */
|
|
6072
|
+
useEffect(() => {
|
|
6073
|
+
const stored = readTreeState()
|
|
6074
|
+
writeTreeState(Object.assign({}, stored, { sections: collapsed }))
|
|
6075
|
+
}, [collapsed])
|
|
6076
|
+
useEffect(() => {
|
|
6077
|
+
const stored = readTreeState()
|
|
6078
|
+
writeTreeState(Object.assign({}, stored, { folders: foldedFolders }))
|
|
6079
|
+
}, [foldedFolders])
|
|
6028
6080
|
const branches = props.branches
|
|
6029
6081
|
const needle = filter.trim().toLowerCase()
|
|
6030
6082
|
const match = (name) => needle === '' || String(name).toLowerCase().indexOf(needle) >= 0
|
|
@@ -6140,7 +6192,15 @@ window.__ModuleLoader__.load({
|
|
|
6140
6192
|
const summary = props.summary
|
|
6141
6193
|
const [message, setMessage] = useState('')
|
|
6142
6194
|
const [amend, setAmend] = useState(false)
|
|
6143
|
-
const [collapsed, setCollapsed] = useState(
|
|
6195
|
+
const [collapsed, setCollapsed] = useState(() => {
|
|
6196
|
+
const stored = readTreeState()
|
|
6197
|
+
return stored !== null && typeof stored === 'object' && stored.changesCollapsed === true
|
|
6198
|
+
})
|
|
6199
|
+
/* Same remount-survival as the branch sections (v0.13.1). */
|
|
6200
|
+
useEffect(() => {
|
|
6201
|
+
const stored = readTreeState()
|
|
6202
|
+
writeTreeState(Object.assign({}, stored, { changesCollapsed: collapsed === true }))
|
|
6203
|
+
}, [collapsed])
|
|
6144
6204
|
/* AI-written message (issue #6). The draft is kept so the write can be
|
|
6145
6205
|
undone, and an existing draft is never overwritten without a word. */
|
|
6146
6206
|
const [aiBusy, setAiBusy] = useState(false)
|
|
@@ -6620,7 +6680,12 @@ window.__ModuleLoader__.load({
|
|
|
6620
6680
|
const sessionId = typeof scope.sessionId === 'string' ? scope.sessionId : 'default'
|
|
6621
6681
|
const base = useMemo(() => ({ cwd: cwd, repoRoot: repoRoot === null ? undefined : repoRoot }), [cwd, repoRoot])
|
|
6622
6682
|
|
|
6623
|
-
|
|
6683
|
+
/* Layout effect ON PURPOSE: the chrome (compact/columns/stack) and the
|
|
6684
|
+
rail capacity derive from these numbers, and a paint-then-correct
|
|
6685
|
+
cycle showed up on every seat switch as buttons lighting up one by
|
|
6686
|
+
one (v0.13.1 report). Measuring in the commit phase keeps the first
|
|
6687
|
+
paint on the final chrome. */
|
|
6688
|
+
useMeasureLayoutEffect(() => {
|
|
6624
6689
|
const element = hostRef.current
|
|
6625
6690
|
if (element === null) return undefined
|
|
6626
6691
|
const measure = () => {
|
|
@@ -6664,7 +6729,7 @@ window.__ModuleLoader__.load({
|
|
|
6664
6729
|
// Like the rail, the panes measure the box that actually holds them instead
|
|
6665
6730
|
// of trusting the root probe: the column chrome keeps a vertical rail
|
|
6666
6731
|
// beside the body, and the header sits above it.
|
|
6667
|
-
|
|
6732
|
+
useMeasureLayoutEffect(() => {
|
|
6668
6733
|
const element = bodyRef.current
|
|
6669
6734
|
if (element === null) { setBodyBox({ width: 0, height: 0 }); return undefined }
|
|
6670
6735
|
const measure = () => {
|
|
@@ -6802,7 +6867,9 @@ window.__ModuleLoader__.load({
|
|
|
6802
6867
|
// function of the strip that holds them (a 1200x300 workbench and a 300x900
|
|
6803
6868
|
// sidebar disagree about that). railBox keeps the last measured span; until
|
|
6804
6869
|
// the first measurement lands, every action is rendered.
|
|
6805
|
-
|
|
6870
|
+
// Layout effect (see the host measure above): the button count derives
|
|
6871
|
+
// from this box, and a late measure reads as the rail lighting up late.
|
|
6872
|
+
useMeasureLayoutEffect(() => {
|
|
6806
6873
|
const element = railHostRef.current
|
|
6807
6874
|
if (element === null) { setRailBox({ width: 0, height: 0 }); return undefined }
|
|
6808
6875
|
const measure = () => {
|
|
@@ -8079,6 +8146,47 @@ window.__ModuleLoader__.load({
|
|
|
8079
8146
|
const ctx = props.ctx
|
|
8080
8147
|
const sessionId = typeof props.sessionId === 'string' ? props.sessionId : ''
|
|
8081
8148
|
const [cwd, setCwd] = useState(() => sessionCwdOf(ctx, sessionId))
|
|
8149
|
+
/* The active conversation's view area is `flex: 1 0 auto; min-height:
|
|
8150
|
+
auto` — it GROWS with content and scrolls inside the shared
|
|
8151
|
+
conversation scrollport. An auto-height panel there measured
|
|
8152
|
+
content-tall (a 120-commit history, a long diff), so the width-vs-
|
|
8153
|
+
height chrome test flipped the wide seat into the stacked sidebar
|
|
8154
|
+
layout and everything shifted downward (v0.13.1 report). Pin the seat
|
|
8155
|
+
to the scrollport's VISIBLE height instead: the panel fills exactly
|
|
8156
|
+
the on-screen area and scrolls internally, the way the sidebar seats
|
|
8157
|
+
behave. */
|
|
8158
|
+
const seatRef = useRef(null)
|
|
8159
|
+
const [seatHeight, setSeatHeight] = useState(null)
|
|
8160
|
+
useEffect(() => {
|
|
8161
|
+
const el = seatRef.current
|
|
8162
|
+
if (el === null) return undefined
|
|
8163
|
+
const measure = () => {
|
|
8164
|
+
let scroll = null
|
|
8165
|
+
try { scroll = el.closest('[data-conversation-scroll]') } catch (error) { void error }
|
|
8166
|
+
if (scroll === null || scroll === undefined) return
|
|
8167
|
+
const offset = Math.max(0, el.getBoundingClientRect().top - scroll.getBoundingClientRect().top)
|
|
8168
|
+
const available = Math.round(scroll.clientHeight - offset - 8)
|
|
8169
|
+
if (available >= 240) setSeatHeight(available)
|
|
8170
|
+
}
|
|
8171
|
+
measure()
|
|
8172
|
+
const raf = requestAnimationFrame(() => { measure() })
|
|
8173
|
+
const late = setTimeout(() => { measure() }, 400)
|
|
8174
|
+
let observer
|
|
8175
|
+
try {
|
|
8176
|
+
const scroll = el.closest('[data-conversation-scroll]')
|
|
8177
|
+
if (scroll !== null && scroll !== undefined && typeof ResizeObserver === 'function') {
|
|
8178
|
+
observer = new ResizeObserver(() => { measure() })
|
|
8179
|
+
observer.observe(scroll)
|
|
8180
|
+
}
|
|
8181
|
+
} catch (error) { void error }
|
|
8182
|
+
window.addEventListener('resize', measure)
|
|
8183
|
+
return () => {
|
|
8184
|
+
cancelAnimationFrame(raf)
|
|
8185
|
+
clearTimeout(late)
|
|
8186
|
+
if (observer !== undefined) observer.disconnect()
|
|
8187
|
+
window.removeEventListener('resize', measure)
|
|
8188
|
+
}
|
|
8189
|
+
}, [])
|
|
8082
8190
|
useEffect(() => {
|
|
8083
8191
|
let alive = true
|
|
8084
8192
|
const sync = () => { if (alive === true) setCwd(sessionCwdOf(ctx, sessionId)) }
|
|
@@ -8102,7 +8210,11 @@ window.__ModuleLoader__.load({
|
|
|
8102
8210
|
}
|
|
8103
8211
|
}, [ctx, sessionId])
|
|
8104
8212
|
const scope = useMemo(() => ({ cwd: cwd, sessionId: sessionId }), [cwd, sessionId])
|
|
8105
|
-
return E(
|
|
8213
|
+
return E('div', {
|
|
8214
|
+
ref: seatRef,
|
|
8215
|
+
className: 'dig-conversation-seat',
|
|
8216
|
+
style: seatHeight === null ? undefined : { height: seatHeight + 'px' },
|
|
8217
|
+
}, E(LocaleLive, { ctx: ctx, scope: scope, t: props.t, visible: true }))
|
|
8106
8218
|
}, (prev, next) => prev.sessionId === next.sessionId && prev.t === next.t && prev.ctx === next.ctx)
|
|
8107
8219
|
|
|
8108
8220
|
function NativePanel(props) {
|