@sidequest-007/dsh-atlas 1.0.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 +269 -0
- package/CONTRIBUTING.md +50 -0
- package/LICENSE +21 -0
- package/LICENSE-ORIGINAL +21 -0
- package/NOTICE +29 -0
- package/README.md +297 -0
- package/README.zh.md +289 -0
- package/assets/diagrams/atlas-overview.svg +53 -0
- package/assets/diagrams/atlas-seam.svg +32 -0
- package/assets/screenshots/file-mention-composer.png +0 -0
- package/assets/screenshots/file-mention-settings.png +0 -0
- package/assets/screenshots/menu-mixed.png +0 -0
- package/cordis.patch.yml +14 -0
- package/dsh.plugin.json +12 -0
- package/lib/client.js +20615 -0
- package/lib/index.js +17458 -0
- package/lib/invariant.js +13 -0
- package/lib/types/abort.d.ts +22 -0
- package/lib/types/atlas.d.ts +181 -0
- package/lib/types/client/DraftLinks.d.ts +21 -0
- package/lib/types/client/FilesDock.d.ts +75 -0
- package/lib/types/client/FolderPicker.d.ts +66 -0
- package/lib/types/client/FolderTab.d.ts +50 -0
- package/lib/types/client/MentionNavigator.d.ts +96 -0
- package/lib/types/client/MenuIcons.d.ts +16 -0
- package/lib/types/client/ReferenceLinks.d.ts +85 -0
- package/lib/types/client/SettingsSection.d.ts +31 -0
- package/lib/types/client/draft-links.d.ts +205 -0
- package/lib/types/client/draft-scope.d.ts +22 -0
- package/lib/types/client/git-provider.d.ts +45 -0
- package/lib/types/client/icons.d.ts +48 -0
- package/lib/types/client/index.d.ts +8 -0
- package/lib/types/client/locales.d.ts +267 -0
- package/lib/types/client/model.d.ts +38 -0
- package/lib/types/client/reference-links.d.ts +100 -0
- package/lib/types/client/remote.d.ts +54 -0
- package/lib/types/client/search.d.ts +9 -0
- package/lib/types/client/source.d.ts +325 -0
- package/lib/types/client/styles.d.ts +15 -0
- package/lib/types/contract.d.ts +512 -0
- package/lib/types/defaults.d.ts +115 -0
- package/lib/types/external.d.ts +58 -0
- package/lib/types/files.d.ts +76 -0
- package/lib/types/git.d.ts +44 -0
- package/lib/types/index.d.ts +88 -0
- package/lib/types/invariant.d.ts +15 -0
- package/lib/types/mention.d.ts +80 -0
- package/lib/types/paste.d.ts +13 -0
- package/lib/types/reference.d.ts +16 -0
- package/lib/types/references.d.ts +126 -0
- package/lib/types/runtime-info.d.ts +30 -0
- package/lib/types/runtime.d.ts +180 -0
- package/lib/types/settings.d.ts +25 -0
- package/lib/types/tokens.d.ts +35 -0
- package/lib/types/tools.d.ts +94 -0
- package/lib/types/typert.d.ts +13 -0
- package/lib/types/types.d.ts +12 -0
- package/package.json +208 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two pure pieces of the sent-message click-through: decoding one rendered
|
|
3
|
+
* reference chip back into the action it can offer, and spelling the file
|
|
4
|
+
* address the right Sidebar resolves.
|
|
5
|
+
*
|
|
6
|
+
* Both are pure string work so the browser bridge (`ReferenceLinks.tsx`) stays a
|
|
7
|
+
* thin event adapter, and so the address grammar can be pinned against the
|
|
8
|
+
* Harness's own implementation in the tests instead of being trusted by eye.
|
|
9
|
+
*/
|
|
10
|
+
/** One mention of an already sent message that a click can act on. */
|
|
11
|
+
export type ReferenceLink = {
|
|
12
|
+
readonly kind: 'file';
|
|
13
|
+
readonly path: string;
|
|
14
|
+
}
|
|
15
|
+
/** A directory: no in-product viewer, so the Host opener (file manager) gets it. */
|
|
16
|
+
| {
|
|
17
|
+
readonly kind: 'folder';
|
|
18
|
+
readonly path: string;
|
|
19
|
+
} | {
|
|
20
|
+
readonly kind: 'skill';
|
|
21
|
+
readonly name: string;
|
|
22
|
+
}
|
|
23
|
+
/** A seam provider's item; only that provider's own `open` knows what it means. */
|
|
24
|
+
| {
|
|
25
|
+
readonly kind: 'atlas';
|
|
26
|
+
readonly provider: string;
|
|
27
|
+
readonly item: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Decode one reference chip's raw label (its `title`) into the action it offers.
|
|
31
|
+
*
|
|
32
|
+
* The chip's own `title` is the label the framework decorated, verbatim:
|
|
33
|
+
* `@path/to/file.ts`, `@"path with spaces.md"`, `@skill:name`, `@plugin:name`,
|
|
34
|
+
* `@atlas:provider/item`, `@[label](dsh-session:…)`, or an out-of-workspace
|
|
35
|
+
* absolute path (`@E:\…` / `@E:/…`, whose drive letter is a path, not a handle).
|
|
36
|
+
* Only a workspace file, a quoted path, an absolute path, a skill (whose source
|
|
37
|
+
* the skill source opens) and a provider item (whose meaning belongs to that
|
|
38
|
+
* provider) name something to act on. A trailing separator is a folder mention
|
|
39
|
+
* (its chip kind is `folder`), and a colon in the first segment is our own handle
|
|
40
|
+
* spelling unless it is a drive letter.
|
|
41
|
+
* @param title - the chip's `title` attribute (the undecorated label).
|
|
42
|
+
* @returns the mention, or undefined when there is nothing to open.
|
|
43
|
+
*/
|
|
44
|
+
export declare function decodeReferenceLink(title: string | null | undefined): ReferenceLink | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Decode one DRAFT reference token the client activated in the composer.
|
|
47
|
+
*
|
|
48
|
+
* Same vocabulary as {@link decodeReferenceLink}, plus the one form the editor
|
|
49
|
+
* decorates by syntax rather than by name: a trailing separator marks a folder
|
|
50
|
+
* mention (`@src/`), whose chip kind is its own and whose click opens the
|
|
51
|
+
* directory through the Host opener.
|
|
52
|
+
* @param token - the activated token, trigger included (e.g. `@src/a.ts`).
|
|
53
|
+
* @returns the mention, or undefined when nothing can be opened.
|
|
54
|
+
*/
|
|
55
|
+
export declare function decodeDraftReference(token: string): ReferenceLink | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* The address of a workspace-relative file in one session: the Harness's
|
|
58
|
+
* `dsh-resource://file/session/<sessionId>/<path>` form, spelled here so the
|
|
59
|
+
* browser bundle needs no new runtime dependency. Backslashes are normalized
|
|
60
|
+
* and a leading `./` is dropped, exactly as the Harness normalizes them; every
|
|
61
|
+
* segment is component-encoded so spaces, `#` and `?` survive the round trip.
|
|
62
|
+
* `tests/reference-links.spec.tsx` pins this against the Harness function.
|
|
63
|
+
* @param sessionId - the session whose Host workspace resolves the path.
|
|
64
|
+
* @param path - workspace-relative path in either separator spelling.
|
|
65
|
+
* @returns the file address the right Sidebar claims.
|
|
66
|
+
*/
|
|
67
|
+
export declare function sessionFileAddress(sessionId: string, path: string): string;
|
|
68
|
+
/**
|
|
69
|
+
* The address of a file or directory OUTSIDE the session workspace: the
|
|
70
|
+
* Harness's second file-address scope, `dsh-resource://file/absolute/<path>`
|
|
71
|
+
* (leading separator dropped, a drive letter kept literal, UNC keeping its empty
|
|
72
|
+
* first segment). Out-of-workspace references carry absolute paths, so this is
|
|
73
|
+
* the address the sidebar is asked for. A sidebar that refuses to read outside
|
|
74
|
+
* the workspace (`ui-sidebar-files` says so in its own words) fails harmlessly
|
|
75
|
+
* and the caller keeps the Host opener as the effective action.
|
|
76
|
+
* @param path - absolute path in either separator spelling.
|
|
77
|
+
* @returns the file address the right Sidebar claims, or falls back from.
|
|
78
|
+
*/
|
|
79
|
+
export declare function absoluteFileAddress(path: string): string;
|
|
80
|
+
/**
|
|
81
|
+
* The address of one folder, in this plugin's OWN resource type.
|
|
82
|
+
*
|
|
83
|
+
* A directory cannot ride the `file` type: `dsh-resource://file/…` is claimed by
|
|
84
|
+
* the file viewers, which answer a directory with `"…" is a directory`, and the
|
|
85
|
+
* `files` tab is a PAGE — a tree rooted at the session workspace — that claims no
|
|
86
|
+
* address at all. So the plugin registers a `folder` type of its own and opens
|
|
87
|
+
* folders through it, which is also the only way an out-of-workspace folder can
|
|
88
|
+
* reach the sidebar. The path is absolute (the opener resolves a workspace folder
|
|
89
|
+
* first); every segment is component-encoded so spaces, `#`, `?` and a drive
|
|
90
|
+
* colon survive the round trip.
|
|
91
|
+
* @param path - absolute folder path in either separator spelling.
|
|
92
|
+
* @returns the folder address this plugin's tab claims.
|
|
93
|
+
*/
|
|
94
|
+
export declare function folderAddress(path: string): string;
|
|
95
|
+
/**
|
|
96
|
+
* The folder one address names, or undefined when it is not ours.
|
|
97
|
+
* @param address - a resource address.
|
|
98
|
+
* @returns the absolute path in canonical (forward-slash) spelling.
|
|
99
|
+
*/
|
|
100
|
+
export declare function folderPathOf(address: string | undefined): string | undefined;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client-side Typert Remote contribution for the dsh-atlas host
|
|
3
|
+
* services: mounts the shared strict descriptors into `ctx.remote` under the
|
|
4
|
+
* `atFile` and `atMention` namespaces. The descriptors and codecs come from
|
|
5
|
+
* the shared contract module, so the browser bundle and the host manifest
|
|
6
|
+
* stay on one wire definition. One contribution per package face: both
|
|
7
|
+
* namespaces ride the same `dsh-atlas` package.
|
|
8
|
+
*/
|
|
9
|
+
import type { RemoteResult, TypertRemoteContribution } from '@deepseek-ai/dsh-typert-protocol';
|
|
10
|
+
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
11
|
+
import type { AtlasRuntimeInfo } from '../atlas.ts';
|
|
12
|
+
import type { AtFileSettings, AtFileSettingsUpdate, ChatCandidate, DirectoryListing, GitChange, PluginCandidate, ReferenceInfo, SkillCandidate } from '../contract.ts';
|
|
13
|
+
export type { DirectoryEntry, DirectoryListing, FileEntry, SkillCandidate, ChatCandidate, PluginCandidate, GitChange } from '../contract.ts';
|
|
14
|
+
/** The combined Remote namespace contribution (one package face). */
|
|
15
|
+
export declare const AT_REMOTE: TypertRemoteContribution;
|
|
16
|
+
declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
17
|
+
/** The `atFile` namespace face. */
|
|
18
|
+
interface TypertRemoteNamespace$617446696c65 {
|
|
19
|
+
search: (agentId: SessionId, signal?: AbortSignal) => Promise<RemoteResult<readonly import('../contract.ts').FileEntry[]>>;
|
|
20
|
+
inspect: (agentId: SessionId, targets: readonly string[], signal?: AbortSignal) => Promise<RemoteResult<readonly ReferenceInfo[]>>;
|
|
21
|
+
getSettings: () => Promise<RemoteResult<AtFileSettings>>;
|
|
22
|
+
updateSettings: (update: AtFileSettingsUpdate) => Promise<RemoteResult<AtFileSettings>>;
|
|
23
|
+
list: (agentId: SessionId, path: string, signal?: AbortSignal) => Promise<RemoteResult<DirectoryListing>>;
|
|
24
|
+
}
|
|
25
|
+
/** The `atlas` namespace face (host half of the @ data-source seam). */
|
|
26
|
+
interface TypertRemoteNamespace$61746c6173 {
|
|
27
|
+
runtime: (signal?: AbortSignal) => Promise<RemoteResult<AtlasRuntimeInfo>>;
|
|
28
|
+
gitChanges: (agentId: SessionId, signal?: AbortSignal) => Promise<RemoteResult<readonly GitChange[]>>;
|
|
29
|
+
}
|
|
30
|
+
/** The `atMention` namespace face. */
|
|
31
|
+
interface TypertRemoteNamespace$61744d656e74696f6e {
|
|
32
|
+
listSkills: (agentId: SessionId, signal?: AbortSignal) => Promise<RemoteResult<readonly SkillCandidate[]>>;
|
|
33
|
+
listChats: (agentId: SessionId, query: string, limit: number, signal?: AbortSignal) => Promise<RemoteResult<readonly ChatCandidate[]>>;
|
|
34
|
+
listPlugins: (signal?: AbortSignal) => Promise<RemoteResult<readonly PluginCandidate[]>>;
|
|
35
|
+
}
|
|
36
|
+
interface TypertRemoteMap {
|
|
37
|
+
'atFile/search': (agentId: SessionId, signal?: AbortSignal) => Promise<RemoteResult<readonly import('../contract.ts').FileEntry[]>>;
|
|
38
|
+
'atFile/inspect': (agentId: SessionId, targets: readonly string[], signal?: AbortSignal) => Promise<RemoteResult<readonly ReferenceInfo[]>>;
|
|
39
|
+
'atFile/external': (agentId: SessionId, signal?: AbortSignal) => Promise<RemoteResult<import('../contract.ts').ExternalAccessScope>>;
|
|
40
|
+
'atFile/list': (agentId: SessionId, path: string, signal?: AbortSignal) => Promise<RemoteResult<DirectoryListing>>;
|
|
41
|
+
'atFile/getSettings': () => Promise<RemoteResult<AtFileSettings>>;
|
|
42
|
+
'atFile/updateSettings': (update: AtFileSettingsUpdate) => Promise<RemoteResult<AtFileSettings>>;
|
|
43
|
+
'atMention/listSkills': (agentId: SessionId, signal?: AbortSignal) => Promise<RemoteResult<readonly SkillCandidate[]>>;
|
|
44
|
+
'atMention/listChats': (agentId: SessionId, query: string, limit: number, signal?: AbortSignal) => Promise<RemoteResult<readonly ChatCandidate[]>>;
|
|
45
|
+
'atMention/listPlugins': (signal?: AbortSignal) => Promise<RemoteResult<readonly PluginCandidate[]>>;
|
|
46
|
+
'atlas/runtime': (signal?: AbortSignal) => Promise<RemoteResult<AtlasRuntimeInfo>>;
|
|
47
|
+
'atlas/gitChanges': (agentId: SessionId, signal?: AbortSignal) => Promise<RemoteResult<readonly GitChange[]>>;
|
|
48
|
+
}
|
|
49
|
+
interface TypertRemoteNamespaceMap {
|
|
50
|
+
atFile: TypertRemoteNamespace$617446696c65;
|
|
51
|
+
atMention: TypertRemoteNamespace$61744d656e74696f6e;
|
|
52
|
+
atlas: TypertRemoteNamespace$61746c6173;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure file-search ranking for the @file menu. A plain query matches basenames
|
|
3
|
+
* only, so letters spread across a long generated path cannot create false
|
|
4
|
+
* positives. Queries containing a slash match path segments in order. The
|
|
5
|
+
* empty query remains a directory-first alphabetical browse view.
|
|
6
|
+
*/
|
|
7
|
+
import type { FileEntry } from './remote.ts';
|
|
8
|
+
/** Ranked top-N paths matching `query` (ties break by kind, length, then path). */
|
|
9
|
+
export declare function rankFiles(files: readonly FileEntry[], query: string, limit: number): readonly FileEntry[];
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The '@' input-trigger source: turns the ui-input-trigger pipeline into the
|
|
3
|
+
* category-based mention picker. An empty query shows the category rows
|
|
4
|
+
* (files, folders, skills, past chats, plugins); typing without choosing
|
|
5
|
+
* shows a mixed result list that prioritizes files/folders/skills while the
|
|
6
|
+
* category rows stay pinned; `file:`, `folder:`, `skill:`, `chat:`,
|
|
7
|
+
* `plugin:` prefixes enter one category. Picks land plain-text tokens that
|
|
8
|
+
* the Host's pre-step boundary validates and expands into sourced references.
|
|
9
|
+
* Pure factory over injected deps: the browser bundle wires the real Remotes
|
|
10
|
+
* and clock, tests wire stubs.
|
|
11
|
+
*/
|
|
12
|
+
import type { InputTriggerCandidate, InputTriggerSource, TokenSpan } from '@deepseek-ai/dsh-client-ui-input-trigger/client';
|
|
13
|
+
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
14
|
+
import type { ChatCandidate, FileEntry, PluginCandidate, SkillCandidate } from './remote.ts';
|
|
15
|
+
import type { DirectoryListing, ExternalAccessScope, UsageEntry } from '../contract.ts';
|
|
16
|
+
import type { AtlasRegistration } from '../atlas.ts';
|
|
17
|
+
import type { SkillTier } from '../contract.ts';
|
|
18
|
+
import { type ReferenceLink } from './reference-links.ts';
|
|
19
|
+
declare module '@deepseek-ai/dsh-client-ui-input-trigger/client' {
|
|
20
|
+
interface InputTriggerCandidate {
|
|
21
|
+
/** Source-owned stable value when the visible name is only a display label. */
|
|
22
|
+
readonly value?: string;
|
|
23
|
+
/** Indexed path kind used by source-owned keyboard navigation. */
|
|
24
|
+
readonly atFileKind?: FileEntry['kind'];
|
|
25
|
+
/** Routing kind for the category state machine and the dock. */
|
|
26
|
+
readonly mentionKind?: MentionKind;
|
|
27
|
+
/** Host-built `dsh-session:` URI for one chat candidate row. */
|
|
28
|
+
readonly chatUri?: string;
|
|
29
|
+
/** Stable collapse key for group header rows (chat:/skill:). */
|
|
30
|
+
readonly groupKey?: string;
|
|
31
|
+
/** Recent-file rows may be pickable before the workspace index settles. */
|
|
32
|
+
readonly recent?: boolean;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** One mention row kind the picker and the dock both understand. */
|
|
36
|
+
export type MentionKind = 'category' | 'back' | 'file' | 'dir' | 'skill' | 'chat' | 'plugin' | 'provider' | 'chat-group' | 'skill-group' | 'plugin-group' | 'section-header' | 'dir-group' | 'folder-choose' | 'browse-up' | 'browse-dir' | 'browse-file' | 'browse-drive' | 'browse-note';
|
|
37
|
+
/** Owner source name (the lexicon and decoration routing key). */
|
|
38
|
+
export declare const SOURCE_NAME = "atlas";
|
|
39
|
+
/** Design cap on visible picker rows (the menu height is maximized by CSS). */
|
|
40
|
+
export declare const MAX_CANDIDATES = 20;
|
|
41
|
+
/** How many provider rows one mixed search may contribute in total. */
|
|
42
|
+
export declare const PROVIDER_MIX_LIMIT = 6;
|
|
43
|
+
/** How long one session's index stays hot before the next menu open refetches. */
|
|
44
|
+
export declare const INDEX_TTL_MS = 30000;
|
|
45
|
+
/** How long the per-session skill list and the global plugin list stay hot. */
|
|
46
|
+
export declare const CATEGORY_TTL_MS = 30000;
|
|
47
|
+
/** One preloaded page of past-chat candidates (menu height is 12; 20 gives headroom). */
|
|
48
|
+
export declare const CHAT_PAGE = 20;
|
|
49
|
+
/** One category key (folder rows are routed as dir mentions). */
|
|
50
|
+
export type CategoryKey = 'file' | 'folder' | 'skill' | 'chat' | 'plugin' | 'provider';
|
|
51
|
+
/** One selectable category row. */
|
|
52
|
+
export interface CategoryDef {
|
|
53
|
+
readonly key: CategoryKey;
|
|
54
|
+
/** The draft prefix that enters this category (`@skill:` etc.). */
|
|
55
|
+
readonly prefix: string;
|
|
56
|
+
/** Built-in Chinese label, used when no locale binder is wired. */
|
|
57
|
+
readonly name: string;
|
|
58
|
+
/** Locale key of the displayed label (see client/locales.ts). */
|
|
59
|
+
readonly nameKey: string;
|
|
60
|
+
/** Single-letter shortcut shown in the menu and accepted after `@`. */
|
|
61
|
+
readonly shortcut: string;
|
|
62
|
+
/** English name prefix matched for completion (e.g. `pl` → plugin). */
|
|
63
|
+
readonly english: string;
|
|
64
|
+
/**
|
|
65
|
+
* Set on rows built from a registered `@` seam provider: it is the provider
|
|
66
|
+
* handle whose `list` fills the category and whose `resolve` the pick routes to.
|
|
67
|
+
*/
|
|
68
|
+
readonly providerId?: string;
|
|
69
|
+
}
|
|
70
|
+
/** Locale binder shape shared by the row builders (dictionary key → copy). */
|
|
71
|
+
export type Translate = (key: string, params?: Record<string, string>) => string;
|
|
72
|
+
/** The five categories, shown in menu order. */
|
|
73
|
+
export declare const CATEGORIES: readonly CategoryDef[];
|
|
74
|
+
/**
|
|
75
|
+
* Build the menu categories for the currently registered `@` seam providers.
|
|
76
|
+
*
|
|
77
|
+
* These are dynamic rows: a provider that registers while the app runs shows up
|
|
78
|
+
* on the next menu open, and one that disposes disappears with it. A provider
|
|
79
|
+
* claims a shortcut letter when it can — the first letter of its id, upper-cased,
|
|
80
|
+
* and only while no built-in category (or an earlier provider) already answers to
|
|
81
|
+
* it. A letter that means two things is worse than no letter, and the explicit
|
|
82
|
+
* `@<id>:` prefix always works either way.
|
|
83
|
+
* @param providers - the live registrations from the seam registry.
|
|
84
|
+
* @returns one category definition per provider, in registration order.
|
|
85
|
+
*/
|
|
86
|
+
export declare function providerCategories(providers: readonly {
|
|
87
|
+
readonly id: string;
|
|
88
|
+
readonly display: string;
|
|
89
|
+
}[]): readonly CategoryDef[];
|
|
90
|
+
/** Everything the source needs that the browser bundle supplies (tests stub). */
|
|
91
|
+
export interface MentionSourceDeps {
|
|
92
|
+
/** Search the addressed session's workspace index (Remote wrapper). */
|
|
93
|
+
search(sessionId: SessionId, signal: AbortSignal): Promise<readonly FileEntry[]>;
|
|
94
|
+
/** List discoverable skills for the addressed session (Remote wrapper). */
|
|
95
|
+
listSkills?(sessionId: SessionId, signal: AbortSignal): Promise<readonly SkillCandidate[]>;
|
|
96
|
+
/** List past-session candidates for the addressed session (Remote wrapper). */
|
|
97
|
+
listChats?(sessionId: SessionId, query: string, limit: number, signal: AbortSignal): Promise<readonly ChatCandidate[]>;
|
|
98
|
+
/** List installed plugins (Remote wrapper). */
|
|
99
|
+
listPlugins?(signal: AbortSignal): Promise<readonly PluginCandidate[]>;
|
|
100
|
+
/** Monotonic clock for index freshness (default Date.now). */
|
|
101
|
+
now?: () => number;
|
|
102
|
+
/** Locale binder for time/tier labels (defaults to zh copy). */
|
|
103
|
+
t?: (key: string, params?: Record<string, string>) => string;
|
|
104
|
+
/** Record one referenced path into the session workspace's recent list. */
|
|
105
|
+
onRecent?(sessionId: SessionId, relative: string): void;
|
|
106
|
+
/** Record one pick in the cross-category usage counters. */
|
|
107
|
+
onUsage?(sessionId: SessionId, kind: string, value: string): void;
|
|
108
|
+
/** The live cross-category pick counters (highest count first). */
|
|
109
|
+
usage?(): readonly UsageEntry[];
|
|
110
|
+
/**
|
|
111
|
+
* The live `@` seam providers. Called on every menu request; a provider that
|
|
112
|
+
* is not registered here is never listed, so registration stays the gate.
|
|
113
|
+
*/
|
|
114
|
+
atlasProviders?(): readonly AtlasRegistration[];
|
|
115
|
+
/** The session workspace's recent-file list for one workspace, most recent first. */
|
|
116
|
+
recentFiles?(sessionId: SessionId, workspace: string): readonly string[];
|
|
117
|
+
/**
|
|
118
|
+
* The Host's out-of-workspace scope for one session: whether it may discover
|
|
119
|
+
* external paths, and the external folders `@folder:` may offer. The Host owns
|
|
120
|
+
* this verdict (settings plus the resolved sandbox policy); the source only
|
|
121
|
+
* renders what it returns.
|
|
122
|
+
*/
|
|
123
|
+
external?(sessionId: SessionId, signal: AbortSignal): Promise<ExternalAccessScope>;
|
|
124
|
+
/**
|
|
125
|
+
* One bounded, one-level directory listing (the folder browser's data). The Host
|
|
126
|
+
* gates an out-of-workspace path exactly as it gates a reference, and answers a
|
|
127
|
+
* refusal as a reason, so a missing member simply means "no browser here".
|
|
128
|
+
*/
|
|
129
|
+
list?(sessionId: SessionId, path: string, signal: AbortSignal): Promise<DirectoryListing>;
|
|
130
|
+
/**
|
|
131
|
+
* The folder the composer's DRAFT is scoped by (R-05): the nearest folder
|
|
132
|
+
* reference typed before the active category token. `@e:/work/docs/ @file:` therefore
|
|
133
|
+
* lists that folder's files — the scope is positional, so no connective word is
|
|
134
|
+
* needed. Undefined when the draft names none, which is this view's behaviour
|
|
135
|
+
* before the scope existed.
|
|
136
|
+
*/
|
|
137
|
+
scopeFolder?(sessionId: SessionId, signal: AbortSignal): Promise<string | undefined>;
|
|
138
|
+
/**
|
|
139
|
+
* Open the product's own folder chooser for one draft token span, answering
|
|
140
|
+
* whether this build has one. The chooser is asynchronous (a native or in-app
|
|
141
|
+
* dialog), so it takes over the insertion itself; a `false` answer lets the
|
|
142
|
+
* source fall back to its own in-menu walk.
|
|
143
|
+
*/
|
|
144
|
+
chooseFolder?(sessionId: SessionId, span: TokenSpan): boolean;
|
|
145
|
+
/**
|
|
146
|
+
* The click action for one mention of the draft, or undefined when this build
|
|
147
|
+
* has none. Used by {@link InputTriggerSource.openReference}: a client that
|
|
148
|
+
* activates a reference token in the composer asks its owning source to open
|
|
149
|
+
* it, and the plugin answers with the same action the sent-message bridge uses.
|
|
150
|
+
*/
|
|
151
|
+
actionFor?(sessionId: SessionId, link: ReferenceLink): (() => void) | undefined;
|
|
152
|
+
}
|
|
153
|
+
/** The registered source plus the cache teardown the wiring layer owns. */
|
|
154
|
+
export interface MentionSource {
|
|
155
|
+
readonly source: InputTriggerSource;
|
|
156
|
+
/** Drop every per-session cache and path map (connection reset). */
|
|
157
|
+
invalidateAll(): void;
|
|
158
|
+
/** Drop one session's out-of-workspace scope (a sent reference may have grown the ledger). */
|
|
159
|
+
invalidateExternal(sessionId: SessionId): void;
|
|
160
|
+
/** Toggle one group header's collapse state for a session (menu re-tracks). */
|
|
161
|
+
toggleGroup(sessionId: SessionId, key: string): void;
|
|
162
|
+
/** Whether a group header is currently collapsed for a session. */
|
|
163
|
+
isCollapsed(sessionId: SessionId, key: string): boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Rebuild the candidate rows for one category query synchronously from the
|
|
166
|
+
* settled caches (instant collapse without a refetch). Returns undefined
|
|
167
|
+
* when the needed cache is not settled yet — callers fall back to a refresh.
|
|
168
|
+
*/
|
|
169
|
+
rebuildRows(sessionId: SessionId, query: string): readonly MentionCandidate[] | undefined;
|
|
170
|
+
}
|
|
171
|
+
/** One picker row with a stable value and routing kind. */
|
|
172
|
+
interface MentionCandidate extends InputTriggerCandidate {
|
|
173
|
+
readonly value: string;
|
|
174
|
+
readonly mentionKind: MentionKind;
|
|
175
|
+
/** Set on provider rows: whose `resolve` the committed reference routes to. */
|
|
176
|
+
readonly providerId?: string;
|
|
177
|
+
/** Set on the Tab-completion hint, which is the row the highlight belongs on. */
|
|
178
|
+
readonly completionHint?: true;
|
|
179
|
+
}
|
|
180
|
+
/** Resolve the active category from a `file:`-style query prefix. */
|
|
181
|
+
export declare function categoryOfQuery(query: string, providers?: readonly CategoryDef[]): CategoryDef | undefined;
|
|
182
|
+
/**
|
|
183
|
+
* The always-pinned category rows for the empty/mixed view.
|
|
184
|
+
* @param t - locale binder.
|
|
185
|
+
* @param providers - the live `@` seam providers, appended after the built-ins.
|
|
186
|
+
* @returns the category rows in menu order.
|
|
187
|
+
*/
|
|
188
|
+
export declare function categoryRows(t?: Translate, providers?: readonly CategoryDef[]): readonly MentionCandidate[];
|
|
189
|
+
/**
|
|
190
|
+
* Resolve the single category the user's typed query points at, or undefined
|
|
191
|
+
* when it is ambiguous.
|
|
192
|
+
*
|
|
193
|
+
* A query matching exactly one shortcut letter (F/D/S/C/P, plus whatever letter
|
|
194
|
+
* a registered provider claimed — `G` for `@git`) or exactly one English name
|
|
195
|
+
* prefix (fi/fo/sk/ch/pl…, and a provider's own id, so `gi` reaches git) wins;
|
|
196
|
+
* otherwise the completion stays unavailable so plain mixed search keeps working.
|
|
197
|
+
* @param query - the text after the trigger.
|
|
198
|
+
* @param providers - the live provider categories, which compete on equal terms.
|
|
199
|
+
* @returns the category the query names, or undefined.
|
|
200
|
+
*/
|
|
201
|
+
export declare function completionTarget(query: string, providers?: readonly CategoryDef[]): CategoryDef | undefined;
|
|
202
|
+
/**
|
|
203
|
+
* The hint row a completion query puts at the END of the list.
|
|
204
|
+
*
|
|
205
|
+
* It is a category row like the one it repeats (same `value`, so a pick enters
|
|
206
|
+
* the same `@<category>:` prefix) plus `completionHint`, which is the flag the
|
|
207
|
+
* navigator uses to land the default highlight here: the menu opens above the
|
|
208
|
+
* composer, so the last row is the one closest to the caret and the eye stays in
|
|
209
|
+
* the bottom band instead of jumping to the top of the list.
|
|
210
|
+
*/
|
|
211
|
+
export declare function hintRow(category: CategoryDef, t?: Translate): MentionCandidate;
|
|
212
|
+
/** The row tag for one mention kind, localized when a binder is supplied. */
|
|
213
|
+
export declare function categoryTag(kind: 'file' | 'folder' | 'skill' | 'chat' | 'plugin', t?: Translate): string;
|
|
214
|
+
/** The back row shown inside one category. */
|
|
215
|
+
export declare function backRow(t?: Translate): MentionCandidate;
|
|
216
|
+
/** A non-pickable section divider row (e.g. 最近引用 / 全部匹配). */
|
|
217
|
+
export declare function sectionHeaderRow(label: string): MentionCandidate;
|
|
218
|
+
/** Relative-time buckets, mirroring the sidebar workspace rows. */
|
|
219
|
+
export interface RelativeTime {
|
|
220
|
+
readonly unit: 'now' | 'minutes' | 'hours' | 'days' | 'months' | 'years';
|
|
221
|
+
readonly n: number;
|
|
222
|
+
}
|
|
223
|
+
/** Compact relative time ("刚刚"/"5分钟"…), same buckets as the sidebar. */
|
|
224
|
+
export declare function relativeTime(updatedAt: number, now: number): RelativeTime;
|
|
225
|
+
/** Render one relative time as the sidebar does ("刚刚"/"5分钟"/"2天"). */
|
|
226
|
+
export declare function relativeTimeLabel(updatedAt: number, now: number, t?: (key: string, params?: Record<string, string>) => string): string;
|
|
227
|
+
/** Skill tier labels (mirror the sidebar Skill Manager grouping). */
|
|
228
|
+
export declare const TIER_ORDER: readonly SkillTier[];
|
|
229
|
+
/**
|
|
230
|
+
* Clean a session title for display and the mention token: trim, drop leading
|
|
231
|
+
* markdown heading markers (#), collapse internal whitespace, cap at 40 chars.
|
|
232
|
+
*/
|
|
233
|
+
export declare function cleanChatLabel(label: string): string;
|
|
234
|
+
/** The workspace title for one chat group (basename; 未分组 for none). */
|
|
235
|
+
export declare function workspaceTitle(cwd: string | undefined, t?: Translate): string;
|
|
236
|
+
/**
|
|
237
|
+
* Damerau–Levenshtein distance between two short strings (optimal string
|
|
238
|
+
* alignment). Used for typo-tolerant mention search.
|
|
239
|
+
*/
|
|
240
|
+
export declare function editDistance(a: string, b: string): number;
|
|
241
|
+
/**
|
|
242
|
+
* Typo-tolerant match: substring OR any word token of the field within edit
|
|
243
|
+
* distance (1 for short queries, 2 for queries of 8+ chars). Enables e.g.
|
|
244
|
+
* `qulity` → `animation-quality-gate` via its `quality` token.
|
|
245
|
+
*/
|
|
246
|
+
export declare function matchesFuzzy(query: string, ...fields: readonly string[]): boolean;
|
|
247
|
+
/**
|
|
248
|
+
* Rank files for one query, falling back to typo tolerance only when the exact
|
|
249
|
+
* ranking returns nothing — so a near miss can never displace an exact match.
|
|
250
|
+
* @param files - the workspace index.
|
|
251
|
+
* @param query - the user's query.
|
|
252
|
+
* @param limit - maximum rows.
|
|
253
|
+
* @returns the ranked entries.
|
|
254
|
+
*/
|
|
255
|
+
export declare function rankFilesWithFuzzy(files: readonly FileEntry[], query: string, limit: number): readonly FileEntry[];
|
|
256
|
+
/**
|
|
257
|
+
* The directory a draft query asks to walk, or undefined when it is a search.
|
|
258
|
+
*
|
|
259
|
+
* Both spellings reach the browser: the bare mention the browser's own picks write
|
|
260
|
+
* (`@E:/…/`) and the category form a user may type (`@folder:E:/…`). A drive letter
|
|
261
|
+
* alone is its root, so `@folder:E:` is enough to leave the drive you are on. A
|
|
262
|
+
* FILE path is not a special case: it browses the directory it lives in, where the
|
|
263
|
+
* file itself is a row — which is what makes typing a path feel like a path.
|
|
264
|
+
* @param query - the live query (text after `@`).
|
|
265
|
+
* @returns the absolute directory to list, or undefined for an ordinary search.
|
|
266
|
+
*/
|
|
267
|
+
export declare function browseTarget(query: string): string | undefined;
|
|
268
|
+
/** The collapse key of the out-of-workspace folder group. */
|
|
269
|
+
export declare const OUTSIDE_GROUP_KEY = "folder:outside";
|
|
270
|
+
/** The collapse key of the workspace folder group. */
|
|
271
|
+
export declare const WORKSPACE_GROUP_KEY = "folder:workspace";
|
|
272
|
+
/**
|
|
273
|
+
* How many rows the folder category shows at most (headers included).
|
|
274
|
+
*
|
|
275
|
+
* The user's口径: the visible area must always be the folders whose NAME matched —
|
|
276
|
+
* workspace first, then outside — and the folder view must not FOLD rows away to
|
|
277
|
+
* stay short. Beyond what fits on screen the menu SCROLLS (with PageUp/PageDown,
|
|
278
|
+
* and the focus row is kept visible), so this is only the hard ceiling that keeps
|
|
279
|
+
* one category from flooding the menu; the name matches still claim the top rows
|
|
280
|
+
* and the path-only group fills whatever is left.
|
|
281
|
+
*/
|
|
282
|
+
export declare const FOLDER_ROW_BUDGET = 20;
|
|
283
|
+
/** The collapse key of the path-only matches (folded, so they never bury the name hits). */
|
|
284
|
+
export declare const PATH_GROUP_KEY = "folder:path";
|
|
285
|
+
/**
|
|
286
|
+
* How many rows the `@file:` view shows at most, headers included.
|
|
287
|
+
*
|
|
288
|
+
* The same口径 as the folder view (R-03/R-05): the user asked for the two views to
|
|
289
|
+
* behave alike, so this is deliberately the same ceiling — and the same answer to
|
|
290
|
+
* "more than fits": scroll, don't truncate the priority rows away.
|
|
291
|
+
*/
|
|
292
|
+
export declare const FILE_ROW_BUDGET = 20;
|
|
293
|
+
/** The collapse key of the file view's path-only matches. */
|
|
294
|
+
export declare const FILE_PATH_GROUP_KEY = "file:path";
|
|
295
|
+
/**
|
|
296
|
+
* The row that starts walking the filesystem from the session workspace root.
|
|
297
|
+
*
|
|
298
|
+
* It is the folder category's default highlight, because the case this feature
|
|
299
|
+
* exists for is "a folder that is NOT in my workspace" — and a user who wants one
|
|
300
|
+
* of the workspace rows does not need to travel through it.
|
|
301
|
+
* @param t - locale binder.
|
|
302
|
+
* @returns the row (its pick drafts the workspace root and keeps the menu open).
|
|
303
|
+
*/
|
|
304
|
+
export declare function folderChooseRow(t?: Translate): MentionCandidate;
|
|
305
|
+
/** The stable collapse key for one skill tier. */
|
|
306
|
+
export declare function skillGroupKey(tier: SkillTier): string;
|
|
307
|
+
/** The stable collapse key for one skill domain inside a tier. */
|
|
308
|
+
export declare function skillDomainKey(tier: SkillTier, domain: string): string;
|
|
309
|
+
/** The domain of a skill: the prefix before the first '-' (e.g. blender-modeling → blender). */
|
|
310
|
+
export declare function domainOf(skillName: string): string;
|
|
311
|
+
/** The scope group of one plugin module: the npm scope (e.g. @deepseek-ai) or 其他. */
|
|
312
|
+
export declare function pluginScope(moduleName: string): string;
|
|
313
|
+
/** The stable collapse key for one plugin scope group. */
|
|
314
|
+
export declare function pluginGroupKey(scope: string): string;
|
|
315
|
+
/** The stable collapse key for one chat workspace group. */
|
|
316
|
+
export declare function chatGroupKey(cwd: string | undefined): string;
|
|
317
|
+
/**
|
|
318
|
+
* Build the '@' trigger source over the injected deps. One source per plugin
|
|
319
|
+
* fiber; per-session caches live in the returned closure and die with it.
|
|
320
|
+
* @param deps - Remote, locale, and clock faces.
|
|
321
|
+
* @returns the source to register with `inputTriggers.registerSource`, plus
|
|
322
|
+
* the cache invalidator.
|
|
323
|
+
*/
|
|
324
|
+
export declare function createMentionSource(deps: MentionSourceDeps): MentionSource;
|
|
325
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dock stylesheet, hand-written as a template string and injected once by
|
|
3
|
+
* the plugin body: the web server serves exactly one file per client plugin,
|
|
4
|
+
* so no separate CSS artifact may exist. Tokens come only from the shared
|
|
5
|
+
* `--dsw-alias-*` design platform (no literal colors); class names carry the
|
|
6
|
+
* `dsh_atFile` prefix to stay unique in the assembled shell.
|
|
7
|
+
*/
|
|
8
|
+
/** Stable `<style>` element id (idempotent injection across HMR re-runs). */
|
|
9
|
+
export declare const STYLE_ID = "dsh-atlas-style";
|
|
10
|
+
/** The dock's injected stylesheet text. */
|
|
11
|
+
export declare const cssText = "\n.dsh_atFile_rail {\n box-sizing: border-box;\n display: flex;\n flex: none;\n flex-wrap: wrap;\n gap: 6px;\n width: calc(\n 100% -\n var(--dsh-composer-side-clearance) -\n var(--dsh-composer-side-clearance)\n );\n max-width: var(--dsh-composer-card-max-width);\n min-width: 0;\n margin: 0 auto;\n}\n.dsh_atFile_row {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n min-width: 0;\n max-width: 100%;\n min-height: 36px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 14px;\n background: var(--dsw-alias-bg-layer-1);\n}\n.dsh_atFile_path {\n display: inline-flex;\n align-items: flex-end;\n gap: 6px;\n min-width: 0;\n max-width: 360px;\n height: 100%;\n padding: 0 6px 0 10px;\n border: 0;\n background: none;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n line-height: 18px;\n cursor: pointer;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh_atFile_path:hover {\n color: var(--dsw-alias-brand-primary);\n}\n.dsh_atFile_cost {\n flex: none;\n padding: 0 6px;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n line-height: 18px;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n.dsh_atFile_cost_warn {\n color: var(--dsw-alias-label-error, #ef4444);\n font-weight: 600;\n}\n.dsh_atFile_missing {\n flex: none;\n padding: 0 6px;\n color: var(--dsw-alias-label-error, #ef4444);\n font-size: 11px;\n line-height: 18px;\n white-space: nowrap;\n}\n.dsh_atFile_icon {\n flex: none;\n width: 14px;\n height: 14px;\n}\n.dsh_atFile_remove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 20px;\n height: 20px;\n margin-right: 4px;\n border: 0;\n border-radius: 10px;\n background: none;\n color: var(--dsw-alias-label-dimmed);\n cursor: pointer;\n}\n.dsh_atFile_remove svg {\n width: 12px;\n height: 12px;\n}\n.dsh_atFile_remove:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n.dsh_atFile_section {\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n}\n.dsh_atFile_title {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 18px;\n line-height: 26px;\n font-weight: 600;\n}\n.dsh_atFile_card {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n min-width: 0;\n padding: 14px 16px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-1);\n cursor: pointer;\n}\n.dsh_atFile_checkbox {\n flex: none;\n width: 18px;\n height: 18px;\n margin: 2px 0 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n.dsh_atFile_cardText {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n.dsh_atFile_cardTitle {\n color: var(--dsw-alias-label-primary);\n font-size: 14px;\n line-height: 22px;\n}\n.dsh_atFile_cardDesc {\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n line-height: 20px;\n}\n.dsh_atFile_filter {\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n padding-top: 4px;\n}\n.dsh_atFile_filterHeading {\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n flex-wrap: wrap;\n gap: 12px;\n min-width: 0;\n}\n.dsh_atFile_filterHeadingText {\n display: flex;\n flex: 1 1 280px;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n.dsh_atFile_filterTitle {\n color: var(--dsw-alias-label-primary);\n font-size: 14px;\n line-height: 22px;\n font-weight: 600;\n}\n.dsh_atFile_filterDesc,\n.dsh_atFile_filterHint,\n.dsh_atFile_workspaceField > span {\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n line-height: 20px;\n}\n.dsh_atFile_scopeTabs {\n display: inline-flex;\n flex: 0 1 auto;\n min-width: 220px;\n padding: 3px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n}\n.dsh_atFile_scopeTab {\n flex: 1 1 0;\n min-width: 0;\n height: 30px;\n padding: 0 14px;\n border: 0;\n border-radius: 6px;\n background: none;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n cursor: pointer;\n}\n.dsh_atFile_scopeTab:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n.dsh_atFile_scopeTab[aria-selected='true'] {\n background: var(--dsw-alias-button-ghost-active-fill);\n color: var(--dsw-alias-label-primary);\n font-weight: 600;\n}\n.dsh_atFile_workspaceField {\n display: flex;\n flex-direction: column;\n gap: 5px;\n min-width: 0;\n}\n.dsh_atFile_workspaceSelect,\n.dsh_atFile_filterInput {\n box-sizing: border-box;\n width: 100%;\n min-width: 0;\n height: 36px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n outline: none;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n}\n.dsh_atFile_workspaceSelect:focus,\n.dsh_atFile_filterInput:focus {\n border-color: var(--dsw-alias-brand-primary);\n}\n.dsh_atFile_workspaceSelect:disabled,\n.dsh_atFile_filterInput:disabled {\n opacity: 0.55;\n}\n.dsh_atFile_filterToolbar {\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n flex-wrap: wrap;\n gap: 8px;\n min-width: 0;\n}\n.dsh_atFile_filterGroupTitle {\n color: var(--dsw-alias-label-primary);\n font-size: 14px;\n line-height: 22px;\n font-weight: 600;\n}\n.dsh_atFile_secondaryButton {\n flex: none;\n min-height: 30px;\n padding: 0 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 15px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n}\n.dsh_atFile_secondaryButton:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n.dsh_atFile_secondaryButton:disabled,\n.dsh_atFile_filterRemove:disabled,\n.dsh_atFile_addButton:disabled {\n opacity: 0.45;\n cursor: default;\n}\n.dsh_atFile_filterList {\n min-width: 0;\n overflow: hidden;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n}\n.dsh_atFile_filterRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n min-width: 0;\n min-height: 40px;\n padding: 0 8px 0 12px;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n}\n.dsh_atFile_filterRow:last-child {\n border-bottom: 0;\n}\n.dsh_atFile_filterName {\n min-width: 0;\n overflow: hidden;\n color: var(--dsw-alias-label-primary);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n line-height: 20px;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh_atFile_ruleMain {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n flex: 1 1 auto;\n gap: 8px;\n min-width: 0;\n}\n.dsh_atFile_ruleBadge {\n flex: none;\n padding: 2px 6px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 4px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n line-height: 16px;\n}\n.dsh_atFile_ruleMain .dsh_atFile_filterName {\n flex: 1 1 180px;\n}\n.dsh_atFile_filterRemove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 28px;\n height: 28px;\n border: 0;\n border-radius: 14px;\n background: none;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n}\n.dsh_atFile_filterRemove:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n color: var(--dsw-alias-state-error-primary);\n}\n.dsh_atFile_filterRemove svg,\n.dsh_atFile_addButton svg {\n width: 15px;\n height: 15px;\n}\n.dsh_atFile_filterEmpty {\n padding: 16px 12px;\n color: var(--dsw-alias-label-tertiary);\n font-size: 13px;\n line-height: 20px;\n text-align: center;\n}\n.dsh_atFile_filterAddRow {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px;\n min-width: 0;\n}\n.dsh_atFile_ruleMode {\n display: inline-flex;\n align-self: flex-start;\n gap: 2px;\n padding: 3px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n}\n.dsh_atFile_ruleModeButton {\n min-width: 72px;\n height: 28px;\n padding: 0 10px;\n border: 0;\n border-radius: 5px;\n background: none;\n color: var(--dsw-alias-label-secondary);\n font: inherit;\n font-size: 12px;\n cursor: pointer;\n}\n.dsh_atFile_ruleModeButton[aria-pressed='true'] {\n background: var(--dsw-alias-button-ghost-active-fill);\n color: var(--dsw-alias-label-primary);\n font-weight: 600;\n}\n.dsh_atFile_caseToggle {\n display: inline-flex;\n align-items: center;\n align-self: flex-start;\n gap: 7px;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n}\n.dsh_atFile_caseToggle input {\n width: 15px;\n height: 15px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n}\n.dsh_atFile_filterAddRow .dsh_atFile_filterInput {\n flex: 1 1 240px;\n width: auto;\n}\n.dsh_atFile_filterInput[aria-invalid='true'] {\n border-color: var(--dsw-alias-state-error-primary);\n}\n.dsh_atFile_addButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n flex: none;\n height: 36px;\n padding: 0 14px;\n border: 0;\n border-radius: 18px;\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-inverted);\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n cursor: pointer;\n}\n.dsh_atFile_filterError {\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n line-height: 20px;\n}\n.dsh_atFile_inherited {\n display: flex;\n flex-direction: column;\n gap: 7px;\n min-width: 0;\n padding-top: 4px;\n}\n.dsh_atFile_inheritedTitle {\n color: var(--dsw-alias-label-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n.dsh_atFile_inheritedList {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n min-width: 0;\n}\n.dsh_atFile_inheritedList code {\n max-width: 100%;\n overflow: hidden;\n padding: 3px 8px;\n border-radius: 4px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-secondary);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n line-height: 18px;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n@media (max-width: 560px) {\n .dsh_atFile_scopeTabs {\n width: 100%;\n }\n .dsh_atFile_addButton {\n flex: 1 1 auto;\n }\n}\n\n\n/* Maximize the @ mention menu height: the framework caps the listbox at 400px\n (its own design cap, itself already clamped to the space above the composer)\n inline; target only OUR menu (the atlas data-source marker is present on BOTH\n the pending loading row and the ready option rows, so the override holds from\n the first open and the width never jumps).\n\n The WIDTH is deliberately left alone. The framework lays the menu out as an\n absolutely positioned box with left:0 and right:0 inside the composer card's\n own overlay anchor, so it is exactly as wide as the card, which is itself\n capped by --dsh-composer-card-max-width. A content-sized width here (the\n previous behaviour) could push the popup past the composer instead \u2014 the\n plugin's UI must never leave the chat box. Long names wrap inside the card\n width instead (see the itemName rule below). */\n[role=\"listbox\"]:has([data-source=\"atlas\"]) {\n /* Fit as much of the list as the composer leaves room for: MentionNavigator\n measures the composer card's top edge into --dsh-atlas-menu-max on every\n menu change and on resize, so the cap follows the real available height (a\n multi-line draft shrinks it) instead of a fixed viewport guess. The\n fallback keeps the previous behaviour until the first measurement lands,\n and 760px stays the absolute ceiling. */\n max-height: min(760px, var(--dsh-atlas-menu-max, calc(100dvh - 100px))) !important;\n}\n/* The MENU SHELL's own cap is 400px, set inline by the framework. Our category is\n short by design (a bounded row budget), and the user asked the popup to use the\n space above the composer instead of cutting the list at 400px \u2014 so when our\n group is present the shell may grow to the same measured budget. An important\n rule outranks an inline style, and a taller cap never stretches a short list\n (the height stays content-driven). */\n[data-trigger-menu]:has([data-source=\"atlas\"]) {\n max-height: min(760px, var(--dsh-atlas-menu-max, calc(100dvh - 100px))) !important;\n}\n[id^=\"dsh-slash-option-atlas-\"] {\n /* Text and icon share the bottom edge so the label reads against the icon. */\n align-items: flex-end !important;\n gap: 10px !important;\n}\n[id^=\"dsh-slash-option-atlas-\"] [class*=\"itemIcon\"] {\n /* The icon slot grows with the bigger Tabler icons (36px skill included). */\n width: 32px !important;\n height: 32px !important;\n flex: none !important;\n}\n[id^=\"dsh-slash-option-atlas-\"] [class*=\"itemName\"] {\n /* The name NEVER shrinks to make room for the description: full width,\n wrapping only when the menu hits its max width. */\n max-width: none !important;\n flex: 0 0 auto !important;\n min-width: 0 !important;\n white-space: normal !important;\n overflow-wrap: anywhere !important;\n overflow: visible !important;\n text-overflow: clip !important;\n}\n[id^=\"dsh-slash-option-atlas-\"] [class*=\"itemDescription\"] {\n /* The description stays on ONE line with an ellipsis inside the remaining\n space \u2014 it can never spill into the name or the next row. */\n flex: 1 1 auto !important;\n min-width: 0 !important;\n white-space: nowrap !important;\n overflow: hidden !important;\n text-overflow: ellipsis !important;\n}\n\n\n/* Display-only side ad panel: beside the @ menu, same height, never in the\n list. z-index stays BELOW the menu's 100 so a residual overlap can never\n hide the candidate list (placement also keeps the panel outside the menu). */\n.dsh_atAll_ad {\n position: fixed;\n z-index: 90;\n width: 150px; /* overwritten inline: adapts to the ad's aspect */\n border-radius: 12px;\n overflow: hidden;\n pointer-events: none;\n background: linear-gradient(160deg, #0b1e33 0%, #12294a 55%, #1c3d6b 100%);\n border: 1px solid rgba(255, 255, 255, 0.12);\n box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);\n}\n.dsh_atAll_ad img {\n display: block;\n width: 100%;\n height: 100%;\n /* contain (never crop): the whole ad stays visible, letterboxed on the\n panel's gradient when the panel is taller than the image aspect. */\n object-fit: contain;\n}\n.dsh_atAll_adBanner {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 10px;\n width: 100%;\n height: 100%;\n min-height: 120px;\n padding: 12px;\n box-sizing: border-box;\n}\n.dsh_atAll_adWordmark {\n font-family: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif;\n font-size: 22px;\n font-weight: 700;\n letter-spacing: 6px;\n color: rgba(255, 255, 255, 0.95);\n text-shadow: 0 2px 10px rgba(120, 180, 255, 0.45);\n}\n.dsh_atAll_adTag {\n font-size: 11px;\n letter-spacing: 2px;\n color: rgba(255, 255, 255, 0.55);\n}\n\n/* Sent-message reference chips are inert in this client build (its chat view\n calls projectUserText without the reference actions), so ReferenceLinks\n supplies the click and publishes the affordance itself: only a chip that\n bridge can actually open carries data-dsh-atlas-link. The styling is the\n framework's OWN link language \u2014 the tokens its wired chips use (the\n ui-primitives markdown fileMention rule): link-blue at rest, a dotted\n underline under the pointer. A build that wires the chips itself renders\n buttons, which this rule never matches. */\n[data-dsh-atlas-link] {\n color: var(--dsw-alias-link);\n font-weight: 500;\n text-decoration: none;\n cursor: pointer;\n}\n[data-dsh-atlas-link]:hover,\n[data-dsh-atlas-link]:focus {\n outline: none;\n text-decoration: underline dotted var(--dsw-alias-link);\n text-underline-offset: 3px;\n}\n/* A reference whose target is gone: the click learned it, so the chip stops\n looking like a link and reads as stale instead \u2014 the dock's own \u5DF2\u5931\u6548\n language, applied to a chip the plugin does not own. */\n[data-dsh-atlas-missing] {\n color: var(--dsw-alias-label-dimmed, inherit);\n text-decoration: line-through;\n cursor: default;\n}\n/* A composer reference chip: the framework styles its BODY with the chip blue and\n cursor:default, and that rule is a single class \u2014 the same specificity as the\n link rule above, so this one is spelled with the host ancestor to win outright\n (a chip that opens must not keep telling the pointer it is not clickable). An\n inert chip keeps the framework's own look and cursor untouched. */\n[data-composer-chip] [data-dsh-atlas-link] {\n color: var(--dsw-alias-link);\n cursor: pointer;\n}\n[data-composer-chip] [data-dsh-atlas-link]:hover {\n text-decoration: underline dotted var(--dsw-alias-link);\n text-underline-offset: 3px;\n}\n[data-composer-chip] [data-dsh-atlas-missing] {\n color: var(--dsw-alias-label-dimmed, inherit);\n text-decoration: line-through;\n cursor: default;\n}\n/* A draft reference is drawn as a link by the plugin itself: the framework\n colours only the head of a token it recognises (its matcher stops at the last\n separator or the first non-word character), and a hand-typed token it never\n recognised stays plain. DraftLinks paints the WHOLE token through the CSS\n Custom Highlight API \u2014 the one way to colour a text range without\n re-parenting a Lexical text span, whose updateDOM writes through\n firstChild.nodeValue. The colour is the framework's own reference colour (its\n composer-editor textRef rule), so a token it did colour reads unchanged. */\n::highlight(dsh-atlas-draft-ref) {\n color: var(--dsw-alias-state-business-primary);\n}\n/* A draft reference whose target the Host reports as gone: the dock's own \u5DF2\u5931\u6548\n language (dimmed and struck through), painted over the same range so a\n reference that cannot open still reads as a reference. It is never a link:\n nothing here touches the cursor, and the click gate refuses it. */\n::highlight(dsh-atlas-draft-missing) {\n color: var(--dsw-alias-label-dimmed, inherit);\n text-decoration: line-through;\n}\n/* Menu glyphs: this plugin's own icons, drawn over the icon slot the framework\n reserves on each of its rows (see MenuIcons). They live in their own fixed\n layer \u2014 nothing is inserted into a row the framework owns \u2014 and a slot is\n marked only while a glyph covers it, which is what hides the framework's own\n three-glyph set exactly there and nowhere else. */\n.dsh_atlas_menuIcon {\n position: fixed;\n z-index: 2147482500;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: var(--dsw-alias-label-secondary, #9aa4b2);\n pointer-events: none;\n}\n[data-dsh-atlas-menu-icon-host] > svg {\n display: none;\n}\n/* The folder tab: a directory, one level, walked in place. Rows are the sidebar's\n own density (they sit next to the files tree), the path is the only wide text\n and it ellipsizes rather than pushing the controls out of the header. */\n.dsh_atlas_folder {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n overflow-y: auto;\n font-size: 13px;\n color: var(--dsw-alias-label-primary);\n}\n.dsh_atlas_folderHead {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border-bottom: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.25));\n position: sticky;\n top: 0;\n background: var(--dsw-alias-bg-primary, transparent);\n}\n.dsh_atlas_folderUp,\n.dsh_atlas_folderOpen {\n flex: none;\n border: none;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #9aa4b2);\n cursor: pointer;\n border-radius: 6px;\n padding: 2px 6px;\n font: inherit;\n}\n.dsh_atlas_folderUp:disabled {\n opacity: 0.4;\n cursor: default;\n}\n.dsh_atlas_folderUp:not(:disabled):hover,\n.dsh_atlas_folderOpen:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.16));\n}\n.dsh_atlas_folderPath {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n direction: rtl;\n text-align: left;\n color: var(--dsw-alias-label-secondary, #9aa4b2);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n}\n.dsh_atlas_folderNote {\n margin: 8px 10px;\n color: var(--dsw-alias-label-secondary, #9aa4b2);\n font-size: 12px;\n}\n.dsh_atlas_folderList {\n list-style: none;\n margin: 0;\n padding: 4px;\n}\n.dsh_atlas_folderRow {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 4px 6px;\n border: none;\n border-radius: 6px;\n /* A row stays plain until the pointer is on it. The theme paints generic\n buttons, and a list where every line carries a plate reads as a table rather\n than a tree (reported from a screenshot), so this one wins outright. */\n background: none !important;\n box-shadow: none !important;\n color: inherit;\n cursor: pointer;\n text-align: left;\n font: inherit;\n}\n.dsh_atlas_folderRow:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.16)) !important;\n}\n.dsh_atlas_folderGlyph {\n display: inline-flex;\n flex: none;\n align-items: center;\n}\n.dsh_atlas_folderName {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n/* The folder chooser dialog: the product's browse backend wearing this plugin's\n face (the product's own dialog is a private child slot of the workspace picker,\n so a plugin cannot render it). */\n.dsh_atlas_pickerScrim {\n position: fixed;\n inset: 0;\n z-index: 2147482600;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.45);\n}\n.dsh_atlas_picker {\n display: flex;\n flex-direction: column;\n width: min(560px, 88vw);\n max-height: min(560px, 84vh);\n border-radius: 14px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.3));\n background: var(--dsw-specific-menu, var(--dsw-alias-bg-primary, #1b1f2a));\n box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n overflow: hidden;\n}\n.dsh_atlas_pickerHead {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 14px 16px 8px;\n}\n.dsh_atlas_pickerTitle {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n}\n.dsh_atlas_pickerPathRow {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.dsh_atlas_pickerPath {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--dsw-alias-label-secondary, #9aa4b2);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n}\n.dsh_atlas_pickerCrumbs {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n}\n.dsh_atlas_pickerCrumb {\n border: none;\n background: none !important;\n color: var(--dsw-alias-label-secondary, #9aa4b2);\n cursor: pointer;\n font: inherit;\n font-size: 12px;\n padding: 1px 4px;\n border-radius: 5px;\n}\n.dsh_atlas_pickerCrumb:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.16)) !important;\n}\n.dsh_atlas_pickerList {\n list-style: none;\n margin: 0;\n padding: 4px 8px;\n overflow-y: auto;\n flex: 1 1 auto;\n min-height: 120px;\n}\n.dsh_atlas_pickerRow {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 5px 8px;\n border: none;\n border-radius: 7px;\n background: none !important;\n color: inherit;\n cursor: pointer;\n text-align: left;\n font: inherit;\n}\n.dsh_atlas_pickerRow:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.16)) !important;\n}\n.dsh_atlas_pickerGlyph {\n display: inline-flex;\n flex: none;\n align-items: center;\n}\n.dsh_atlas_pickerName {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dsh_atlas_pickerChevron {\n flex: none;\n color: var(--dsw-alias-label-secondary, #9aa4b2);\n}\n.dsh_atlas_pickerNote {\n margin: 6px 16px;\n color: var(--dsw-alias-label-secondary, #9aa4b2);\n font-size: 12px;\n}\n.dsh_atlas_pickerNewRow {\n display: flex;\n gap: 6px;\n padding: 0 16px 8px;\n}\n.dsh_atlas_pickerInput {\n flex: 1 1 auto;\n min-width: 0;\n padding: 5px 8px;\n border-radius: 7px;\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.35));\n background: transparent;\n color: inherit;\n font: inherit;\n}\n.dsh_atlas_pickerFoot {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 16px 14px;\n border-top: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.25));\n}\n.dsh_atlas_pickerButton {\n border: 1px solid var(--dsw-alias-border-l1, rgba(128, 128, 128, 0.35));\n border-radius: 7px;\n background: none !important;\n color: inherit;\n cursor: pointer;\n font: inherit;\n padding: 4px 10px;\n}\n.dsh_atlas_pickerButton:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.16)) !important;\n}\n.dsh_atlas_pickerButton:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.dsh_atlas_pickerIconButton {\n border: none;\n background: none !important;\n color: var(--dsw-alias-label-secondary, #9aa4b2);\n cursor: pointer;\n font: inherit;\n padding: 2px 6px;\n border-radius: 6px;\n}\n.dsh_atlas_pickerIconButton:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.16)) !important;\n}\n.dsh_atlas_pickerPrimary {\n border-color: transparent;\n background: var(--dsw-alias-state-business-primary, #5686fe) !important;\n color: #fff;\n}\n.dsh_atlas_pickerToggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n color: var(--dsw-alias-label-secondary, #9aa4b2);\n font-size: 12px;\n}\n.dsh_atlas_pickerSpacer {\n flex: 1 1 auto;\n}\n";
|
|
12
|
+
/**
|
|
13
|
+
* Inject the dock stylesheet once (stable id; HMR-safe).
|
|
14
|
+
*/
|
|
15
|
+
export declare function adoptStyles(): void;
|