chrome-devtools-frontend 1.0.1697595 → 1.0.1698733
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/.agents/skills/version-control/SKILL.md +21 -9
- package/.stylelintignore +2 -0
- package/front_end/core/common/VersionController.ts +5 -1
- package/front_end/core/platform/HostRuntime.ts +12 -0
- package/front_end/core/platform/api/HostRuntime.ts +13 -0
- package/front_end/core/platform/browser/HostRuntime.ts +34 -0
- package/front_end/core/platform/node/HostRuntime.ts +40 -0
- package/front_end/core/sdk/AnimationModel.ts +21 -99
- package/front_end/core/sdk/CSSModel.ts +4 -2
- package/front_end/core/sdk/CSSPropertyParser.ts +4 -4
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +4 -44
- package/front_end/core/sdk/Connections.ts +6 -1
- package/front_end/core/sdk/ConsoleModel.ts +1 -12
- package/front_end/core/sdk/DOMModel.ts +3 -51
- package/front_end/core/sdk/DebuggerModel.ts +9 -8
- package/front_end/core/sdk/NetworkRequest.ts +1 -1
- package/front_end/core/sdk/PageFunctions.ts +187 -0
- package/front_end/core/sdk/Resource.ts +1 -1
- package/front_end/core/sdk/RuntimeModel.ts +1 -20
- package/front_end/core/sdk/Script.ts +21 -0
- package/front_end/core/sdk/SourceMap.ts +15 -2
- package/front_end/core/sdk/SourceMapCache.ts +13 -10
- package/front_end/core/sdk/SourceMapManager.ts +11 -9
- package/front_end/core/sdk/SourceMapScopeChainEntry.ts +59 -17
- package/front_end/core/sdk/SourceMapScopesInfo.ts +8 -2
- package/front_end/entrypoints/heap_snapshot_worker/ContextAnalyzer.ts +635 -0
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +26 -12
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotLoader.ts +16 -2
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.ts +4 -4
- package/front_end/entrypoints/heap_snapshot_worker/heap_snapshot_worker.ts +2 -0
- package/front_end/entrypoints/shell/shell.ts +1 -1
- package/front_end/generated/InspectorBackendCommands.ts +1 -1
- package/front_end/generated/protocol.ts +8 -2
- package/front_end/models/ai_assistance/DOMHelpers.ts +95 -0
- package/front_end/models/ai_assistance/EvaluateAction.ts +5 -42
- package/front_end/models/ai_assistance/ExtensionScope.ts +2 -31
- package/front_end/models/ai_assistance/agents/AiAgent.ts +2 -1
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +27 -2
- package/front_end/models/ai_assistance/ai_assistance.ts +6 -4
- package/front_end/models/ai_assistance/contexts/DOMNodeContext.ts +10 -7
- package/front_end/models/ai_assistance/contexts/FileContext.ts +2 -10
- package/front_end/models/ai_assistance/skills/performance.md +4 -3
- package/front_end/models/ai_assistance/tools/GetSourceContent.ts +3 -2
- package/front_end/models/ai_assistance/tools/{GetFunctionCode.ts → GetTraceFunctionCode.ts} +26 -13
- package/front_end/models/ai_assistance/tools/{GetResourceContent.ts → GetTraceResourceContent.ts} +31 -11
- package/front_end/models/ai_assistance/tools/ListSources.ts +21 -14
- package/front_end/models/ai_assistance/tools/Tool.ts +2 -2
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +6 -6
- package/front_end/models/bindings/CompilerScriptMapping.ts +3 -10
- package/front_end/models/bindings/DebuggerLanguagePlugins.ts +48 -17
- package/front_end/models/bindings/ResourceScriptMapping.ts +2 -2
- package/front_end/models/bindings/StylesSourceMapping.ts +1 -1
- package/front_end/models/comment_manager/CD4ABridge.ts +55 -12
- package/front_end/models/comment_manager/CommentManager.ts +46 -91
- package/front_end/models/comment_manager/CommentThread.ts +145 -0
- package/front_end/models/comment_manager/comment_manager.ts +2 -0
- package/front_end/models/elements/DOMChanges.ts +238 -0
- package/front_end/models/elements/elements.ts +2 -0
- package/front_end/models/emulation/DeviceModeModel.ts +53 -0
- package/front_end/models/extensions/ExtensionAPI.ts +8 -6
- package/front_end/models/extensions/ExtensionEndpoint.ts +11 -8
- package/front_end/models/extensions/HostUrlPattern.ts +4 -3
- package/front_end/models/extensions/LanguageExtensionEndpoint.ts +3 -2
- package/front_end/models/extensions/RecorderExtensionEndpoint.ts +2 -1
- package/front_end/models/formatter/FormatterWorkerPool.ts +4 -6
- package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +55 -0
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +18 -14
- package/front_end/models/issues_manager/IssuesManager.ts +5 -0
- package/front_end/models/issues_manager/WebInstallIssue.ts +63 -0
- package/front_end/models/issues_manager/descriptions/webInstallManifestMissingId.md +3 -0
- package/front_end/models/issues_manager/descriptions/webInstallManifestMissingNameOrShortName.md +3 -0
- package/front_end/models/issues_manager/descriptions/webInstallManifestParsingOrNetworkError.md +3 -0
- package/front_end/models/issues_manager/descriptions/webInstallNoManifest.md +3 -0
- package/front_end/models/issues_manager/descriptions/webInstallStartUrlInvalid.md +3 -0
- package/front_end/models/issues_manager/issues_manager.ts +2 -0
- package/front_end/models/workspace/UISourceCode.ts +20 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +7 -7
- package/front_end/panels/ai_assistance/components/AIv2MarkdownRenderer.ts +4 -10
- package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +25 -5
- package/front_end/panels/browser_debugger/ObjectEventListenersSidebarPane.ts +2 -3
- package/front_end/panels/common/CommentThreadWidget.ts +221 -0
- package/front_end/{ui/comments → panels/common}/CommentsOverlayWidget.ts +117 -28
- package/front_end/{ui/comments → panels/common}/CommentsStatusBarPill.ts +5 -5
- package/front_end/panels/common/ExtensionServer.ts +2 -1
- package/front_end/panels/common/commentThreadWidget.css +173 -0
- package/front_end/{ui/comments → panels/common}/commentsOverlay.css +8 -1
- package/front_end/{ui/comments/comments-meta.ts → panels/common/common-meta.ts} +11 -11
- package/front_end/panels/common/common.ts +3 -0
- package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +3 -3
- package/front_end/panels/elements/ElementsPanel.ts +5 -1
- package/front_end/panels/elements/ElementsTreeElement.ts +102 -21
- package/front_end/panels/elements/ElementsTreeOutline.ts +326 -91
- package/front_end/panels/elements/EventListenersWidget.ts +0 -1
- package/front_end/panels/elements/PropertiesWidget.ts +11 -10
- package/front_end/panels/elements/PropertyRenderer.ts +29 -25
- package/front_end/panels/elements/StylePropertyTreeElement.ts +6 -6
- package/front_end/panels/elements/elementsTreeOutline.css +7 -1
- package/front_end/panels/emulation/DeviceModeToolbar.ts +2 -3
- package/front_end/panels/event_listeners/EventListenersView.ts +235 -302
- package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorController.ts +6 -7
- package/front_end/panels/linear_memory_inspector/components/linearMemoryInspector.css +5 -1
- package/front_end/panels/linear_memory_inspector/linear_memory_inspector-meta.ts +1 -1
- package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -2
- package/front_end/panels/network/NetworkConfigView.ts +141 -116
- package/front_end/panels/network/NetworkPanel.ts +7 -4
- package/front_end/panels/network/RequestHeadersView.ts +1 -1
- package/front_end/panels/network/forward/BackendLinking.ts +5 -2
- package/front_end/panels/recorder/stepView.css +1 -0
- package/front_end/panels/security/SecurityPanel.ts +5 -5
- package/front_end/panels/security/SecurityPanelSidebar.ts +2 -11
- package/front_end/panels/settings/BackendLinkingSettingsTab.ts +7 -2
- package/front_end/panels/sources/DebuggerPlugin.ts +1 -1
- package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +11 -16
- package/front_end/panels/sources/sources-meta.ts +1 -1
- package/front_end/panels/timeline/TimelineTreeView.ts +11 -1
- package/front_end/panels/timeline/overlays/OverlaysImpl.ts +0 -3
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/comments/CommentOverlayManager.ts +89 -42
- package/front_end/ui/comments/comments.ts +0 -4
- package/front_end/ui/legacy/Treeoutline.ts +4 -0
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +15 -8
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +4 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +5 -5
|
@@ -404,6 +404,8 @@ declare global {
|
|
|
404
404
|
chrome: PublicAPI.Chrome.DevTools.Chrome;
|
|
405
405
|
webInspector?: APIImpl.InspectorExtensionAPI;
|
|
406
406
|
}
|
|
407
|
+
var injectedExtensionAPI: Window['injectedExtensionAPI'];
|
|
408
|
+
var buildExtensionAPIInjectedScript: Window['buildExtensionAPIInjectedScript'];
|
|
407
409
|
}
|
|
408
410
|
|
|
409
411
|
export interface ExtensionDescriptor {
|
|
@@ -511,7 +513,7 @@ namespace APIImpl {
|
|
|
511
513
|
}
|
|
512
514
|
}
|
|
513
515
|
|
|
514
|
-
|
|
516
|
+
globalThis.injectedExtensionAPI = function(
|
|
515
517
|
extensionInfo: ExtensionDescriptor, inspectedTabId: string, themeName: string, keysToForward: number[],
|
|
516
518
|
testHook: (extensionServer: APIImpl.ExtensionServerClient, extensionAPI: APIImpl.InspectorExtensionAPI) => unknown,
|
|
517
519
|
injectedScriptId: number, targetWindowForTest?: Window): void {
|
|
@@ -1763,22 +1765,22 @@ self.injectedExtensionAPI = function(
|
|
|
1763
1765
|
testHook(extensionServer, coreAPI);
|
|
1764
1766
|
};
|
|
1765
1767
|
|
|
1766
|
-
|
|
1768
|
+
globalThis.buildExtensionAPIInjectedScript = function(
|
|
1767
1769
|
extensionInfo: {
|
|
1768
1770
|
startPage: string,
|
|
1769
1771
|
name: string,
|
|
1770
1772
|
exposeExperimentalAPIs: boolean,
|
|
1771
1773
|
},
|
|
1772
1774
|
inspectedTabId: string, themeName: string, keysToForward: number[],
|
|
1773
|
-
testHook:
|
|
1774
|
-
|
|
1775
|
-
undefined): string {
|
|
1775
|
+
testHook: ((extensionServer: APIImpl.ExtensionServerClient, extensionAPI: APIImpl.InspectorExtensionAPI) =>
|
|
1776
|
+
unknown)|undefined): string {
|
|
1776
1777
|
const argumentsJSON =
|
|
1777
1778
|
[extensionInfo, inspectedTabId || null, themeName, keysToForward].map(_ => JSON.stringify(_)).join(',');
|
|
1778
1779
|
if (!testHook) {
|
|
1779
1780
|
testHook = (): void => {};
|
|
1780
1781
|
}
|
|
1781
1782
|
return '(function(injectedScriptId){ ' +
|
|
1782
|
-
'(' +
|
|
1783
|
+
'(' + globalThis.injectedExtensionAPI.toString() + ')(' + argumentsJSON + ',' + testHook +
|
|
1784
|
+
', injectedScriptId);' +
|
|
1783
1785
|
'})';
|
|
1784
1786
|
};
|
|
@@ -2,29 +2,31 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
|
+
import type * as Platform from '../../core/platform/platform.js';
|
|
6
|
+
|
|
5
7
|
interface Response {
|
|
6
8
|
requestId: number;
|
|
7
9
|
result: unknown;
|
|
8
10
|
error: Error|null;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
interface
|
|
13
|
+
interface EventMessage {
|
|
12
14
|
event: string;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
type Message = MessageEvent<Response|Event>;
|
|
16
|
-
|
|
17
17
|
export class ExtensionEndpoint {
|
|
18
|
-
private readonly port:
|
|
18
|
+
private readonly port: Platform.HostRuntime.WorkerMessagePort;
|
|
19
19
|
private nextRequestId = 0;
|
|
20
20
|
private pendingRequests: Map<number, {
|
|
21
21
|
resolve: (arg: unknown) => void,
|
|
22
22
|
reject: (error: Error) => void,
|
|
23
23
|
}>;
|
|
24
24
|
|
|
25
|
-
constructor(port:
|
|
25
|
+
constructor(port: Platform.HostRuntime.WorkerMessagePort) {
|
|
26
26
|
this.port = port;
|
|
27
|
-
this.port.
|
|
27
|
+
this.port.addEventListener('message', (event: unknown) => this.onResponse(event as MessageEvent));
|
|
28
|
+
(this.port as {start?: () => void}).start?.();
|
|
29
|
+
(this.port as {unref?: () => void}).unref?.();
|
|
28
30
|
this.pendingRequests = new Map();
|
|
29
31
|
}
|
|
30
32
|
|
|
@@ -44,7 +46,8 @@ export class ExtensionEndpoint {
|
|
|
44
46
|
this.port.close();
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
private onResponse(
|
|
49
|
+
private onResponse(event: MessageEvent): void {
|
|
50
|
+
const data = event.data as Response | EventMessage;
|
|
48
51
|
if ('event' in data) {
|
|
49
52
|
this.handleEvent(data);
|
|
50
53
|
return;
|
|
@@ -63,7 +66,7 @@ export class ExtensionEndpoint {
|
|
|
63
66
|
}
|
|
64
67
|
}
|
|
65
68
|
|
|
66
|
-
protected handleEvent(_event:
|
|
69
|
+
protected handleEvent(_event: EventMessage): void {
|
|
67
70
|
throw new Error('handleEvent is not implemented');
|
|
68
71
|
}
|
|
69
72
|
}
|
|
@@ -77,8 +77,9 @@ function parseHostAndPort(pattern: string, scheme: string): {host: string, port:
|
|
|
77
77
|
asUrl.hostname = asUrl.hostname.substr(0, asUrl.hostname.length - 1);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
// The URL constructor
|
|
81
|
-
|
|
80
|
+
// The URL constructor in Chrome replaces '*' with '%2A', whereas Node preserves '*'.
|
|
81
|
+
const isWildcardHost = asUrl.hostname === '%2A' || asUrl.hostname === '*';
|
|
82
|
+
if (!isWildcardHost && (asUrl.hostname.includes('%2A') || asUrl.hostname.includes('*'))) {
|
|
82
83
|
return undefined;
|
|
83
84
|
}
|
|
84
85
|
|
|
@@ -93,7 +94,7 @@ function parseHostAndPort(pattern: string, scheme: string): {host: string, port:
|
|
|
93
94
|
return undefined;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
const host =
|
|
97
|
+
const host = !isWildcardHost ? (pattern.startsWith('*.') ? `*.${asUrl.hostname}` : asUrl.hostname) : '*';
|
|
97
98
|
return {
|
|
98
99
|
host,
|
|
99
100
|
port,
|
|
@@ -13,7 +13,7 @@ import {ExtensionEndpoint} from './ExtensionEndpoint.js';
|
|
|
13
13
|
class LanguageExtensionEndpointImpl extends ExtensionEndpoint {
|
|
14
14
|
private plugin: LanguageExtensionEndpoint;
|
|
15
15
|
#pluginManager: Bindings.DebuggerLanguagePlugins.DebuggerLanguagePluginManager;
|
|
16
|
-
constructor(plugin: LanguageExtensionEndpoint, port:
|
|
16
|
+
constructor(plugin: LanguageExtensionEndpoint, port: Platform.HostRuntime.WorkerMessagePort,
|
|
17
17
|
pluginManager: Bindings.DebuggerLanguagePlugins.DebuggerLanguagePluginManager) {
|
|
18
18
|
super(port);
|
|
19
19
|
this.plugin = plugin;
|
|
@@ -46,7 +46,8 @@ export class LanguageExtensionEndpoint implements Bindings.DebuggerLanguagePlugi
|
|
|
46
46
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
47
47
|
symbol_types: string[],
|
|
48
48
|
},
|
|
49
|
-
port:
|
|
49
|
+
port: Platform.HostRuntime.WorkerMessagePort,
|
|
50
|
+
pluginManager: Bindings.DebuggerLanguagePlugins.DebuggerLanguagePluginManager) {
|
|
50
51
|
this.name = name;
|
|
51
52
|
this.extensionOrigin = extensionOrigin;
|
|
52
53
|
this.supportedScriptTypes = supportedScriptTypes;
|
|
@@ -15,7 +15,8 @@ export class RecorderExtensionEndpoint extends ExtensionEndpoint {
|
|
|
15
15
|
readonly #extensionOrigin: Platform.DevToolsPath.UrlString;
|
|
16
16
|
readonly #recorderPluginManager: RecorderPluginManager;
|
|
17
17
|
|
|
18
|
-
constructor(name: string, port:
|
|
18
|
+
constructor(name: string, port: Platform.HostRuntime.WorkerMessagePort,
|
|
19
|
+
capabilities: PrivateAPI.RecordingExtensionPluginCapability[],
|
|
19
20
|
extensionOrigin: Platform.DevToolsPath.UrlString, recorderPluginManager: RecorderPluginManager,
|
|
20
21
|
mediaType?: string) {
|
|
21
22
|
super(port);
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
|
-
import type * as PlatformApi from '../../core/platform/api/api.js';
|
|
6
5
|
import * as Platform from '../../core/platform/platform.js';
|
|
7
6
|
import * as FormatterActions from '../../entrypoints/formatter_actions/formatter_actions.js';
|
|
8
7
|
|
|
@@ -12,7 +11,7 @@ let formatterWorkerPoolInstance: FormatterWorkerPool|undefined;
|
|
|
12
11
|
|
|
13
12
|
export class FormatterWorkerPool {
|
|
14
13
|
private taskQueue: Task[];
|
|
15
|
-
private workerTasks: Map<
|
|
14
|
+
private workerTasks: Map<Platform.HostRuntime.Worker, Task|null>;
|
|
16
15
|
private entrypointURL: string;
|
|
17
16
|
|
|
18
17
|
constructor(entrypointURL?: string) {
|
|
@@ -46,7 +45,7 @@ export class FormatterWorkerPool {
|
|
|
46
45
|
formatterWorkerPoolInstance = undefined;
|
|
47
46
|
}
|
|
48
47
|
|
|
49
|
-
private createWorker():
|
|
48
|
+
private createWorker(): Platform.HostRuntime.Worker {
|
|
50
49
|
const worker = Platform.HostRuntime.HOST_RUNTIME.createWorker(this.entrypointURL);
|
|
51
50
|
worker.onmessage = this.onWorkerMessage.bind(this, worker);
|
|
52
51
|
worker.onerror = this.onWorkerError.bind(this, worker);
|
|
@@ -75,8 +74,7 @@ export class FormatterWorkerPool {
|
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
76
|
|
|
78
|
-
private onWorkerMessage(worker:
|
|
79
|
-
void {
|
|
77
|
+
private onWorkerMessage(worker: Platform.HostRuntime.Worker, event: Platform.HostRuntime.WorkerMessageEvent): void {
|
|
80
78
|
const task = this.workerTasks.get(worker);
|
|
81
79
|
if (!task) {
|
|
82
80
|
return;
|
|
@@ -91,7 +89,7 @@ export class FormatterWorkerPool {
|
|
|
91
89
|
task.callback(event.data ? event.data : null);
|
|
92
90
|
}
|
|
93
91
|
|
|
94
|
-
private onWorkerError(worker:
|
|
92
|
+
private onWorkerError(worker: Platform.HostRuntime.Worker, event: Event): void {
|
|
95
93
|
console.error(event);
|
|
96
94
|
const task = this.workerTasks.get(worker);
|
|
97
95
|
worker.terminate();
|
|
@@ -363,6 +363,61 @@ export interface ObjectInfo {
|
|
|
363
363
|
retainerCount: number;
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
+
export interface ContextAnalysisResult {
|
|
367
|
+
/** Scopes with dead fields, sorted by their highest-ranked context. */
|
|
368
|
+
scopes: ScopeAnalysis[];
|
|
369
|
+
/** Scripts with live contexts that could not be analyzed because they lack embedded scope metadata. */
|
|
370
|
+
scriptsWithoutScopes: ScriptWithoutScopes[];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export interface ScopeAnalysis {
|
|
374
|
+
scopeInfoNodeIndex: number;
|
|
375
|
+
scopeInfoNodeId: number;
|
|
376
|
+
scriptNodeIndex: number;
|
|
377
|
+
scriptNodeId: number;
|
|
378
|
+
scriptName: string;
|
|
379
|
+
/** Source name of the scope, when available. */
|
|
380
|
+
scopeName?: string;
|
|
381
|
+
scopeStart: number;
|
|
382
|
+
scopeEnd: number;
|
|
383
|
+
/** Number of context-typed fields in each context belonging to this scope. */
|
|
384
|
+
contextFieldCount: number;
|
|
385
|
+
/** Contexts with dead fields, sorted descending by deadFieldsRetainedSizeSum. */
|
|
386
|
+
contexts: ContextAnalysis[];
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export interface ContextAnalysis {
|
|
390
|
+
contextNodeIndex: number;
|
|
391
|
+
contextNodeId: number;
|
|
392
|
+
retainedSize: number;
|
|
393
|
+
/**
|
|
394
|
+
* The sum of the retained sizes of values in fields classified as dead.
|
|
395
|
+
* This is a ranking heuristic, not the number of bytes that would be
|
|
396
|
+
* reclaimed by clearing the fields.
|
|
397
|
+
*/
|
|
398
|
+
deadFieldsRetainedSizeSum: number;
|
|
399
|
+
/** Fields classified as dead in this context, sorted descending by retainedSize. */
|
|
400
|
+
deadFields: ContextField[];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export interface ContextField {
|
|
404
|
+
name: string;
|
|
405
|
+
valueNodeIndex: number;
|
|
406
|
+
valueNodeId: number;
|
|
407
|
+
valueName: string;
|
|
408
|
+
valueType: string;
|
|
409
|
+
selfSize: number;
|
|
410
|
+
retainedSize: number;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export interface ScriptWithoutScopes {
|
|
414
|
+
scriptNodeIndex: number;
|
|
415
|
+
scriptNodeId: number;
|
|
416
|
+
scriptName: string;
|
|
417
|
+
/** Number of live context instances belonging to this script. */
|
|
418
|
+
contextCount: number;
|
|
419
|
+
}
|
|
420
|
+
|
|
366
421
|
export interface HeapQueryOptions {
|
|
367
422
|
className?: string;
|
|
368
423
|
propertyName?: string;
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
5
|
import * as Common from '../../core/common/common.js';
|
|
6
|
-
import type * as PlatformApi from '../../core/platform/api/api.js';
|
|
7
6
|
import * as Platform from '../../core/platform/platform.js';
|
|
8
7
|
|
|
9
8
|
import type {ChildrenProvider} from './ChildrenProvider.js';
|
|
@@ -17,7 +16,7 @@ export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<
|
|
|
17
16
|
callbacks: Map<number, (error?: string, result?: unknown) => void> =
|
|
18
17
|
new Map<number, (error?: string, result?: unknown) => void>();
|
|
19
18
|
readonly previousCallbacks: Set<number> = new Set<number>();
|
|
20
|
-
readonly worker:
|
|
19
|
+
readonly worker: Platform.HostRuntime.Worker;
|
|
21
20
|
interval?: ReturnType<typeof setInterval>;
|
|
22
21
|
readonly workerUrl?: string;
|
|
23
22
|
|
|
@@ -69,17 +68,18 @@ export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<
|
|
|
69
68
|
this.postMessage({callId, disposition: 'evaluateForTest', source: script});
|
|
70
69
|
}
|
|
71
70
|
|
|
72
|
-
callFactoryMethod<T extends Object>(
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
callFactoryMethod<T extends Object>(callback: null, objectId: string, methodName: string,
|
|
72
|
+
proxyConstructor: new(...arg1: any[]) => T,
|
|
73
|
+
transfer: Platform.HostRuntime.WorkerTransferable[],
|
|
74
|
+
...methodArguments: any[]): T;
|
|
75
75
|
callFactoryMethod<T extends Object>(callback: ((error?: string, result?: T) => void), objectId: string,
|
|
76
76
|
methodName: string, proxyConstructor: new(...arg1: any[]) => T,
|
|
77
|
-
transfer:
|
|
77
|
+
transfer: Platform.HostRuntime.WorkerTransferable[],
|
|
78
78
|
...methodArguments: any[]): null;
|
|
79
79
|
callFactoryMethod<T extends Object>(callback: ((error?: string, result?: T) => void)|null, objectId: string,
|
|
80
80
|
methodName: string, proxyConstructor: new(...arg1: any[]) => T,
|
|
81
|
-
transfer:
|
|
82
|
-
|
|
81
|
+
transfer: Platform.HostRuntime.WorkerTransferable[], ...methodArguments: any[]): T
|
|
82
|
+
|null {
|
|
83
83
|
const callId = this.nextCallId++;
|
|
84
84
|
const newObjectId = this.nextObjectId++;
|
|
85
85
|
|
|
@@ -154,7 +154,7 @@ export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<
|
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
setupForSecondaryInit(port:
|
|
157
|
+
setupForSecondaryInit(port: Platform.HostRuntime.WorkerMessagePort): Promise<void> {
|
|
158
158
|
const callId = this.nextCallId++;
|
|
159
159
|
const done = new Promise<void>((resolve, reject) => {
|
|
160
160
|
this.callbacks.set(callId, error => {
|
|
@@ -175,7 +175,7 @@ export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<
|
|
|
175
175
|
return done;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
messageReceived(event:
|
|
178
|
+
messageReceived(event: Platform.HostRuntime.WorkerMessageEvent): void {
|
|
179
179
|
const data = event.data;
|
|
180
180
|
if (data.eventName) {
|
|
181
181
|
if (this.eventHandler) {
|
|
@@ -199,7 +199,7 @@ export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<
|
|
|
199
199
|
callback(undefined, data.result);
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
postMessage(message: unknown, transfer?:
|
|
202
|
+
postMessage(message: unknown, transfer?: Platform.HostRuntime.WorkerTransferable[]): void {
|
|
203
203
|
this.worker.postMessage(message, transfer);
|
|
204
204
|
}
|
|
205
205
|
}
|
|
@@ -231,9 +231,9 @@ export class HeapSnapshotProxyObject {
|
|
|
231
231
|
return this.worker.callFactoryMethod(null, String(this.objectId), methodName, proxyConstructor, [], ...args);
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
callFactoryMethodPromise<T extends Object>(
|
|
235
|
-
|
|
236
|
-
|
|
234
|
+
callFactoryMethodPromise<T extends Object>(methodName: string, proxyConstructor: new(...arg1: any[]) => T,
|
|
235
|
+
transfer: Platform.HostRuntime.WorkerTransferable[],
|
|
236
|
+
...args: any[]): Promise<T> {
|
|
237
237
|
return new Promise((resolve, reject) => this.worker.callFactoryMethod((error, result) => {
|
|
238
238
|
if (error) {
|
|
239
239
|
reject(new Error(error));
|
|
@@ -348,6 +348,10 @@ export class HeapSnapshotProxy extends HeapSnapshotProxyObject {
|
|
|
348
348
|
return this.callMethodPromise('getObjectInfo', nodeIndex);
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
+
analyzeContexts(): Promise<HeapSnapshotModel.ContextAnalysisResult> {
|
|
352
|
+
return this.callMethodPromise('analyzeContexts');
|
|
353
|
+
}
|
|
354
|
+
|
|
351
355
|
createEdgesProvider(nodeIndex: number, options?: HeapSnapshotModel.HeapEdgesQueryOptions): HeapSnapshotProviderProxy {
|
|
352
356
|
return this.callFactoryMethod('createEdgesProvider', HeapSnapshotProviderProxy, nodeIndex, options);
|
|
353
357
|
}
|
|
@@ -38,6 +38,7 @@ import {SourceFrameIssuesManager} from './SourceFrameIssuesManager.js';
|
|
|
38
38
|
import {SRIMessageSignatureIssue} from './SRIMessageSignatureIssue.js';
|
|
39
39
|
import {StylesheetLoadingIssue} from './StylesheetLoadingIssue.js';
|
|
40
40
|
import {UnencodedDigestIssue} from './UnencodedDigestIssue.js';
|
|
41
|
+
import {WebInstallIssue} from './WebInstallIssue.js';
|
|
41
42
|
|
|
42
43
|
export {Events} from './IssuesManagerEvents.js';
|
|
43
44
|
|
|
@@ -149,6 +150,10 @@ const issueCodeHandlers =
|
|
|
149
150
|
Protocol.Audits.InspectorIssueCode.ConnectionAllowlistIssue,
|
|
150
151
|
ConnectionAllowlistIssue.fromInspectorIssue,
|
|
151
152
|
],
|
|
153
|
+
[
|
|
154
|
+
Protocol.Audits.InspectorIssueCode.WebInstallIssue,
|
|
155
|
+
WebInstallIssue.fromInspectorIssue,
|
|
156
|
+
],
|
|
152
157
|
[
|
|
153
158
|
Protocol.Audits.InspectorIssueCode.PermissionElementIssue,
|
|
154
159
|
PermissionElementIssue.fromInspectorIssue,
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Copyright 2026 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
import type * as SDK from '../../core/sdk/sdk.js';
|
|
6
|
+
import * as Protocol from '../../generated/protocol.js';
|
|
7
|
+
|
|
8
|
+
import {Issue, IssueCategory, IssueKind} from './Issue.js';
|
|
9
|
+
import type {MarkdownIssueDescription} from './MarkdownIssueDescription.js';
|
|
10
|
+
|
|
11
|
+
export class WebInstallIssue extends Issue<Protocol.Audits.WebInstallIssueDetails> {
|
|
12
|
+
constructor(issueDetails: Protocol.Audits.WebInstallIssueDetails, issuesModel: SDK.IssuesModel.IssuesModel|null) {
|
|
13
|
+
super(`${Protocol.Audits.InspectorIssueCode.WebInstallIssue}::${issueDetails.reason}`, issueDetails, issuesModel);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
override requests(): Protocol.Audits.AffectedRequest[] {
|
|
17
|
+
const {manifestUrl, reason} = this.details();
|
|
18
|
+
if (reason === Protocol.Audits.WebInstallIssueReason.NoManifest || !manifestUrl) {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
return [{url: manifestUrl}];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getCategory(): IssueCategory {
|
|
25
|
+
return IssueCategory.OTHER;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getDescription(): MarkdownIssueDescription|null {
|
|
29
|
+
switch (this.details().reason) {
|
|
30
|
+
case Protocol.Audits.WebInstallIssueReason.ManifestParsingOrNetworkError:
|
|
31
|
+
return {file: 'webInstallManifestParsingOrNetworkError.md', links: []};
|
|
32
|
+
case Protocol.Audits.WebInstallIssueReason.StartUrlInvalid:
|
|
33
|
+
return {file: 'webInstallStartUrlInvalid.md', links: []};
|
|
34
|
+
case Protocol.Audits.WebInstallIssueReason.ManifestMissingNameOrShortName:
|
|
35
|
+
return {file: 'webInstallManifestMissingNameOrShortName.md', links: []};
|
|
36
|
+
case Protocol.Audits.WebInstallIssueReason.ManifestMissingId:
|
|
37
|
+
return {file: 'webInstallManifestMissingId.md', links: []};
|
|
38
|
+
case Protocol.Audits.WebInstallIssueReason.NoManifest:
|
|
39
|
+
return {file: 'webInstallNoManifest.md', links: []};
|
|
40
|
+
default:
|
|
41
|
+
console.warn('Unknown WebInstallIssueReason:', this.details().reason);
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getKind(): IssueKind {
|
|
47
|
+
return IssueKind.PAGE_ERROR;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
primaryKey(): string {
|
|
51
|
+
return JSON.stringify(this.details());
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static fromInspectorIssue(issuesModel: SDK.IssuesModel.IssuesModel|null,
|
|
55
|
+
inspectorIssue: Protocol.Audits.InspectorIssue): WebInstallIssue[] {
|
|
56
|
+
const details = inspectorIssue.details.webInstallIssueDetails;
|
|
57
|
+
if (!details) {
|
|
58
|
+
console.warn('Web install issue without details received.');
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
return [new WebInstallIssue(details, issuesModel)];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -36,6 +36,7 @@ import * as SourceFrameIssuesManager from './SourceFrameIssuesManager.js';
|
|
|
36
36
|
import * as SRIMessageSignatureIssue from './SRIMessageSignatureIssue.js';
|
|
37
37
|
import * as StylesheetLoadingIssue from './StylesheetLoadingIssue.js';
|
|
38
38
|
import * as UnencodedDigestIssue from './UnencodedDigestIssue.js';
|
|
39
|
+
import * as WebInstallIssue from './WebInstallIssue.js';
|
|
39
40
|
|
|
40
41
|
export {
|
|
41
42
|
ClientHintIssue,
|
|
@@ -72,4 +73,5 @@ export {
|
|
|
72
73
|
SRIMessageSignatureIssue,
|
|
73
74
|
StylesheetLoadingIssue,
|
|
74
75
|
UnencodedDigestIssue,
|
|
76
|
+
WebInstallIssue,
|
|
75
77
|
};
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import * as Common from '../../core/common/common.js';
|
|
8
8
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
9
9
|
import * as Platform from '../../core/platform/platform.js';
|
|
10
|
+
import * as SDK from '../../core/sdk/sdk.js';
|
|
10
11
|
import * as TextUtils from '../../core/text_utils/text_utils.js';
|
|
11
12
|
|
|
12
13
|
import {IgnoreListManager} from './IgnoreListManager.js';
|
|
@@ -29,6 +30,7 @@ export class UISourceCode extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
|
29
30
|
TextUtils.ContentProvider.ContentProvider {
|
|
30
31
|
readonly #origin: Platform.DevToolsPath.UrlString;
|
|
31
32
|
readonly #parentURL: Platform.DevToolsPath.UrlString;
|
|
33
|
+
#securityOrigin?: SDK.SecurityOrigin.SecurityOrigin;
|
|
32
34
|
#project: Project;
|
|
33
35
|
#url: Platform.DevToolsPath.UrlString;
|
|
34
36
|
#name: string;
|
|
@@ -110,6 +112,23 @@ export class UISourceCode extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
|
110
112
|
return this.#origin;
|
|
111
113
|
}
|
|
112
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Returns the security origin for this source code.
|
|
117
|
+
* Prefers the project security origin if available. If the project does not
|
|
118
|
+
* define a security origin, derives it from the source code's URL and caches
|
|
119
|
+
* the result until the source code is renamed.
|
|
120
|
+
*/
|
|
121
|
+
securityOrigin(): SDK.SecurityOrigin.SecurityOrigin {
|
|
122
|
+
const projectOrigin = this.#project.securityOrigin?.();
|
|
123
|
+
if (projectOrigin) {
|
|
124
|
+
return projectOrigin;
|
|
125
|
+
}
|
|
126
|
+
if (!this.#securityOrigin) {
|
|
127
|
+
this.#securityOrigin = SDK.SecurityOrigin.SecurityOrigin.create(this.#url);
|
|
128
|
+
}
|
|
129
|
+
return this.#securityOrigin;
|
|
130
|
+
}
|
|
131
|
+
|
|
113
132
|
fullDisplayName(): string {
|
|
114
133
|
return this.#project.fullDisplayName(this);
|
|
115
134
|
}
|
|
@@ -150,6 +169,7 @@ export class UISourceCode extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
|
150
169
|
#updateName(
|
|
151
170
|
name: Platform.DevToolsPath.RawPathString, url: Platform.DevToolsPath.UrlString,
|
|
152
171
|
contentType?: Common.ResourceType.ResourceType): void {
|
|
172
|
+
this.#securityOrigin = undefined;
|
|
153
173
|
const oldURL = this.#url;
|
|
154
174
|
this.#name = name;
|
|
155
175
|
if (url) {
|
|
@@ -282,12 +282,10 @@ async function getEmptyStateSuggestions(conversation?: AiAssistanceModel.AiConve
|
|
|
282
282
|
}
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
function createV2MarkdownRenderer(conversation
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
options.getEstablishedOrigin = () => conversation.origin;
|
|
290
|
-
}
|
|
285
|
+
function createV2MarkdownRenderer(conversation: AiAssistanceModel.AiConversation.AiConversation): AIv2MarkdownRenderer {
|
|
286
|
+
const options: AIv2MarkdownRendererOptions = {
|
|
287
|
+
getOriginLock: () => conversation.getOriginLock(),
|
|
288
|
+
};
|
|
291
289
|
const primaryTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
|
|
292
290
|
const domModel = primaryTarget?.model(SDK.DOMModel.DOMModel);
|
|
293
291
|
const resourceTreeModel = primaryTarget?.model(SDK.ResourceTreeModel.ResourceTreeModel);
|
|
@@ -337,7 +335,9 @@ function getMarkdownRenderer(conversation?: AiAssistanceModel.AiConversation.AiC
|
|
|
337
335
|
return new AccessibilityAgentMarkdownRenderer(mainDocumentURL);
|
|
338
336
|
}
|
|
339
337
|
|
|
340
|
-
return new MarkdownRendererWithCodeBlock(
|
|
338
|
+
return new MarkdownRendererWithCodeBlock({
|
|
339
|
+
getEstablishedOrigin: () => conversation?.origin,
|
|
340
|
+
});
|
|
341
341
|
}
|
|
342
342
|
|
|
343
343
|
interface ToolbarViewInput {
|
|
@@ -21,13 +21,10 @@ export interface AIv2MarkdownRendererOptions {
|
|
|
21
21
|
mainFrameId?: string;
|
|
22
22
|
mainDocumentURL?: Platform.DevToolsPath.UrlString;
|
|
23
23
|
/**
|
|
24
|
-
* Retrieves the
|
|
24
|
+
* Retrieves the origin lock for the active conversation.
|
|
25
25
|
* Required to authorize #file-<id> links and prevent cross-origin file leakage.
|
|
26
|
-
*
|
|
27
|
-
* TODO(crbug.com/559522248): Defer renderer instantiation until a conversation
|
|
28
|
-
* starts so this origin can be passed as a mandatory, non-getter property.
|
|
29
26
|
*/
|
|
30
|
-
|
|
27
|
+
getOriginLock: () => AiAssistanceModel.Tool.OriginLockState;
|
|
31
28
|
lookupTraceEvent?: (key: string) => Trace.Types.Events.Event | null;
|
|
32
29
|
}
|
|
33
30
|
|
|
@@ -45,7 +42,7 @@ type ParsedLink = {
|
|
|
45
42
|
* the only markdown renderer used by AI assistance.
|
|
46
43
|
*/
|
|
47
44
|
export class AIv2MarkdownRenderer extends MarkdownView.MarkdownView.MarkdownInsightRenderer {
|
|
48
|
-
constructor(private readonly options: AIv2MarkdownRendererOptions
|
|
45
|
+
constructor(private readonly options: AIv2MarkdownRendererOptions) {
|
|
49
46
|
super();
|
|
50
47
|
}
|
|
51
48
|
|
|
@@ -119,10 +116,7 @@ export class AIv2MarkdownRenderer extends MarkdownView.MarkdownView.MarkdownInsi
|
|
|
119
116
|
}
|
|
120
117
|
if (href.startsWith('#file-')) {
|
|
121
118
|
const fileId = Number(href.substring(6));
|
|
122
|
-
const
|
|
123
|
-
const file = (origin && Number.isInteger(fileId) && fileId > 0) ?
|
|
124
|
-
AiAssistanceModel.ListSources.ListSourcesTool.getSourceById(fileId, origin) :
|
|
125
|
-
undefined;
|
|
119
|
+
const file = AiAssistanceModel.ListSources.ListSourcesTool.getSourceById(fileId, this.options.getOriginLock());
|
|
126
120
|
|
|
127
121
|
if (file) {
|
|
128
122
|
return this.#revealableLink(file, file.name());
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Common from '../../../core/common/common.js';
|
|
6
6
|
import * as Platform from '../../../core/platform/platform.js';
|
|
7
|
+
import type * as SDK from '../../../core/sdk/sdk.js';
|
|
7
8
|
import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
|
|
8
9
|
import * as Logs from '../../../models/logs/logs.js';
|
|
9
10
|
import type * as Marked from '../../../third_party/marked/marked.js';
|
|
@@ -12,7 +13,18 @@ import * as Lit from '../../../ui/lit/lit.js';
|
|
|
12
13
|
|
|
13
14
|
const {html} = Lit;
|
|
14
15
|
|
|
16
|
+
export interface MarkdownRendererWithCodeBlockOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Retrieves the established origin locked for the active conversation.
|
|
19
|
+
* Required to authorize #file-<id> links and prevent cross-origin file leakage.
|
|
20
|
+
* When omitted or unauthorized, file links safely render as plain inert text.
|
|
21
|
+
*/
|
|
22
|
+
getEstablishedOrigin?: () => SDK.SecurityOrigin.SecurityOrigin | undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
/**
|
|
26
|
+
* Markdown renderer for AI assistance conversations.
|
|
27
|
+
*
|
|
16
28
|
* The model returns multiline code blocks in an erroneous way with the language being in new line.
|
|
17
29
|
* This renderer takes that into account and correctly updates the parsed multiline token with the language
|
|
18
30
|
* correctly identified and stripped from the content.
|
|
@@ -23,8 +35,13 @@ const {html} = Lit;
|
|
|
23
35
|
* color: red;
|
|
24
36
|
* }
|
|
25
37
|
* ```
|
|
26
|
-
|
|
38
|
+
*
|
|
39
|
+
* Also handles linkifying DevTools internal resource links (#req- and origin-locked #file-).
|
|
40
|
+
*/
|
|
27
41
|
export class MarkdownRendererWithCodeBlock extends MarkdownView.MarkdownView.MarkdownInsightRenderer {
|
|
42
|
+
constructor(private readonly options: MarkdownRendererWithCodeBlockOptions = {}) {
|
|
43
|
+
super();
|
|
44
|
+
}
|
|
28
45
|
#revealableLink(revealable: unknown, label: string): Lit.LitTemplate {
|
|
29
46
|
return html`<devtools-link @click=${(e: Event) => {
|
|
30
47
|
e.preventDefault();
|
|
@@ -48,13 +65,16 @@ export class MarkdownRendererWithCodeBlock extends MarkdownView.MarkdownView.Mar
|
|
|
48
65
|
return html`${fallbackText}`;
|
|
49
66
|
}
|
|
50
67
|
if (href.startsWith('#file-')) {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
const fileId = Number(href.substring(6));
|
|
69
|
+
const file = AiAssistanceModel.ContextSelectionAgent.ContextSelectionAgent.getSourceById(
|
|
70
|
+
fileId,
|
|
71
|
+
this.options.getEstablishedOrigin?.(),
|
|
72
|
+
);
|
|
54
73
|
|
|
55
74
|
if (file) {
|
|
56
75
|
return this.#revealableLink(file, file.name());
|
|
57
76
|
}
|
|
77
|
+
// Fall back to plain text to prevent rendering unauthorized or unverified links.
|
|
58
78
|
return html`${fallbackText}`;
|
|
59
79
|
}
|
|
60
80
|
return null;
|
|
@@ -78,7 +98,7 @@ export class MarkdownRendererWithCodeBlock extends MarkdownView.MarkdownView.Mar
|
|
|
78
98
|
|
|
79
99
|
if (token.type === 'codespan') {
|
|
80
100
|
// LLM likes outputting the link inside a codespan block.
|
|
81
|
-
// Remove the codespan and render the link directly
|
|
101
|
+
// Remove the codespan and render the link directly.
|
|
82
102
|
const matches = token.text.match(/^\[(.*)\]\((.+)\)$/);
|
|
83
103
|
if (matches?.[2]) {
|
|
84
104
|
const link = this.#renderLink(
|