@nimbalyst/extension-sdk 0.4.0 → 0.6.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/CHANGELOG.md +39 -0
- package/dist/__tests__/editorHostCapabilities.test.d.ts +2 -0
- package/dist/__tests__/editorHostCapabilities.test.d.ts.map +1 -0
- package/dist/__tests__/editorHostCapabilities.test.js +24 -0
- package/dist/__tests__/fileDirectoryTree.test.js +34 -0
- package/dist/__tests__/gitOperationLog.test.d.ts +2 -0
- package/dist/__tests__/gitOperationLog.test.d.ts.map +1 -0
- package/dist/__tests__/gitOperationLog.test.js +51 -0
- package/dist/__tests__/useCollaborativeEditor.offline.test.js +43 -1
- package/dist/agents/AgentProtocol.d.ts +20 -0
- package/dist/agents/AgentProtocol.d.ts.map +1 -1
- package/dist/agents/AgentProtocolHost.d.ts +1 -1
- package/dist/agents/AgentProtocolHost.d.ts.map +1 -1
- package/dist/createReadOnlyHost.d.ts +11 -1
- package/dist/createReadOnlyHost.d.ts.map +1 -1
- package/dist/createReadOnlyHost.js +3 -0
- package/dist/editorHostCapabilities.d.ts +31 -0
- package/dist/editorHostCapabilities.d.ts.map +1 -0
- package/dist/editorHostCapabilities.js +35 -0
- package/dist/externalsPlugin.d.ts.map +1 -1
- package/dist/externalsPlugin.js +1 -5
- package/dist/fileDirectoryTree.d.ts +9 -2
- package/dist/fileDirectoryTree.d.ts.map +1 -1
- package/dist/fileDirectoryTree.js +82 -14
- package/dist/gitOperationLog.d.ts +104 -0
- package/dist/gitOperationLog.d.ts.map +1 -0
- package/dist/gitOperationLog.js +88 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/types/collab.d.ts +31 -2
- package/dist/types/collab.d.ts.map +1 -1
- package/dist/types/comments.d.ts +104 -0
- package/dist/types/comments.d.ts.map +1 -0
- package/dist/types/comments.js +2 -0
- package/dist/types/editor.d.ts +201 -1
- package/dist/types/editor.d.ts.map +1 -1
- package/dist/types/extension.d.ts +49 -0
- package/dist/types/extension.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/panel.d.ts +21 -0
- package/dist/types/panel.d.ts.map +1 -1
- package/dist/useCollaborativeEditor.d.ts +13 -8
- package/dist/useCollaborativeEditor.d.ts.map +1 -1
- package/dist/useCollaborativeEditor.js +92 -74
- package/package.json +14 -2
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,8 @@ The SDK is versioned independently of the Nimbalyst app. Each release declares i
|
|
|
8
8
|
|
|
9
9
|
| SDK version | Minimum Nimbalyst app version |
|
|
10
10
|
| --- | --- |
|
|
11
|
+
| 0.6.0 | 0.78.0 |
|
|
12
|
+
| 0.5.0 | 0.70.0 |
|
|
11
13
|
| 0.4.0 | 0.70.0 |
|
|
12
14
|
| 0.3.0 | 0.70.0 |
|
|
13
15
|
| 0.2.2 | 0.58.5 |
|
|
@@ -18,6 +20,43 @@ The SDK is versioned independently of the Nimbalyst app. Each release declares i
|
|
|
18
20
|
|
|
19
21
|
## [Unreleased]
|
|
20
22
|
|
|
23
|
+
## [0.6.0] - 2026-09-14
|
|
24
|
+
|
|
25
|
+
Requires Nimbalyst 0.78.0, available on the alpha channel at publication, for the complete host API including native screenshots.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- `CollaborationCommentsService`, `CommentAnchor`, and codec-level `commentAnchors` let collaborative editors mount the platform comment experience and resolve structured anchors both live and headlessly.
|
|
30
|
+
- `host.registerViewport()` and `EditorViewport` let an editor expose proportional scroll position to hosts that carry the reader's place between related documents; `createReadOnlyHost()` can receive those registrations.
|
|
31
|
+
- `contributions.customEditors[].collaboration.documentType` lets a host identify a collaborative document type from the manifest before its extension bundle and codec are loaded.
|
|
32
|
+
- `screenshotService` exposes host-provided element and file screenshots, including unopened documents.
|
|
33
|
+
- `PanelHost.getWorkspaceFolders()` and `getPrimaryFolderPath()` expose multiple workspace roots; file-tree helpers accept those roots.
|
|
34
|
+
- `EditorHost.getAssetUrl()` and the `assetUrls` capability let media editors stream local files through a host-provided URL.
|
|
35
|
+
- The `agents`, `types/editor`, and `git-operation-log` entry points expose agent contracts, editor types, and Git operation selectors.
|
|
36
|
+
- `ProtocolSession.appliedModel` and `deliveredMcpServerCount` report the model and MCP servers actually used by an agent.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- `CollabCodec` documents file-form fallback for headless agent edits when no structured patch pair exists, while recommending structured edits for minimal concurrent deltas.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- Agent host declarations use a Node-compatible relative import for consumers using Node16 or NodeNext module resolution.
|
|
45
|
+
|
|
46
|
+
## [0.5.0]
|
|
47
|
+
|
|
48
|
+
Still runs on Nimbalyst 0.70.0: a host that declares no capabilities is treated as fully capable, so an extension written against this release keeps working on older desktop builds.
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- `host.capabilities` plus `editorHostSupports()` and `editorHostCapabilityGap()` let one editor bundle run in hosts that cannot honour every part of `EditorHost` — check a capability before calling instead of discovering the gap at runtime.
|
|
53
|
+
- `AiAgentProviderContribution` declares `supportsSlashCommands`, `supportsSkills`, and `compaction` instead of having the app infer them; all three default to off, so an unclaimed affordance is hidden rather than offered and silently broken.
|
|
54
|
+
- `AgentProtocol.compactSession()` is the optional real compaction entry point for a transport that has one.
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
|
|
58
|
+
- `host.filePath` is documented as an opaque document identifier — an absolute path on the desktop host, a synthetic URI on a host with no filesystem. Gate anything that touches disk on the `localFileSave` / `projectFileSystem` capabilities.
|
|
59
|
+
|
|
21
60
|
## [0.4.0]
|
|
22
61
|
|
|
23
62
|
### Added
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editorHostCapabilities.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/editorHostCapabilities.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { editorHostCapabilityGap, editorHostSupports, } from '../editorHostCapabilities.js';
|
|
4
|
+
const browserCapabilities = {
|
|
5
|
+
environment: 'browser',
|
|
6
|
+
unavailable: [{ capability: 'localFileSave', reason: 'No file to write.' }],
|
|
7
|
+
supports: (capability) => capability !== 'localFileSave',
|
|
8
|
+
};
|
|
9
|
+
describe('editorHostSupports', () => {
|
|
10
|
+
it('treats a host that makes no claim as fully capable', () => {
|
|
11
|
+
// The Electron renderer predates this contract and implements every
|
|
12
|
+
// member. Answering false there would make every portable extension
|
|
13
|
+
// silently drop features on desktop.
|
|
14
|
+
expect(editorHostSupports({}, 'localFileSave')).toBe(true);
|
|
15
|
+
expect(editorHostCapabilityGap({}, 'localFileSave')).toBeNull();
|
|
16
|
+
});
|
|
17
|
+
it('defers to a host that does declare capabilities', () => {
|
|
18
|
+
const host = { capabilities: browserCapabilities };
|
|
19
|
+
expect(editorHostSupports(host, 'collaboration')).toBe(true);
|
|
20
|
+
expect(editorHostSupports(host, 'localFileSave')).toBe(false);
|
|
21
|
+
expect(editorHostCapabilityGap(host, 'localFileSave')).toBe('No file to write.');
|
|
22
|
+
expect(editorHostCapabilityGap(host, 'collaboration')).toBeNull();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -56,4 +56,38 @@ describe('buildFileDirectoryTree', () => {
|
|
|
56
56
|
expect(tree.fileCount).toBe(1);
|
|
57
57
|
expect(tree.files).toHaveLength(1);
|
|
58
58
|
});
|
|
59
|
+
describe('multi-root workspaces', () => {
|
|
60
|
+
const ROOTS = ['/repo', '/other/collab'];
|
|
61
|
+
it('leaves primary-root paths bare and names the attached root', () => {
|
|
62
|
+
expect(getWorkspaceRelativeFilePath('/repo/src/index.ts', ROOTS)).toBe('src/index.ts');
|
|
63
|
+
expect(getWorkspaceRelativeFilePath('/other/collab/src/index.ts', ROOTS))
|
|
64
|
+
.toBe('collab/src/index.ts');
|
|
65
|
+
});
|
|
66
|
+
it('keeps same-named files in different roots in separate groups', () => {
|
|
67
|
+
const tree = buildTree(['/repo/src/index.ts', '/other/collab/src/index.ts'], ROOTS);
|
|
68
|
+
// `collab/src` is the collapsed single-child chain; the point is that the
|
|
69
|
+
// two `src` directories do not merge into one group.
|
|
70
|
+
expect(getFileDirectoryPaths(tree)).toEqual(['src', 'collab/src']);
|
|
71
|
+
expect(tree.subdirectories.get('src')?.fileCount).toBe(1);
|
|
72
|
+
expect(tree.subdirectories.get('collab')?.fileCount).toBe(1);
|
|
73
|
+
});
|
|
74
|
+
it('prefers the deepest root for a root nested inside another root', () => {
|
|
75
|
+
expect(getWorkspaceRelativeFilePath('/repo/vendor/lib/a.ts', ['/repo', '/repo/vendor/lib']))
|
|
76
|
+
.toBe('lib/a.ts');
|
|
77
|
+
});
|
|
78
|
+
it('falls back to the absolute path for a file in no root', () => {
|
|
79
|
+
expect(getWorkspaceRelativeFilePath('/elsewhere/a.ts', ROOTS)).toBe('/elsewhere/a.ts');
|
|
80
|
+
});
|
|
81
|
+
it('disambiguates attached roots that share a basename', () => {
|
|
82
|
+
// `/app/api` and `/infra/api` are an ordinary pair, and a bare basename
|
|
83
|
+
// would collapse both repos into one `api` group in the tree.
|
|
84
|
+
const roots = ['/repo', '/app/api', '/infra/api'];
|
|
85
|
+
expect(getWorkspaceRelativeFilePath('/app/api/src/a.ts', roots)).toBe('app/api/src/a.ts');
|
|
86
|
+
expect(getWorkspaceRelativeFilePath('/infra/api/src/a.ts', roots)).toBe('infra/api/src/a.ts');
|
|
87
|
+
});
|
|
88
|
+
it('matches single-root behaviour when given one root', () => {
|
|
89
|
+
expect(getWorkspaceRelativeFilePath('/repo/src/index.ts', ['/repo']))
|
|
90
|
+
.toBe(getWorkspaceRelativeFilePath('/repo/src/index.ts', '/repo'));
|
|
91
|
+
});
|
|
92
|
+
});
|
|
59
93
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitOperationLog.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/gitOperationLog.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { mergeGitOperationEntries, normalizeGitOperationEntry, selectLatestRunningGitOperation, selectLatestTerminalGitOperation, selectRunningGitOperations, } from '../gitOperationLog';
|
|
3
|
+
function entry(overrides) {
|
|
4
|
+
return {
|
|
5
|
+
timestamp: 1000,
|
|
6
|
+
updatedAt: 1000,
|
|
7
|
+
command: 'git status',
|
|
8
|
+
executable: 'git',
|
|
9
|
+
args: ['status'],
|
|
10
|
+
cwd: '/repo',
|
|
11
|
+
status: 'running',
|
|
12
|
+
output: '',
|
|
13
|
+
stdout: '',
|
|
14
|
+
stderr: '',
|
|
15
|
+
...overrides,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
describe('git operation log selectors', () => {
|
|
19
|
+
it('keeps a live terminal event that landed while hydration was in flight', () => {
|
|
20
|
+
// The `git:operation-log:get` response is older than upserts that arrived
|
|
21
|
+
// after it was requested. Applying it blindly rewinds a finished push back
|
|
22
|
+
// to running, which is exactly the stale indicator this ordering prevents.
|
|
23
|
+
const hydrated = [entry({ id: 'a', status: 'running', updatedAt: 1000 })];
|
|
24
|
+
const live = [entry({ id: 'a', status: 'success', updatedAt: 2000 })];
|
|
25
|
+
expect(mergeGitOperationEntries(hydrated, live)[0].status).toBe('success');
|
|
26
|
+
expect(mergeGitOperationEntries(live, hydrated)[0].status).toBe('success');
|
|
27
|
+
});
|
|
28
|
+
it('names the newest running command and counts the rest', () => {
|
|
29
|
+
const entries = [
|
|
30
|
+
entry({ id: 'push', timestamp: 1000, command: 'git push' }),
|
|
31
|
+
entry({ id: 'fetch', timestamp: 3000, command: 'git fetch origin' }),
|
|
32
|
+
entry({ id: 'done', timestamp: 2000, status: 'success', updatedAt: 5000 }),
|
|
33
|
+
];
|
|
34
|
+
expect(selectLatestRunningGitOperation(entries)?.id).toBe('fetch');
|
|
35
|
+
expect(selectRunningGitOperations(entries).map((e) => e.id)).toEqual(['push', 'fetch']);
|
|
36
|
+
expect(selectLatestTerminalGitOperation(entries)?.id).toBe('done');
|
|
37
|
+
});
|
|
38
|
+
it('reads a journal written before source metadata existed as app-owned direct git', () => {
|
|
39
|
+
const legacy = entry({ id: 'legacy' });
|
|
40
|
+
delete legacy.source;
|
|
41
|
+
delete legacy.executor;
|
|
42
|
+
const normalized = normalizeGitOperationEntry(legacy);
|
|
43
|
+
expect(normalized.source).toBe('nimbalyst');
|
|
44
|
+
expect(normalized.executor).toBe('git');
|
|
45
|
+
});
|
|
46
|
+
it('does not let an agent entry inherit the app-owned default', () => {
|
|
47
|
+
const agent = entry({ id: 'agent', source: 'agent', executor: 'shell', sessionId: 's1' });
|
|
48
|
+
expect(normalizeGitOperationEntry(agent).source).toBe('agent');
|
|
49
|
+
expect(normalizeGitOperationEntry(agent).executor).toBe('shell');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { renderHook, waitFor } from '@testing-library/react';
|
|
1
|
+
import { act, renderHook, waitFor } from '@testing-library/react';
|
|
2
2
|
import { Awareness } from 'y-protocols/awareness';
|
|
3
3
|
import { describe, expect, it, vi } from 'vitest';
|
|
4
4
|
import * as Y from 'yjs';
|
|
@@ -31,4 +31,46 @@ describe('useCollaborativeEditor offline replica binding', () => {
|
|
|
31
31
|
awareness.destroy();
|
|
32
32
|
yDoc.destroy();
|
|
33
33
|
});
|
|
34
|
+
it('does not start a second binding while the first async bind is unresolved', async () => {
|
|
35
|
+
const yDoc = new Y.Doc();
|
|
36
|
+
yDoc.getMap('nodes').set('node_root', 'persisted');
|
|
37
|
+
const awareness = new Awareness(yDoc);
|
|
38
|
+
let notifyStatusChange = () => { };
|
|
39
|
+
let finishBind = () => { };
|
|
40
|
+
const bindReady = new Promise((resolve) => {
|
|
41
|
+
finishBind = resolve;
|
|
42
|
+
});
|
|
43
|
+
const destroy = vi.fn();
|
|
44
|
+
const bind = vi.fn(async () => {
|
|
45
|
+
await bindReady;
|
|
46
|
+
return { destroy };
|
|
47
|
+
});
|
|
48
|
+
const host = {
|
|
49
|
+
collaboration: {
|
|
50
|
+
yDoc,
|
|
51
|
+
awareness,
|
|
52
|
+
user: { id: 'offline-user', name: 'Offline User', color: '#123456' },
|
|
53
|
+
getStatus: () => 'disconnected',
|
|
54
|
+
onStatusChange: (callback) => {
|
|
55
|
+
notifyStatusChange = callback;
|
|
56
|
+
return () => { };
|
|
57
|
+
},
|
|
58
|
+
loadInitialContent: vi.fn(async () => 'must not seed offline'),
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
const { result, unmount } = renderHook(() => useCollaborativeEditor(host, {
|
|
62
|
+
isEmpty: doc => doc.getMap('nodes').size === 0,
|
|
63
|
+
initializeFromContent: vi.fn(),
|
|
64
|
+
createBinding: bind,
|
|
65
|
+
}));
|
|
66
|
+
await waitFor(() => expect(bind).toHaveBeenCalledTimes(1));
|
|
67
|
+
act(() => notifyStatusChange());
|
|
68
|
+
expect(bind).toHaveBeenCalledTimes(1);
|
|
69
|
+
finishBind();
|
|
70
|
+
await waitFor(() => expect(result.current.binding).not.toBeNull());
|
|
71
|
+
unmount();
|
|
72
|
+
expect(destroy).toHaveBeenCalledTimes(1);
|
|
73
|
+
awareness.destroy();
|
|
74
|
+
yDoc.destroy();
|
|
75
|
+
});
|
|
34
76
|
});
|
|
@@ -129,6 +129,14 @@ export interface ProtocolSession {
|
|
|
129
129
|
platform: string;
|
|
130
130
|
/** Platform-specific session data (for internal use) */
|
|
131
131
|
raw?: RawProtocolSession;
|
|
132
|
+
/** Number of host-supplied MCP servers this protocol actually delivered. */
|
|
133
|
+
deliveredMcpServerCount?: number;
|
|
134
|
+
/**
|
|
135
|
+
* Model the agent is actually running, when the protocol can observe it.
|
|
136
|
+
* Absent means "the protocol has no say"; the host then reports the model it
|
|
137
|
+
* asked for. Never report a requested model a protocol knows was not applied.
|
|
138
|
+
*/
|
|
139
|
+
appliedModel?: string;
|
|
132
140
|
}
|
|
133
141
|
/**
|
|
134
142
|
* Event types emitted during message streaming
|
|
@@ -229,5 +237,17 @@ export interface AgentProtocol {
|
|
|
229
237
|
* @param session - Session to clean up
|
|
230
238
|
*/
|
|
231
239
|
cleanupSession(session: ProtocolSession): void;
|
|
240
|
+
/**
|
|
241
|
+
* Compact the session's context in-place, if the transport supports it.
|
|
242
|
+
*
|
|
243
|
+
* Optional: a transport that cannot compact simply omits this, and callers
|
|
244
|
+
* must check for it before offering the action. Do NOT emulate compaction by
|
|
245
|
+
* sending a `/compact` string as a user turn -- for transports without a real
|
|
246
|
+
* compaction RPC that reaches the model as literal prompt text and silently
|
|
247
|
+
* does nothing (#1252).
|
|
248
|
+
*
|
|
249
|
+
* @param session - Session whose context should be compacted
|
|
250
|
+
*/
|
|
251
|
+
compactSession?(session: ProtocolSession): Promise<void>;
|
|
232
252
|
}
|
|
233
253
|
//# sourceMappingURL=AgentProtocol.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentProtocol.d.ts","sourceRoot":"","sources":["../../src/agents/AgentProtocol.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"AgentProtocol.d.ts","sourceRoot":"","sources":["../../src/agents/AgentProtocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
* See `docs/EXTENSION_ARCHITECTURE.md` and the Phase 4 SDK design doc
|
|
40
40
|
* (Section 4.3) for the full host contract.
|
|
41
41
|
*/
|
|
42
|
-
import type { MCPServerConfig } from './index';
|
|
42
|
+
import type { MCPServerConfig } from './index.js';
|
|
43
43
|
/**
|
|
44
44
|
* Permission posture the host has set for the current turn.
|
|
45
45
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentProtocolHost.d.ts","sourceRoot":"","sources":["../../src/agents/AgentProtocolHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;
|
|
1
|
+
{"version":3,"file":"AgentProtocolHost.d.ts","sourceRoot":"","sources":["../../src/agents/AgentProtocolHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAGhC,uDAAuD;IACvD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B;;;OAGG;IACH,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IAExC;;;OAGG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAIjC,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,wDAAwD;IACxD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAE/B;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAElC,yDAAyD;IACzD,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IAExC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAC;IAEhD;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAI/C;;;;;;;;;;;OAWG;IACH,MAAM,CACJ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,GAAG,QAAQ,EAC7B,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;;;;;OAQG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAE3D;;;;;;;OAOG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAErD;;;;OAIG;IACH,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErE;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D"}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* host.setTheme('light');
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
|
-
import type { EditorHost } from './types/editor.js';
|
|
24
|
+
import type { EditorHost, EditorViewport } from './types/editor.js';
|
|
25
25
|
export interface ReadOnlyHostOptions {
|
|
26
26
|
/** Current theme name (e.g., 'dark', 'light') */
|
|
27
27
|
theme: string;
|
|
@@ -34,6 +34,16 @@ export interface ReadOnlyHostOptions {
|
|
|
34
34
|
* When true, extensions may suppress chrome that doesn't fit inline contexts.
|
|
35
35
|
*/
|
|
36
36
|
embedded?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Receives the editor's scroll viewport when it publishes one.
|
|
39
|
+
*
|
|
40
|
+
* Supplied only by a surface that shows several documents in sequence and
|
|
41
|
+
* wants to carry the reader's place between them -- the feedback detail
|
|
42
|
+
* popover comparing design alternatives. Absent, `registerViewport` is
|
|
43
|
+
* absent too, so an extension that checks before registering gets a straight
|
|
44
|
+
* answer rather than a silent no-op.
|
|
45
|
+
*/
|
|
46
|
+
onViewportRegistered?(viewport: EditorViewport | null): void;
|
|
37
47
|
}
|
|
38
48
|
export interface ReadOnlyHost extends EditorHost {
|
|
39
49
|
/** Update the theme (call from viewer chrome's theme toggle) */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createReadOnlyHost.d.ts","sourceRoot":"","sources":["../src/createReadOnlyHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"createReadOnlyHost.d.ts","sourceRoot":"","sources":["../src/createReadOnlyHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAEV,UAAU,EAEV,cAAc,EACf,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,mBAAmB;IAClC,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IAEd,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAC;IAEjB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;;;;;OAQG;IACH,oBAAoB,CAAC,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC;CAC9D;AAED,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC9C,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,mBAAmB,GACxB,YAAY,CAiEd"}
|
|
@@ -60,6 +60,9 @@ export function createReadOnlyHost(content, opts) {
|
|
|
60
60
|
openHistory: () => { },
|
|
61
61
|
registerMenuItems: (_items) => { },
|
|
62
62
|
registerEditorAPI: () => { },
|
|
63
|
+
...(opts.onViewportRegistered
|
|
64
|
+
? { registerViewport: opts.onViewportRegistered }
|
|
65
|
+
: {}),
|
|
63
66
|
setEditorContext: (_context) => { },
|
|
64
67
|
setEditorContextItems: () => { },
|
|
65
68
|
// -- Storage (in-memory, non-persistent) --
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading `EditorHost.capabilities`.
|
|
3
|
+
*
|
|
4
|
+
* One function so the "host makes no claim" case is decided once instead of in
|
|
5
|
+
* every extension. See {@link EditorHostCapabilities}.
|
|
6
|
+
*/
|
|
7
|
+
import type { EditorHost, EditorHostCapability } from './types/editor.js';
|
|
8
|
+
/**
|
|
9
|
+
* Whether `host` really provides `capability`.
|
|
10
|
+
*
|
|
11
|
+
* A host with no `capabilities` block is treated as fully capable. That is not
|
|
12
|
+
* an optimistic guess: the only such host is the Electron renderer, which
|
|
13
|
+
* predates this contract and does implement every member. Any host that cannot
|
|
14
|
+
* -- the browser collaborative host, and anything after it -- is required to
|
|
15
|
+
* populate `capabilities`, so "absent" and "incapable" never overlap.
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* if (editorHostSupports(host, 'localFileSave')) {
|
|
19
|
+
* await host.saveContent(serialize());
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function editorHostSupports(host: Pick<EditorHost, 'capabilities'>, capability: EditorHostCapability): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The host's stated reason for withholding `capability`, or null when it is
|
|
26
|
+
* available (or the host makes no claim). For diagnostics and degraded-mode
|
|
27
|
+
* copy -- never branch on the string itself, branch on
|
|
28
|
+
* {@link editorHostSupports}.
|
|
29
|
+
*/
|
|
30
|
+
export declare function editorHostCapabilityGap(host: Pick<EditorHost, 'capabilities'>, capability: EditorHostCapability): string | null;
|
|
31
|
+
//# sourceMappingURL=editorHostCapabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editorHostCapabilities.d.ts","sourceRoot":"","sources":["../src/editorHostCapabilities.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,EACtC,UAAU,EAAE,oBAAoB,GAC/B,OAAO,CAET;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,EACtC,UAAU,EAAE,oBAAoB,GAC/B,MAAM,GAAG,IAAI,CAIf"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading `EditorHost.capabilities`.
|
|
3
|
+
*
|
|
4
|
+
* One function so the "host makes no claim" case is decided once instead of in
|
|
5
|
+
* every extension. See {@link EditorHostCapabilities}.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Whether `host` really provides `capability`.
|
|
9
|
+
*
|
|
10
|
+
* A host with no `capabilities` block is treated as fully capable. That is not
|
|
11
|
+
* an optimistic guess: the only such host is the Electron renderer, which
|
|
12
|
+
* predates this contract and does implement every member. Any host that cannot
|
|
13
|
+
* -- the browser collaborative host, and anything after it -- is required to
|
|
14
|
+
* populate `capabilities`, so "absent" and "incapable" never overlap.
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* if (editorHostSupports(host, 'localFileSave')) {
|
|
18
|
+
* await host.saveContent(serialize());
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function editorHostSupports(host, capability) {
|
|
23
|
+
return host.capabilities ? host.capabilities.supports(capability) : true;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The host's stated reason for withholding `capability`, or null when it is
|
|
27
|
+
* available (or the host makes no claim). For diagnostics and degraded-mode
|
|
28
|
+
* copy -- never branch on the string itself, branch on
|
|
29
|
+
* {@link editorHostSupports}.
|
|
30
|
+
*/
|
|
31
|
+
export function editorHostCapabilityGap(host, capability) {
|
|
32
|
+
const gap = host.capabilities?.unavailable
|
|
33
|
+
.find((entry) => entry.capability === capability);
|
|
34
|
+
return gap ? gap.reason : null;
|
|
35
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"externalsPlugin.d.ts","sourceRoot":"","sources":["../src/externalsPlugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"externalsPlugin.d.ts","sourceRoot":"","sources":["../src/externalsPlugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAoDnC;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAyDjD"}
|
package/dist/externalsPlugin.js
CHANGED
|
@@ -45,7 +45,6 @@ const EXTERNALS_MAP = {
|
|
|
45
45
|
'@nimbalyst/editor-context': '@nimbalyst/editor-context',
|
|
46
46
|
'@nimbalyst/runtime/ui/icons/MaterialSymbol': '@nimbalyst/runtime/ui/icons/MaterialSymbol',
|
|
47
47
|
'@nimbalyst/screenshot-service': '@nimbalyst/screenshot-service',
|
|
48
|
-
'@nimbalyst/datamodel-platform-service': '@nimbalyst/datamodel-platform-service',
|
|
49
48
|
// Shared libraries
|
|
50
49
|
'pdfjs-dist': 'pdfjs-dist',
|
|
51
50
|
virtua: 'virtua',
|
|
@@ -230,7 +229,7 @@ function generateCommonExports(externalKey) {
|
|
|
230
229
|
}
|
|
231
230
|
else if (externalKey === '@nimbalyst/runtime') {
|
|
232
231
|
exports = [
|
|
233
|
-
'MaterialSymbol', 'useDocumentPath', 'useEditorLifecycle',
|
|
232
|
+
'MaterialSymbol', 'useDocumentPath', 'useEditorLifecycle', 'screenshotService',
|
|
234
233
|
'useCollaborativeEditor', 'COLLAB_INIT_ORIGIN',
|
|
235
234
|
'setTranscriptMarkdownContributions', 'clearTranscriptMarkdownContributions',
|
|
236
235
|
'MarkdownEditor', 'MonacoEditor', 'MonacoCodeEditor',
|
|
@@ -241,9 +240,6 @@ function generateCommonExports(externalKey) {
|
|
|
241
240
|
else if (externalKey === '@nimbalyst/screenshot-service') {
|
|
242
241
|
exports = ['screenshotService'];
|
|
243
242
|
}
|
|
244
|
-
else if (externalKey === '@nimbalyst/datamodel-platform-service') {
|
|
245
|
-
exports = ['DataModelPlatformServiceImpl', 'getInstance'];
|
|
246
|
-
}
|
|
247
243
|
else if (externalKey === 'pdfjs-dist') {
|
|
248
244
|
exports = ['getDocument', 'GlobalWorkerOptions', 'version'];
|
|
249
245
|
}
|
|
@@ -11,9 +11,16 @@ export declare function getFilePathBasename(filePath: string): string;
|
|
|
11
11
|
/**
|
|
12
12
|
* Return a forward-slash path relative to the workspace when the file is inside it.
|
|
13
13
|
* Windows drive paths are compared case-insensitively to match filesystem behavior.
|
|
14
|
+
*
|
|
15
|
+
* A multi-root workspace passes every root, primary first. The primary root
|
|
16
|
+
* still yields a bare relative path, so a single-folder workspace is unchanged;
|
|
17
|
+
* a file in an attached root is prefixed with that root's heading, which is
|
|
18
|
+
* what groups it under its own heading in the directory tree instead of
|
|
19
|
+
* spelling out an absolute path. Deepest matching root wins, so a root checked
|
|
20
|
+
* out inside another root keeps its own prefix.
|
|
14
21
|
*/
|
|
15
|
-
export declare function getWorkspaceRelativeFilePath(filePath: string, workspacePath?: string): string;
|
|
22
|
+
export declare function getWorkspaceRelativeFilePath(filePath: string, workspacePath?: string | string[]): string;
|
|
16
23
|
/** Build a collapsed directory tree for arbitrary file records. */
|
|
17
|
-
export declare function buildFileDirectoryTree<T>(files: T[], getFilePath: (file: T) => string, workspacePath?: string): FileDirectoryNode<T>;
|
|
24
|
+
export declare function buildFileDirectoryTree<T>(files: T[], getFilePath: (file: T) => string, workspacePath?: string | string[]): FileDirectoryNode<T>;
|
|
18
25
|
export declare function getFileDirectoryPaths<T>(node: FileDirectoryNode<T>): string[];
|
|
19
26
|
//# sourceMappingURL=fileDirectoryTree.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileDirectoryTree.d.ts","sourceRoot":"","sources":["../src/fileDirectoryTree.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG5D;
|
|
1
|
+
{"version":3,"file":"fileDirectoryTree.d.ts","sourceRoot":"","sources":["../src/fileDirectoryTree.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG5D;AAwDD;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAChC,MAAM,CAwBR;AA6BD,mEAAmE;AACnE,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,KAAK,EAAE,CAAC,EAAE,EACV,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,EAChC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAChC,iBAAiB,CAAC,CAAC,CAAC,CAwCtB;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,MAAM,EAAE,CAM7E"}
|
|
@@ -6,27 +6,95 @@ export function getFilePathBasename(filePath) {
|
|
|
6
6
|
const normalizedPath = normalizeFilePath(filePath).replace(/\/+$/, '');
|
|
7
7
|
return normalizedPath.slice(normalizedPath.lastIndexOf('/') + 1);
|
|
8
8
|
}
|
|
9
|
+
/** Relative to one root, or null when the file is not inside it. */
|
|
10
|
+
function relativeToRoot(normalizedFile, rootPath) {
|
|
11
|
+
const normalizedRoot = normalizeFilePath(rootPath).replace(/\/$/, '');
|
|
12
|
+
if (!normalizedRoot)
|
|
13
|
+
return null;
|
|
14
|
+
const windowsDrivePath = /^[A-Za-z]:\//.test(normalizedFile)
|
|
15
|
+
&& /^[A-Za-z]:\//.test(normalizedRoot);
|
|
16
|
+
const comparableFile = windowsDrivePath ? normalizedFile.toLowerCase() : normalizedFile;
|
|
17
|
+
const comparableRoot = windowsDrivePath ? normalizedRoot.toLowerCase() : normalizedRoot;
|
|
18
|
+
if (comparableFile === comparableRoot)
|
|
19
|
+
return '';
|
|
20
|
+
if (comparableFile.startsWith(`${comparableRoot}/`)) {
|
|
21
|
+
return normalizedFile.slice(normalizedRoot.length + 1);
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Heading for each attached root, unique within the root list.
|
|
27
|
+
*
|
|
28
|
+
* Two roots sharing a basename is ordinary -- `app/api` and `infra/api`, or two
|
|
29
|
+
* checkouts of the same repo -- and a bare basename would collapse both into
|
|
30
|
+
* one directory group. Colliding roots grow parent segments until they differ;
|
|
31
|
+
* everything else stays the bare folder name. The primary root has no heading,
|
|
32
|
+
* so it never competes.
|
|
33
|
+
*/
|
|
34
|
+
function attachedRootLabels(roots) {
|
|
35
|
+
const segmentsPerRoot = roots.map((rootPath) => normalizeFilePath(rootPath).replace(/\/+$/, '').split('/').filter(Boolean));
|
|
36
|
+
const labels = roots.map((_, index) => segmentsPerRoot[index].slice(-1).join('/'));
|
|
37
|
+
// Grow every still-colliding label by one more parent segment per pass. A
|
|
38
|
+
// root can only run out of segments once it is the filesystem root, at which
|
|
39
|
+
// point its label is already unique.
|
|
40
|
+
for (let depth = 2; depth <= 16; depth++) {
|
|
41
|
+
const counts = new Map();
|
|
42
|
+
for (let index = 1; index < labels.length; index++) {
|
|
43
|
+
counts.set(labels[index], (counts.get(labels[index]) ?? 0) + 1);
|
|
44
|
+
}
|
|
45
|
+
if ([...counts.values()].every((count) => count < 2))
|
|
46
|
+
break;
|
|
47
|
+
let grew = false;
|
|
48
|
+
for (let index = 1; index < labels.length; index++) {
|
|
49
|
+
if ((counts.get(labels[index]) ?? 0) < 2)
|
|
50
|
+
continue;
|
|
51
|
+
if (segmentsPerRoot[index].length < depth)
|
|
52
|
+
continue;
|
|
53
|
+
labels[index] = segmentsPerRoot[index].slice(-depth).join('/');
|
|
54
|
+
grew = true;
|
|
55
|
+
}
|
|
56
|
+
if (!grew)
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
return labels;
|
|
60
|
+
}
|
|
9
61
|
/**
|
|
10
62
|
* Return a forward-slash path relative to the workspace when the file is inside it.
|
|
11
63
|
* Windows drive paths are compared case-insensitively to match filesystem behavior.
|
|
64
|
+
*
|
|
65
|
+
* A multi-root workspace passes every root, primary first. The primary root
|
|
66
|
+
* still yields a bare relative path, so a single-folder workspace is unchanged;
|
|
67
|
+
* a file in an attached root is prefixed with that root's heading, which is
|
|
68
|
+
* what groups it under its own heading in the directory tree instead of
|
|
69
|
+
* spelling out an absolute path. Deepest matching root wins, so a root checked
|
|
70
|
+
* out inside another root keeps its own prefix.
|
|
12
71
|
*/
|
|
13
72
|
export function getWorkspaceRelativeFilePath(filePath, workspacePath) {
|
|
14
73
|
const normalizedFile = normalizeFilePath(filePath);
|
|
15
|
-
|
|
74
|
+
const roots = typeof workspacePath === 'string' ? [workspacePath] : workspacePath;
|
|
75
|
+
if (!roots || roots.length === 0)
|
|
16
76
|
return normalizedFile;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
77
|
+
let bestIndex = -1;
|
|
78
|
+
let bestRelative = null;
|
|
79
|
+
let bestRootLength = -1;
|
|
80
|
+
roots.forEach((rootPath, index) => {
|
|
81
|
+
const relative = relativeToRoot(normalizedFile, rootPath);
|
|
82
|
+
if (relative === null)
|
|
83
|
+
return;
|
|
84
|
+
const rootLength = normalizeFilePath(rootPath).replace(/\/$/, '').length;
|
|
85
|
+
if (rootLength <= bestRootLength)
|
|
86
|
+
return;
|
|
87
|
+
bestIndex = index;
|
|
88
|
+
bestRelative = relative;
|
|
89
|
+
bestRootLength = rootLength;
|
|
90
|
+
});
|
|
91
|
+
if (bestRelative === null)
|
|
92
|
+
return normalizedFile;
|
|
93
|
+
if (bestIndex === 0)
|
|
94
|
+
return bestRelative;
|
|
95
|
+
// Attached root: name it, so two files called `src/index.ts` in two repos do
|
|
96
|
+
// not collapse into one directory group.
|
|
97
|
+
return `${attachedRootLabels(roots)[bestIndex]}/${bestRelative}`;
|
|
30
98
|
}
|
|
31
99
|
function collapseDirectoryTree(node) {
|
|
32
100
|
node.subdirectories.forEach((subdirectory, key) => {
|