dsh-ide-git 0.13.0 → 0.13.1
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 +117 -24
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.1",
|
|
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.1",
|
|
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
|
@@ -27,6 +27,7 @@ window.__ModuleLoader__.load({
|
|
|
27
27
|
const useRef = React.useRef
|
|
28
28
|
const useCallback = React.useCallback
|
|
29
29
|
const useLayoutEffect = React.useLayoutEffect
|
|
30
|
+
const memo = React.memo
|
|
30
31
|
|
|
31
32
|
/* API base is MOUNT-RELATIVE (no leading slash). dsh 0.1.7 serves the
|
|
32
33
|
shell with <base href="./">, so document.baseURI is the mount the page
|
|
@@ -4233,6 +4234,52 @@ window.__ModuleLoader__.load({
|
|
|
4233
4234
|
return body.data
|
|
4234
4235
|
}
|
|
4235
4236
|
|
|
4237
|
+
/* ============================== first-paint cache ============================== */
|
|
4238
|
+
|
|
4239
|
+
/* Session-scoped seats (the conversation view, and every sidebar seat that
|
|
4240
|
+
follows the active session) REMOUNT their panel on each session switch:
|
|
4241
|
+
the slot subtree belongs to the session. Re-running the whole discovery
|
|
4242
|
+
(repos scan) + first page (summary / branches / log — three git spawns)
|
|
4243
|
+
on every switch made the chrome re-appear piece by piece (reported
|
|
4244
|
+
against v0.13.0). Same workspace ⇒ same repository ⇒ the previous paint
|
|
4245
|
+
is still essentially correct, so it seeds the states and a background
|
|
4246
|
+
refresh lands the fresh data a moment later. Pure functions; the panel
|
|
4247
|
+
only ever reads them at effect time and writes after a fetch. */
|
|
4248
|
+
const PAINT_TTL_MS = 30_000
|
|
4249
|
+
const paintRepos = new Map() /* cwd -> { at, data } */
|
|
4250
|
+
const paintPage = new Map() /* repoRoot -> { at, summary, branches, commits, hasMore } */
|
|
4251
|
+
const paintRoot = new Map() /* cwd -> last repoRoot the user was looking at */
|
|
4252
|
+
|
|
4253
|
+
function readPaintRepos(cwd) {
|
|
4254
|
+
const entry = paintRepos.get(cwd)
|
|
4255
|
+
if (entry === undefined || Date.now() - entry.at > PAINT_TTL_MS) return undefined
|
|
4256
|
+
return entry.data
|
|
4257
|
+
}
|
|
4258
|
+
|
|
4259
|
+
function writePaintRepos(cwd, data) {
|
|
4260
|
+
paintRepos.set(cwd, { at: Date.now(), data: data })
|
|
4261
|
+
if (paintRepos.size > 16) paintRepos.delete(paintRepos.keys().next().value)
|
|
4262
|
+
}
|
|
4263
|
+
|
|
4264
|
+
function readPaintPage(repoRoot) {
|
|
4265
|
+
const entry = paintPage.get(repoRoot)
|
|
4266
|
+
if (entry === undefined || Date.now() - entry.at > PAINT_TTL_MS) return undefined
|
|
4267
|
+
return entry
|
|
4268
|
+
}
|
|
4269
|
+
|
|
4270
|
+
function writePaintPage(repoRoot, page) {
|
|
4271
|
+
paintPage.set(repoRoot, Object.assign({ at: Date.now() }, page))
|
|
4272
|
+
if (paintPage.size > 16) paintPage.delete(paintPage.keys().next().value)
|
|
4273
|
+
}
|
|
4274
|
+
|
|
4275
|
+
function rememberPaintRoot(cwd, repoRoot) {
|
|
4276
|
+
if (typeof cwd === 'string' && cwd !== '' && repoRoot !== null) paintRoot.set(cwd, repoRoot)
|
|
4277
|
+
}
|
|
4278
|
+
|
|
4279
|
+
function readPaintRoot(cwd) {
|
|
4280
|
+
return paintRoot.get(cwd)
|
|
4281
|
+
}
|
|
4282
|
+
|
|
4236
4283
|
/* ============================== storage ============================== */
|
|
4237
4284
|
|
|
4238
4285
|
/* ---- ai commit pickers (issue #7): the model catalog and per-model
|
|
@@ -6798,18 +6845,30 @@ window.__ModuleLoader__.load({
|
|
|
6798
6845
|
as "still loading" and would spin forever). */
|
|
6799
6846
|
if (cwd === undefined) { setRepoState({ repos: [], isRepo: false }); return undefined }
|
|
6800
6847
|
let cancelled = false
|
|
6848
|
+
/* First-paint cache: a session switch through the same workspace
|
|
6849
|
+
reuses the previous discovery synchronously (repo bar and pick land
|
|
6850
|
+
with the first paint), then the background scan re-proves it. */
|
|
6851
|
+
const apply = (data, cachedPick) => {
|
|
6852
|
+
setRepoState(data)
|
|
6853
|
+
const paths = data.repos.map((repo) => repo.path)
|
|
6854
|
+
const remembered = readRememberedRepo(sessionId)
|
|
6855
|
+
let pick = null
|
|
6856
|
+
if (remembered !== undefined && paths.indexOf(remembered) >= 0) pick = remembered
|
|
6857
|
+
else if (cachedPick !== undefined && paths.indexOf(cachedPick) >= 0) pick = cachedPick
|
|
6858
|
+
else if (data.isRepo === true) pick = data.cwd
|
|
6859
|
+
else if (paths.length > 0) pick = paths[0]
|
|
6860
|
+
setRepoRoot(pick)
|
|
6861
|
+
rememberPaintRoot(cwd, pick)
|
|
6862
|
+
return pick
|
|
6863
|
+
}
|
|
6864
|
+
const cached = readPaintRepos(cwd)
|
|
6865
|
+
if (cached !== undefined) apply(cached, readPaintRoot(cwd))
|
|
6801
6866
|
void (async () => {
|
|
6802
6867
|
try {
|
|
6803
6868
|
const data = await request('repos', { cwd: cwd })
|
|
6804
6869
|
if (cancelled) return
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
const remembered = readRememberedRepo(sessionId)
|
|
6808
|
-
let pick = null
|
|
6809
|
-
if (remembered !== undefined && paths.indexOf(remembered) >= 0) pick = remembered
|
|
6810
|
-
else if (data.isRepo === true) pick = data.cwd
|
|
6811
|
-
else if (paths.length > 0) pick = paths[0]
|
|
6812
|
-
setRepoRoot(pick)
|
|
6870
|
+
writePaintRepos(cwd, data)
|
|
6871
|
+
apply(data, readPaintRoot(cwd))
|
|
6813
6872
|
} catch (caught) {
|
|
6814
6873
|
if (!cancelled) setError(caught instanceof Error ? caught.message : String(caught))
|
|
6815
6874
|
}
|
|
@@ -6824,7 +6883,9 @@ window.__ModuleLoader__.load({
|
|
|
6824
6883
|
}, [base, showIgnored])
|
|
6825
6884
|
|
|
6826
6885
|
const loadBranches = useCallback(async () => {
|
|
6827
|
-
|
|
6886
|
+
const data = await request('branches', base)
|
|
6887
|
+
setBranches(data)
|
|
6888
|
+
return data
|
|
6828
6889
|
}, [base])
|
|
6829
6890
|
|
|
6830
6891
|
const loadCommits = useCallback(async (skip, overridePath) => {
|
|
@@ -6834,19 +6895,39 @@ window.__ModuleLoader__.load({
|
|
|
6834
6895
|
const data = await request('log', payload)
|
|
6835
6896
|
setCommits((previous) => (skip === undefined || skip === 0 ? data.commits : previous.concat(data.commits)))
|
|
6836
6897
|
setHasMore(data.hasMore)
|
|
6898
|
+
return data
|
|
6837
6899
|
}, [base, pathFilter])
|
|
6838
6900
|
|
|
6839
6901
|
const refresh = useCallback(async () => {
|
|
6840
6902
|
if (repoRoot === null) return
|
|
6841
6903
|
await guard(async () => {
|
|
6842
|
-
await Promise.all([loadSummary(), loadBranches(), loadCommits(0)])
|
|
6904
|
+
const [summaryData, branchesData, page] = await Promise.all([loadSummary(), loadBranches(), loadCommits(0)])
|
|
6905
|
+
/* The write side of the first-paint cache: exactly the FIRST page
|
|
6906
|
+
(a paged fetch never reaches this path), replaced on every
|
|
6907
|
+
refresh — the auto-refresh included — so a session switch through
|
|
6908
|
+
the same workspace seeds fresh data, not the mount-time
|
|
6909
|
+
snapshot. */
|
|
6910
|
+
writePaintPage(repoRoot, { summary: summaryData, branches: branchesData, commits: page.commits, hasMore: page.hasMore })
|
|
6843
6911
|
})
|
|
6844
6912
|
}, [guard, loadSummary, loadBranches, loadCommits, repoRoot])
|
|
6845
6913
|
|
|
6846
6914
|
useEffect(() => {
|
|
6847
6915
|
if (repoRoot === null) return undefined
|
|
6916
|
+
/* First-paint cache: seed the first page synchronously so a session
|
|
6917
|
+
switch through the same workspace paints the full chrome (rail,
|
|
6918
|
+
branch bar, history) at once instead of re-growing it piece by
|
|
6919
|
+
piece; the refresh below re-proves every field right after. Only a
|
|
6920
|
+
cold mount seeds — a later effect run (tick / repo switch) must not
|
|
6921
|
+
roll live state back to a stale snapshot. */
|
|
6922
|
+
const cachedPage = readPaintPage(repoRoot)
|
|
6923
|
+
if (cachedPage !== undefined) {
|
|
6924
|
+
if (summary === null) setSummary(cachedPage.summary)
|
|
6925
|
+
if (branches === null) setBranches(cachedPage.branches)
|
|
6926
|
+
if (commits.length === 0) { setCommits(cachedPage.commits); setHasMore(cachedPage.hasMore) }
|
|
6927
|
+
}
|
|
6848
6928
|
void refresh()
|
|
6849
6929
|
return undefined
|
|
6930
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
6850
6931
|
}, [repoRoot, refresh, tick])
|
|
6851
6932
|
|
|
6852
6933
|
useEffect(() => {
|
|
@@ -7636,7 +7717,7 @@ window.__ModuleLoader__.load({
|
|
|
7636
7717
|
|
|
7637
7718
|
const CSS = [
|
|
7638
7719
|
'.dig-root{position:relative;display:flex;flex-direction:column;height:100%;min-height:0;background:transparent;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family,system-ui,sans-serif);font-size:13px;line-height:1.5;font-weight:600;overflow:hidden}',
|
|
7639
|
-
'.dig-topbar{display:flex;align-items:center;gap:6px;padding:4px 6px;flex:none;min-width:0;border-bottom:1px solid var(--dsw-alias-
|
|
7720
|
+
'.dig-topbar{display:flex;align-items:center;gap:6px;padding:4px 6px;flex:none;min-width:0;border-bottom:1px solid var(--dsw-alias-border-l2);overflow:hidden}',
|
|
7640
7721
|
'.dig-topbar-spacer{flex:1;min-width:4px}',
|
|
7641
7722
|
'.dig-busy{color:var(--dsw-alias-label-tertiary);flex:none;white-space:nowrap}',
|
|
7642
7723
|
'.dig-track{color:var(--dsw-alias-label-secondary);flex:none;font-variant-numeric:tabular-nums;white-space:nowrap}',
|
|
@@ -7659,10 +7740,10 @@ window.__ModuleLoader__.load({
|
|
|
7659
7740
|
'.dig-note{display:flex;align-items:center;gap:8px;padding:4px 8px;flex:none;color:var(--dsw-alias-label-secondary)}',
|
|
7660
7741
|
'.dig-banner-text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}',
|
|
7661
7742
|
'.dig-shell{flex:1;min-height:0;display:flex;overflow:hidden}',
|
|
7662
|
-
'.dig-rail{width:32px;flex:none;display:flex;flex-direction:column;align-items:center;gap:2px;padding:4px 0;overflow:hidden;border-right:1px solid var(--dsw-alias-
|
|
7743
|
+
'.dig-rail{width:32px;flex:none;display:flex;flex-direction:column;align-items:center;gap:2px;padding:4px 0;overflow:hidden;border-right:1px solid var(--dsw-alias-border-l2)}',
|
|
7663
7744
|
'.dig-rail-gap{flex:1;min-height:2px}',
|
|
7664
7745
|
'.dig-rail-gap-x{flex:1;min-width:2px}',
|
|
7665
|
-
'.dig-rail-row{display:flex;align-items:center;gap:2px;padding:3px 6px;flex:none;overflow:hidden;border-bottom:1px solid var(--dsw-alias-
|
|
7746
|
+
'.dig-rail-row{display:flex;align-items:center;gap:2px;padding:3px 6px;flex:none;overflow:hidden;border-bottom:1px solid var(--dsw-alias-border-l2)}',
|
|
7666
7747
|
'.dig-rail-btn{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border:none;border-radius:var(--dsw-radius-sm,8px);background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;padding:0;flex:none}',
|
|
7667
7748
|
'.dig-rail-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}',
|
|
7668
7749
|
'.dig-rail-btn:disabled{opacity:.28;cursor:default}',
|
|
@@ -7745,13 +7826,13 @@ window.__ModuleLoader__.load({
|
|
|
7745
7826
|
'.dig-body-stack{flex-direction:column}',
|
|
7746
7827
|
'.dig-body-compact{flex-direction:column}',
|
|
7747
7828
|
'.dig-pane{display:flex;flex-direction:column;min-height:0;min-width:0}',
|
|
7748
|
-
'.dig-pane-tree{width:200px;flex:none;border-right:1px solid var(--dsw-alias-
|
|
7749
|
-
'.dig-pane-tree-stack{max-height:36%;flex:none;border-bottom:1px solid var(--dsw-alias-
|
|
7829
|
+
'.dig-pane-tree{width:200px;flex:none;border-right:1px solid var(--dsw-alias-border-l2)}',
|
|
7830
|
+
'.dig-pane-tree-stack{max-height:36%;flex:none;border-bottom:1px solid var(--dsw-alias-border-l2)}',
|
|
7750
7831
|
'.dig-pane-main{flex:1;min-width:0}',
|
|
7751
|
-
'.dig-pane-changes{width:290px;flex:none;border-left:1px solid var(--dsw-alias-
|
|
7752
|
-
'.dig-pane-changes-stack{max-height:46%;flex:none;border-bottom:1px solid var(--dsw-alias-
|
|
7832
|
+
'.dig-pane-changes{width:290px;flex:none;border-left:1px solid var(--dsw-alias-border-l2)}',
|
|
7833
|
+
'.dig-pane-changes-stack{max-height:46%;flex:none;border-bottom:1px solid var(--dsw-alias-border-l2)}',
|
|
7753
7834
|
'.dig-compact-bar{display:flex;align-items:center;gap:6px;padding:4px 6px;flex:none}',
|
|
7754
|
-
'.dig-compact-tree{max-height:42%;flex:none;border-bottom:1px solid var(--dsw-alias-
|
|
7835
|
+
'.dig-compact-tree{max-height:42%;flex:none;border-bottom:1px solid var(--dsw-alias-border-l2)}',
|
|
7755
7836
|
// A divider: an 8px hit area (a fingertip needs it) painted as a 1px hairline
|
|
7756
7837
|
// that lights up on hover, focus or while dragging. touch-action:none is what
|
|
7757
7838
|
// makes a touch drag resize the pane instead of scrolling the panel.
|
|
@@ -7835,7 +7916,7 @@ window.__ModuleLoader__.load({
|
|
|
7835
7916
|
'.dig-ref-remote{color:#b083f0}',
|
|
7836
7917
|
'.dig-ref-tag{color:var(--dsw-alias-state-warn-primary,var(--dsw-alias-brand-primary))}',
|
|
7837
7918
|
'.dig-ref-more{color:var(--dsw-alias-label-tertiary)}',
|
|
7838
|
-
'.dig-filters{display:flex;align-items:center;gap:4px;padding:4px 6px;flex:none;min-width:0;overflow-x:auto;overflow-y:hidden;border-bottom:1px solid var(--dsw-alias-
|
|
7919
|
+
'.dig-filters{display:flex;align-items:center;gap:4px;padding:4px 6px;flex:none;min-width:0;overflow-x:auto;overflow-y:hidden;border-bottom:1px solid var(--dsw-alias-border-l2)}',
|
|
7839
7920
|
'.dig-filter-text{flex:1 1 90px;min-width:80px;width:auto}',
|
|
7840
7921
|
'.dig-filter-select{flex:none;max-width:118px;appearance:none;-webkit-appearance:none;background:transparent;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm,8px);color:var(--dsw-alias-label-secondary);font:inherit;font-weight:500;height:22px;padding:0 4px;cursor:pointer;text-overflow:ellipsis}',
|
|
7841
7922
|
'.dig-filter-select:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}',
|
|
@@ -7865,7 +7946,7 @@ window.__ModuleLoader__.load({
|
|
|
7865
7946
|
'.dig-mini{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border:none;border-radius:var(--dsw-radius-xs,4px);background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;opacity:0;flex:none;padding:0}',
|
|
7866
7947
|
'.dig-row:hover .dig-mini{opacity:1}',
|
|
7867
7948
|
'.dig-mini:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}',
|
|
7868
|
-
'.dig-commit-box{flex:none;padding:6px;display:flex;flex-direction:column;gap:6px;border-top:1px solid var(--dsw-alias-
|
|
7949
|
+
'.dig-commit-box{flex:none;padding:6px;display:flex;flex-direction:column;gap:6px;border-top:1px solid var(--dsw-alias-border-l2)}',
|
|
7869
7950
|
'.dig-commit-box-compact{flex-direction:row;align-items:center;gap:6px;padding:4px 6px;flex-wrap:wrap}',
|
|
7870
7951
|
'.dig-textarea{width:100%;box-sizing:border-box;min-height:50px;resize:vertical;padding:5px 6px;border-radius:var(--dsw-radius-sm,8px);border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;outline:none}',
|
|
7871
7952
|
'.dig-textarea::placeholder{color:var(--dsw-alias-label-tertiary)}',
|
|
@@ -7887,7 +7968,7 @@ window.__ModuleLoader__.load({
|
|
|
7887
7968
|
'.dig-detail-files-head{font-weight:600;margin-top:4px}',
|
|
7888
7969
|
'.dig-detail-files{display:flex;flex-direction:column}',
|
|
7889
7970
|
'.dig-mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace}',
|
|
7890
|
-
'.dig-diff-pane{flex:none;max-height:55%;display:flex;flex-direction:column;min-height:0;border-top:1px solid var(--dsw-alias-
|
|
7971
|
+
'.dig-diff-pane{flex:none;max-height:55%;display:flex;flex-direction:column;min-height:0;border-top:1px solid var(--dsw-alias-border-l2)}',
|
|
7891
7972
|
'.dig-diff-head{display:flex;align-items:center;gap:8px;padding:3px 8px;color:var(--dsw-alias-label-tertiary);flex:none;min-width:0}',
|
|
7892
7973
|
'.dig-diff-path{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}',
|
|
7893
7974
|
'.dig-diff{flex:1;overflow:auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.5;font-weight:500;min-height:0}',
|
|
@@ -7984,7 +8065,17 @@ window.__ModuleLoader__.load({
|
|
|
7984
8065
|
return undefined
|
|
7985
8066
|
}
|
|
7986
8067
|
|
|
7987
|
-
|
|
8068
|
+
/* The conversation shell re-renders on EVERY keystroke (the composer owns
|
|
8069
|
+
the draft state), and the view area re-renders with it. Without a bail-out
|
|
8070
|
+
at this boundary that cascade dragged the ENTIRE Git panel tree — branch
|
|
8071
|
+
rows, graph lanes, changes — through a synchronous render per keypress
|
|
8072
|
+
while the Git view was the active one (typing lag, reported against
|
|
8073
|
+
v0.13.0). The component consumes exactly three stable props (sessionId,
|
|
8074
|
+
t, ctx); everything else the slot passes (inspectCall, viewRequest, …)
|
|
8075
|
+
changes identity per shell render and is deliberately ignored, so the
|
|
8076
|
+
comparator is: same session, same bindings → skip the subtree. cwd moves
|
|
8077
|
+
through the sessions subscription inside, not through props. */
|
|
8078
|
+
const ConversationPanel = memo(function ConversationPanel(props) {
|
|
7988
8079
|
const ctx = props.ctx
|
|
7989
8080
|
const sessionId = typeof props.sessionId === 'string' ? props.sessionId : ''
|
|
7990
8081
|
const [cwd, setCwd] = useState(() => sessionCwdOf(ctx, sessionId))
|
|
@@ -7994,7 +8085,9 @@ window.__ModuleLoader__.load({
|
|
|
7994
8085
|
sync()
|
|
7995
8086
|
/* The session list is a live store; without a subscription a later
|
|
7996
8087
|
start (first message in a blank session) would leave the tab on the
|
|
7997
|
-
no-repo chrome until the next remount.
|
|
8088
|
+
no-repo chrome until the next remount. The notify walks are cheap:
|
|
8089
|
+
getSnapshot + a string compare, and setCwd with an identical string
|
|
8090
|
+
bails out without a render. */
|
|
7998
8091
|
let unsubscribe
|
|
7999
8092
|
try {
|
|
8000
8093
|
const sessions = ctx.get('sessions')
|
|
@@ -8010,7 +8103,7 @@ window.__ModuleLoader__.load({
|
|
|
8010
8103
|
}, [ctx, sessionId])
|
|
8011
8104
|
const scope = useMemo(() => ({ cwd: cwd, sessionId: sessionId }), [cwd, sessionId])
|
|
8012
8105
|
return E(LocaleLive, { ctx: ctx, scope: scope, t: props.t, visible: true })
|
|
8013
|
-
}
|
|
8106
|
+
}, (prev, next) => prev.sessionId === next.sessionId && prev.t === next.t && prev.ctx === next.ctx)
|
|
8014
8107
|
|
|
8015
8108
|
function NativePanel(props) {
|
|
8016
8109
|
const workspaces = typeof props.useWorkspaces === 'function'
|