@openforge-app/plugin-sdk 0.2.6 → 0.2.8
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/dist/browserSurfaces.d.ts +4 -0
- package/dist/browserSurfacesTesting.d.ts +3 -2
- package/dist/browserSurfacesTesting.js +19 -1
- package/dist/collapsibleSectionState.d.ts +10 -0
- package/dist/collapsibleSectionState.js +81 -0
- package/dist/domain.d.ts +1 -13
- package/dist/frontend.d.ts +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/publicUiExports.mjs +1 -0
- package/dist/testing/backendServicesFake.d.ts +1 -1
- package/dist/testing/backendServicesFake.js +1 -1
- package/dist/testing/commonApiFake.d.ts +1 -1
- package/dist/testing/commonApiFake.js +2 -2
- package/dist/testing/frontendContributionFake.d.ts +2 -2
- package/dist/testing/frontendContributionFake.js +3 -3
- package/dist/testing/registryFake.d.ts +1 -1
- package/dist/testing/registryFake.js +4 -4
- package/dist/testing.d.ts +3 -3
- package/dist/testing.js +2 -2
- package/dist/types.d.ts +7 -0
- package/dist/types.js +1 -1
- package/dist/ui/CollapsibleSection.svelte +79 -0
- package/dist/ui/MarkdownContent.svelte +94 -13
- package/dist/vite.js +1 -0
- package/package.json +7 -2
|
@@ -9,12 +9,14 @@ export interface BrowserSurfaceNavigationError {
|
|
|
9
9
|
message: string;
|
|
10
10
|
url: string;
|
|
11
11
|
}
|
|
12
|
+
export type BrowserDevToolsPanel = 'elements' | 'console';
|
|
12
13
|
export interface TaskBrowserSurfaceState {
|
|
13
14
|
url: string;
|
|
14
15
|
title: string;
|
|
15
16
|
loading: boolean;
|
|
16
17
|
canGoBack: boolean;
|
|
17
18
|
canGoForward: boolean;
|
|
19
|
+
devToolsOpen: boolean;
|
|
18
20
|
error: BrowserSurfaceNavigationError | null;
|
|
19
21
|
}
|
|
20
22
|
export interface BrowserSurfaceCapture {
|
|
@@ -63,6 +65,8 @@ export interface TaskBrowserSurfaceController {
|
|
|
63
65
|
goForward(): Promise<TaskBrowserSurfaceState>;
|
|
64
66
|
reload(): Promise<TaskBrowserSurfaceState>;
|
|
65
67
|
stop(): Promise<TaskBrowserSurfaceState>;
|
|
68
|
+
openDevTools(panel?: BrowserDevToolsPanel): Promise<TaskBrowserSurfaceState>;
|
|
69
|
+
closeDevTools(): Promise<TaskBrowserSurfaceState>;
|
|
66
70
|
selectVisibleRegion(): Promise<BrowserSurfaceFeedbackSelection | null>;
|
|
67
71
|
cancelVisibleRegionSelection(): Promise<void>;
|
|
68
72
|
clearVisualFeedback(): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BrowserSurfacesAPI, BrowserSurfaceVisualFeedback, GetOrCreateBrowserSurfaceRequest, TaskBrowserSurfaceState } from './browserSurfaces';
|
|
1
|
+
import type { BrowserSurfacesAPI, BrowserDevToolsPanel, BrowserSurfaceVisualFeedback, GetOrCreateBrowserSurfaceRequest, TaskBrowserSurfaceState } from './browserSurfaces';
|
|
2
2
|
export interface TestingBrowserSurfaceCalls {
|
|
3
3
|
browserSurfaceGetOrCreate: GetOrCreateBrowserSurfaceRequest[];
|
|
4
4
|
browserSurfaceAttachments: Array<{
|
|
@@ -22,7 +22,8 @@ export interface TestingBrowserSurfaceCalls {
|
|
|
22
22
|
browserSurfaceControls: Array<{
|
|
23
23
|
taskId: string;
|
|
24
24
|
id: string;
|
|
25
|
-
action: 'goBack' | 'goForward' | 'reload' | 'stop';
|
|
25
|
+
action: 'goBack' | 'goForward' | 'reload' | 'stop' | 'openDevTools' | 'closeDevTools';
|
|
26
|
+
panel?: BrowserDevToolsPanel;
|
|
26
27
|
}>;
|
|
27
28
|
browserSurfaceSelections: Array<{
|
|
28
29
|
taskId: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BrowserSurfaceError, isAllowedBrowserSurfaceUrl, } from './browserSurfaces';
|
|
1
|
+
import { BrowserSurfaceError, isAllowedBrowserSurfaceUrl, } from './browserSurfaces.js';
|
|
2
2
|
function disposable(dispose) {
|
|
3
3
|
let disposed = false;
|
|
4
4
|
return {
|
|
@@ -49,6 +49,7 @@ class TestingTaskBrowserSurface {
|
|
|
49
49
|
loading: false,
|
|
50
50
|
canGoBack: false,
|
|
51
51
|
canGoForward: false,
|
|
52
|
+
devToolsOpen: false,
|
|
52
53
|
error: null,
|
|
53
54
|
};
|
|
54
55
|
}
|
|
@@ -124,6 +125,23 @@ class TestingTaskBrowserSurface {
|
|
|
124
125
|
this.publish({ loading: false });
|
|
125
126
|
return this.getState();
|
|
126
127
|
}
|
|
128
|
+
async openDevTools(panel) {
|
|
129
|
+
this.assertLive();
|
|
130
|
+
this.calls.browserSurfaceControls.push({
|
|
131
|
+
taskId: this.taskId,
|
|
132
|
+
id: this.id,
|
|
133
|
+
action: 'openDevTools',
|
|
134
|
+
...(panel ? { panel } : {}),
|
|
135
|
+
});
|
|
136
|
+
this.publish({ devToolsOpen: true });
|
|
137
|
+
return this.getState();
|
|
138
|
+
}
|
|
139
|
+
async closeDevTools() {
|
|
140
|
+
this.assertLive();
|
|
141
|
+
this.calls.browserSurfaceControls.push({ taskId: this.taskId, id: this.id, action: 'closeDevTools' });
|
|
142
|
+
this.publish({ devToolsOpen: false });
|
|
143
|
+
return this.getState();
|
|
144
|
+
}
|
|
127
145
|
async selectVisibleRegion() {
|
|
128
146
|
this.assertLive();
|
|
129
147
|
this.calls.browserSurfaceSelections.push({ taskId: this.taskId, id: this.id });
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type CollapsedSectionsState = Record<string, boolean>;
|
|
2
|
+
export declare const COLLAPSED_SECTIONS_STORAGE_KEY = "openforge.infoPanelSectionCollapse.v1";
|
|
3
|
+
export declare function pluginSectionKey(pluginId: string, sectionKey: string): string;
|
|
4
|
+
export declare const collapsedSections: {
|
|
5
|
+
subscribe: (this: void, run: import("svelte/store").Subscriber<CollapsedSectionsState>, invalidate?: () => void) => import("svelte/store").Unsubscriber;
|
|
6
|
+
};
|
|
7
|
+
export declare function isSectionCollapsed(state: CollapsedSectionsState, key: string): boolean;
|
|
8
|
+
export declare function setSectionCollapsed(key: string, collapsed: boolean): void;
|
|
9
|
+
export declare function toggleSection(key: string): void;
|
|
10
|
+
export declare function clearCollapsedSections(): void;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { writable } from 'svelte/store';
|
|
2
|
+
// The store started life in the host app as src/lib/infoPanelSectionState.ts. The key is
|
|
3
|
+
// deliberately unchanged: renaming it would reset every user's collapsed sections.
|
|
4
|
+
export const COLLAPSED_SECTIONS_STORAGE_KEY = 'openforge.infoPanelSectionCollapse.v1';
|
|
5
|
+
export function pluginSectionKey(pluginId, sectionKey) {
|
|
6
|
+
return `plugin:${pluginId}:${sectionKey}`;
|
|
7
|
+
}
|
|
8
|
+
function getLocalStorage() {
|
|
9
|
+
try {
|
|
10
|
+
return globalThis.localStorage ?? null;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function readPersisted() {
|
|
17
|
+
const storage = getLocalStorage();
|
|
18
|
+
if (storage === null)
|
|
19
|
+
return {};
|
|
20
|
+
try {
|
|
21
|
+
const rawValue = storage.getItem(COLLAPSED_SECTIONS_STORAGE_KEY);
|
|
22
|
+
if (rawValue === null)
|
|
23
|
+
return {};
|
|
24
|
+
const parsed = JSON.parse(rawValue);
|
|
25
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
26
|
+
return {};
|
|
27
|
+
const state = {};
|
|
28
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
29
|
+
if (value === true)
|
|
30
|
+
state[key] = true;
|
|
31
|
+
}
|
|
32
|
+
return state;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function writePersisted(state) {
|
|
39
|
+
const storage = getLocalStorage();
|
|
40
|
+
if (storage === null)
|
|
41
|
+
return;
|
|
42
|
+
try {
|
|
43
|
+
// Only persist collapsed sections; expanded is the default and needs no entry.
|
|
44
|
+
const collapsedOnly = {};
|
|
45
|
+
for (const [key, value] of Object.entries(state)) {
|
|
46
|
+
if (value === true)
|
|
47
|
+
collapsedOnly[key] = true;
|
|
48
|
+
}
|
|
49
|
+
if (Object.keys(collapsedOnly).length === 0) {
|
|
50
|
+
storage.removeItem(COLLAPSED_SECTIONS_STORAGE_KEY);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
storage.setItem(COLLAPSED_SECTIONS_STORAGE_KEY, JSON.stringify(collapsedOnly));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Persistence is best effort and must not block the section from rendering.
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const { subscribe, set, update } = writable(readPersisted());
|
|
60
|
+
export const collapsedSections = { subscribe };
|
|
61
|
+
export function isSectionCollapsed(state, key) {
|
|
62
|
+
return state[key] === true;
|
|
63
|
+
}
|
|
64
|
+
export function setSectionCollapsed(key, collapsed) {
|
|
65
|
+
update((current) => {
|
|
66
|
+
const next = { ...current, [key]: collapsed };
|
|
67
|
+
writePersisted(next);
|
|
68
|
+
return next;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
export function toggleSection(key) {
|
|
72
|
+
update((current) => {
|
|
73
|
+
const next = { ...current, [key]: !isSectionCollapsed(current, key) };
|
|
74
|
+
writePersisted(next);
|
|
75
|
+
return next;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
export function clearCollapsedSections() {
|
|
79
|
+
writePersisted({});
|
|
80
|
+
set({});
|
|
81
|
+
}
|
package/dist/domain.d.ts
CHANGED
|
@@ -314,7 +314,7 @@ export interface ProjectAttention {
|
|
|
314
314
|
unaddressed_comments: number;
|
|
315
315
|
completed_agents: number;
|
|
316
316
|
}
|
|
317
|
-
export type TaskAttentionState = 'idle' | 'needs-input' | 'paused' | 'agent-done' | 'failed' | 'interrupted' | 'pr-draft' | 'pr-open' | 'ci-failed' | 'changes-requested' | 'unaddressed-comments' | 'ready-to-merge' | 'ready-to-enqueue' | 'pr-queued' | 'pr-merged' | 'pr-closed' | 'ci-running' | 'review-pending' | 'merge-conflict';
|
|
317
|
+
export type TaskAttentionState = 'active' | 'backlog' | 'idle' | 'needs-input' | 'paused' | 'agent-done' | 'failed' | 'interrupted' | 'pr-draft' | 'pr-open' | 'ci-failed' | 'changes-requested' | 'unaddressed-comments' | 'ready-to-merge' | 'ready-to-enqueue' | 'pr-queued' | 'pr-merged' | 'pr-closed' | 'ci-running' | 'review-pending' | 'merge-conflict';
|
|
318
318
|
/** Backend-owned, Task-only Needs Attention read model. */
|
|
319
319
|
export interface TaskAttentionRow {
|
|
320
320
|
task_id: string;
|
|
@@ -499,18 +499,6 @@ export interface ReviewSubmission {
|
|
|
499
499
|
body: string;
|
|
500
500
|
comments: ReviewSubmissionComment[];
|
|
501
501
|
}
|
|
502
|
-
/** Self-review comment for task implementation review */
|
|
503
|
-
export interface SelfReviewComment {
|
|
504
|
-
id: number;
|
|
505
|
-
task_id: string;
|
|
506
|
-
round: number;
|
|
507
|
-
comment_type: string;
|
|
508
|
-
file_path: string | null;
|
|
509
|
-
line_number: number | null;
|
|
510
|
-
body: string;
|
|
511
|
-
created_at: number;
|
|
512
|
-
archived_at: number | null;
|
|
513
|
-
}
|
|
514
502
|
/** One file referenced by a walkthrough step. `hunk_indexes === null` means the entire file's diff belongs to the step. */
|
|
515
503
|
export interface PrWalkthroughStepFile {
|
|
516
504
|
filename: string;
|
package/dist/frontend.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { BrowserSurfaceCapture, BrowserSurfaceFeedbackSelection, BrowserSurfaceVisualFeedback, BrowserSurfaceRegion, BrowserSurfaceErrorCode, BrowserSurfaceNavigationError, BrowserSurfacesAPI, GetOrCreateBrowserSurfaceRequest, TaskBrowserSurfaceController, TaskBrowserSurfaceState } from './browserSurfaces';
|
|
2
|
-
import type { CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, Disposable, FrontendBackendBridge, FrontendOpenForgeAPI, FrontendPlugin, FrontendPluginContext, FrontendSettingsRegistry, FrontendTaskPaneRegistry, FrontendInjectionPointRegistry, FrontendTaskStartRegistry, FrontendTaskUIRegistry, FrontendViewRegistry, InjectionPointLocation, NavigationAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, PluginIcon, PluginSidebarNavigationProps, PluginSidebarViewIdentity, PluginInjectionPointProps, PluginInjectionPointRegistration, PluginSettingsSectionProps, PluginSettingsSectionRegistration, PluginSettingsSectionScope, PluginStorageScope, PluginSvgIcon, PluginTaskPaneProps, PluginTaskPaneTabRegistration, PluginTaskUISectionProps, PluginTaskUISectionRegistration, PluginViewProps, PluginViewRegistration, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, PtyBufferState, TerminalImageProtocol } from './types';
|
|
1
|
+
import type { BrowserSurfaceCapture, BrowserDevToolsPanel, BrowserSurfaceFeedbackSelection, BrowserSurfaceVisualFeedback, BrowserSurfaceRegion, BrowserSurfaceErrorCode, BrowserSurfaceNavigationError, BrowserSurfacesAPI, GetOrCreateBrowserSurfaceRequest, TaskBrowserSurfaceController, TaskBrowserSurfaceState } from './browserSurfaces';
|
|
2
|
+
import type { CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, Disposable, FrontendBackendBridge, FrontendOpenForgeAPI, FrontendPlugin, FrontendPluginContext, FrontendSettingsRegistry, FrontendTaskPaneRegistry, FrontendInjectionPointRegistry, FrontendTaskStartRegistry, FrontendTaskUIRegistry, FrontendViewRegistry, InjectionPointLocation, NavigationAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, PluginIcon, PluginSidebarNavigationProps, PluginSidebarViewIdentity, PluginInjectionPointProps, PluginInjectionPointRegistration, PluginSettingsSectionProps, PluginSettingsSectionRegistration, PluginSettingsSectionScope, PluginStorageScope, PluginSvgIcon, PluginTaskPaneProps, PluginTaskPaneTabRegistration, PluginTaskUISectionProps, PluginTaskUISectionRegistration, PluginViewProps, PluginViewRegistration, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, PtyBufferState, TerminalImageProtocol, TerminalViewSnapshot } from './types';
|
|
3
3
|
export declare const OPENFORGE_FRONTEND_PLUGIN_MARKER = "__openforgeFrontendPlugin";
|
|
4
4
|
export type MarkedFrontendPlugin<TPlugin extends FrontendPlugin = FrontendPlugin> = TPlugin & {
|
|
5
5
|
readonly [OPENFORGE_FRONTEND_PLUGIN_MARKER]: true;
|
|
6
6
|
};
|
|
7
7
|
export declare function defineFrontendPlugin<const TPlugin extends FrontendPlugin>(plugin: TPlugin): MarkedFrontendPlugin<TPlugin>;
|
|
8
8
|
export { BrowserSurfaceError, isAllowedBrowserSurfaceUrl } from './browserSurfaces';
|
|
9
|
-
export type { BrowserSurfaceCapture, BrowserSurfaceFeedbackSelection, BrowserSurfaceVisualFeedback, BrowserSurfaceRegion, BrowserSurfaceErrorCode, BrowserSurfaceNavigationError, BrowserSurfacesAPI, GetOrCreateBrowserSurfaceRequest, TaskBrowserSurfaceController, TaskBrowserSurfaceState, };
|
|
10
|
-
export type { CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, Disposable, FrontendBackendBridge, FrontendOpenForgeAPI, FrontendPlugin, FrontendPluginContext, FrontendSettingsRegistry, FrontendTaskPaneRegistry, FrontendInjectionPointRegistry, FrontendTaskStartRegistry, FrontendTaskUIRegistry, FrontendViewRegistry, InjectionPointLocation, NavigationAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, PluginIcon, PluginSidebarNavigationProps, PluginSidebarViewIdentity, PluginInjectionPointProps, PluginInjectionPointRegistration, PluginSettingsSectionProps, PluginSettingsSectionRegistration, PluginSettingsSectionScope, PluginStorageScope, PluginSvgIcon, PluginTaskPaneProps, PluginTaskPaneTabRegistration, PluginTaskUISectionProps, PluginTaskUISectionRegistration, PluginViewProps, PluginViewRegistration, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, PtyBufferState, TerminalImageProtocol, };
|
|
9
|
+
export type { BrowserSurfaceCapture, BrowserDevToolsPanel, BrowserSurfaceFeedbackSelection, BrowserSurfaceVisualFeedback, BrowserSurfaceRegion, BrowserSurfaceErrorCode, BrowserSurfaceNavigationError, BrowserSurfacesAPI, GetOrCreateBrowserSurfaceRequest, TaskBrowserSurfaceController, TaskBrowserSurfaceState, };
|
|
10
|
+
export type { CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, Disposable, FrontendBackendBridge, FrontendOpenForgeAPI, FrontendPlugin, FrontendPluginContext, FrontendSettingsRegistry, FrontendTaskPaneRegistry, FrontendInjectionPointRegistry, FrontendTaskStartRegistry, FrontendTaskUIRegistry, FrontendViewRegistry, InjectionPointLocation, NavigationAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, PluginIcon, PluginSidebarNavigationProps, PluginSidebarViewIdentity, PluginInjectionPointProps, PluginInjectionPointRegistration, PluginSettingsSectionProps, PluginSettingsSectionRegistration, PluginSettingsSectionScope, PluginStorageScope, PluginSvgIcon, PluginTaskPaneProps, PluginTaskPaneTabRegistration, PluginTaskUISectionProps, PluginTaskUISectionRegistration, PluginViewProps, PluginViewRegistration, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, PtyBufferState, TerminalImageProtocol, TerminalViewSnapshot, };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type { BrowserSurfaceCapture, BrowserSurfaceFeedbackSelection, BrowserSur
|
|
|
3
3
|
export { OPENFORGE_PACKAGE_METADATA_SCHEMA, OPENFORGE_PLUGIN_CAPABILITIES, isOpenForgePackageMetadata, isPluginPackageMetadata, isSupportedOpenForgeApiVersion, validateOpenForgePackageMetadata, validatePluginPackageMetadata, } from './manifest';
|
|
4
4
|
export { createMemoryPluginStorage, createMockBackendOpenForgeApi, createMockFrontendOpenForgeApi, createMockOpenForgeApi, createMockPluginContext, createOpenForgeRegistryFake, createTestingCalls, TestingOpenForgeRegistryFake, TestingSubscriptionSink, } from './testing';
|
|
5
5
|
export { DEFAULT_EXTERNAL_TEXT_FILE_CHUNK_SIZE_BYTES, MAX_EXTERNAL_TEXT_FILE_CHUNK_SIZE_BYTES, MIN_EXTERNAL_TEXT_FILE_CHUNK_SIZE_BYTES, TaskFollowUpError, MAX_SUPPORTED_API_VERSION, MIN_SUPPORTED_API_VERSION, OPENFORGE_PLUGIN_API_VERSION, SUPPORTED_OPENFORGE_API_VERSIONS, isPluginViewKey, resolveExternalTextFileChunkSize, makePluginViewKey, parsePluginViewKey, } from './types';
|
|
6
|
-
export type { AgentCommandDescriptor, AgentCommandMetadata, AgentCommandRuntime, AttentionAPI, BackendReadyState, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, ConfigureStartPromptContributionRequest, CreateTaskRequest, Disposable, BackendFileSystemAPI, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadTextFileChunksRequest, ExternalReadFileSystemAPI, FileSystemAPI, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, ImplementationRun, InjectionPointLocation, JsonObject, JsonPrimitive, JsonSchema, JsonValue, MaybePromise, KeyValueConfigAPI, NotificationRequest, NavigationAPI, NotificationsAPI, OpenForgeCommonAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, OpenForgePackageMetadata, OpenForgePluginCapability, OpenForgePluginContext, OpenForgePluginPackageJson, PluginCommandInvocationContext, PluginCommandInvocationSource, PluginComponentLoader, PluginComponentModule, PluginEntry, PluginIcon, PluginSidebarNavigationProps, PluginSidebarViewIdentity, PluginInjectionPointProps, PluginSettingsSectionProps, PluginState, PluginSvgIcon, PluginTaskPaneProps, PluginTaskUISectionProps, PluginViewProps, PluginStorage, PluginStorageScope, PluginViewKey, ProjectsAPI, PtyBufferState, ShellAPI, ShellResizeRequest, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, TerminalImageProtocol, ShellSessionRequest, ShellSpawnRequest, ShellTerminalQueryResponseRequest, ShellWriteRequest, StartPromptContribution, SendTaskFollowUpRequest, StartTaskImplementationRequest, TaskFollowUpDisposition, TaskFollowUpErrorCode, TaskFollowUpReceipt, SubscriptionSink, SupportedOpenForgeApiVersion, SystemAPI, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, TasksAPI, ValidationError, } from './types';
|
|
6
|
+
export type { AgentCommandDescriptor, AgentCommandMetadata, AgentCommandRuntime, AttentionAPI, BackendReadyState, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, ConfigureStartPromptContributionRequest, CreateTaskRequest, Disposable, BackendFileSystemAPI, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadTextFileChunksRequest, ExternalReadFileSystemAPI, FileSystemAPI, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, ImplementationRun, InjectionPointLocation, JsonObject, JsonPrimitive, JsonSchema, JsonValue, MaybePromise, KeyValueConfigAPI, NotificationRequest, NavigationAPI, NotificationsAPI, OpenForgeCommonAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, OpenForgePackageMetadata, OpenForgePluginCapability, OpenForgePluginContext, OpenForgePluginPackageJson, PluginCommandInvocationContext, PluginCommandInvocationSource, PluginComponentLoader, PluginComponentModule, PluginEntry, PluginIcon, PluginSidebarNavigationProps, PluginSidebarViewIdentity, PluginInjectionPointProps, PluginSettingsSectionProps, PluginState, PluginSvgIcon, PluginTaskPaneProps, PluginTaskUISectionProps, PluginViewProps, PluginStorage, PluginStorageScope, PluginViewKey, ProjectsAPI, PtyBufferState, ShellAPI, ShellResizeRequest, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, TerminalImageProtocol, TerminalViewSnapshot, ShellSessionRequest, ShellSpawnRequest, ShellTerminalQueryResponseRequest, ShellWriteRequest, StartPromptContribution, SendTaskFollowUpRequest, StartTaskImplementationRequest, TaskFollowUpDisposition, TaskFollowUpErrorCode, TaskFollowUpReceipt, SubscriptionSink, SupportedOpenForgeApiVersion, SystemAPI, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, TasksAPI, ValidationError, } from './types';
|
|
7
7
|
export type { MockBackendOpenForgeAPI, MockFrontendOpenForgeAPI, TestingBackgroundServiceContribution, TestingBackendMethodContribution, TestingCommandContribution, TestingContributionBase, TestingExternalTextFile, TestingExternalTextFileChunksCall, TestingEventListenerContribution, TestingOpenForgeApiCalls, TestingOpenForgeApiOptions, TestingOpenForgeRegistrySnapshot, TestingRuntimeKind, TestingRuntimeScope, TestingSettingsSectionContribution, TestingTaskPaneTabContribution, TestingTaskUISectionContribution, TestingViewContribution, } from './testing';
|
|
8
8
|
export { parseStrictFiniteNumber } from './numberParsing';
|
|
9
9
|
export { buildProjectFileTree, flattenVisibleProjectFileTree, formatProjectFileTreeSize, getProjectFileTreeDepth, getProjectFileTreeItemAccessibility, getProjectFileTreeKeyboardAction, getProjectFileTreeParentPath, hasProjectFileTreeShortcutModifier, projectFileTreePathToId, } from './projectFileTree';
|
package/dist/publicUiExports.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BackendOpenForgeAPI } from '../types';
|
|
2
|
-
import { type TestingRegistryServices } from './support';
|
|
2
|
+
import { type TestingRegistryServices } from './support.js';
|
|
3
3
|
import type { TestingBackendMethodContribution, TestingBackgroundServiceContribution } from './contracts';
|
|
4
4
|
export declare class TestingBackendServicesFake {
|
|
5
5
|
private readonly services;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BackendOpenForgeAPI, FrontendOpenForgeAPI, OpenForgeCommonAPI } from '../types';
|
|
2
|
-
import { type TestingRegistryServices } from './support';
|
|
2
|
+
import { type TestingRegistryServices } from './support.js';
|
|
3
3
|
import type { TestingCommandContribution, TestingEventListenerContribution } from './contracts';
|
|
4
4
|
export type TestingCommonApi = OpenForgeCommonAPI & Pick<FrontendOpenForgeAPI, 'navigation'>;
|
|
5
5
|
export declare class TestingCommonApiFake {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { resolveExternalTextFileChunkSize } from '../types';
|
|
2
|
-
import { assertFunction, assertTitle, commandDescriptor, createDisposable, isJsonValue, normalizeAgentCommandMetadata, } from './support';
|
|
1
|
+
import { resolveExternalTextFileChunkSize } from '../types.js';
|
|
2
|
+
import { assertFunction, assertTitle, commandDescriptor, createDisposable, isJsonValue, normalizeAgentCommandMetadata, } from './support.js';
|
|
3
3
|
const UTF8_ENCODER = new TextEncoder();
|
|
4
4
|
function* splitExternalTextFile(content, maxBytes) {
|
|
5
5
|
let chunk = '';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type TaskBrowserSurfaceState } from '../browserSurfaces';
|
|
1
|
+
import { type TaskBrowserSurfaceState } from '../browserSurfaces.js';
|
|
2
2
|
import type { FrontendOpenForgeAPI } from '../types';
|
|
3
|
-
import { type TestingRegistryServices } from './support';
|
|
3
|
+
import { type TestingRegistryServices } from './support.js';
|
|
4
4
|
import type { TestingInjectionPointContribution, TestingTaskStartPrefixProviderContribution, TestingSettingsSectionContribution, TestingTaskPaneTabContribution, TestingTaskUISectionContribution, TestingViewContribution } from './contracts';
|
|
5
5
|
type TestingFrontendContributionApi = Pick<FrontendOpenForgeAPI, 'browserSurfaces' | 'taskLinks' | 'views' | 'taskUI' | 'taskPane' | 'settings' | 'backend' | 'injectionPoints' | 'taskStart'>;
|
|
6
6
|
export declare class TestingFrontendContributionFake {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createTestingBrowserSurfaces } from '../browserSurfacesTesting';
|
|
2
|
-
import { isAllowedBrowserSurfaceUrl } from '../browserSurfaces';
|
|
3
|
-
import { assertFunction, assertTitle, createDisposable, } from './support';
|
|
1
|
+
import { createTestingBrowserSurfaces } from '../browserSurfacesTesting.js';
|
|
2
|
+
import { isAllowedBrowserSurfaceUrl } from '../browserSurfaces.js';
|
|
3
|
+
import { assertFunction, assertTitle, createDisposable, } from './support.js';
|
|
4
4
|
export class TestingFrontendContributionFake {
|
|
5
5
|
services;
|
|
6
6
|
invokeBackendMethod;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { TaskBrowserSurfaceState } from '../browserSurfaces';
|
|
2
2
|
import type { BackendPlugin, BackendPluginContext, FrontendPlugin, FrontendPluginContext, OpenForgePackageMetadata, PluginStorage } from '../types';
|
|
3
3
|
import type { MockBackendOpenForgeAPI, MockFrontendOpenForgeAPI, TestingOpenForgeApiCalls, TestingOpenForgeApiOptions, TestingOpenForgeRegistrySnapshot } from './contracts';
|
|
4
|
-
import { TestingSubscriptionSink } from './support';
|
|
4
|
+
import { TestingSubscriptionSink } from './support.js';
|
|
5
5
|
export declare class TestingOpenForgeRegistryFake {
|
|
6
6
|
readonly pluginId: string;
|
|
7
7
|
readonly projectId: string | null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { TestingBackendServicesFake } from './backendServicesFake';
|
|
2
|
-
import { TestingCommonApiFake } from './commonApiFake';
|
|
3
|
-
import { TestingFrontendContributionFake } from './frontendContributionFake';
|
|
4
|
-
import { TestingRegistryServices, TestingSubscriptionSink, } from './support';
|
|
1
|
+
import { TestingBackendServicesFake } from './backendServicesFake.js';
|
|
2
|
+
import { TestingCommonApiFake } from './commonApiFake.js';
|
|
3
|
+
import { TestingFrontendContributionFake } from './frontendContributionFake.js';
|
|
4
|
+
import { TestingRegistryServices, TestingSubscriptionSink, } from './support.js';
|
|
5
5
|
export class TestingOpenForgeRegistryFake {
|
|
6
6
|
pluginId;
|
|
7
7
|
projectId;
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { createMockBackendOpenForgeApi, createMockFrontendOpenForgeApi, createMockOpenForgeApi, createMockPluginContext, createOpenForgeRegistryFake, TestingOpenForgeRegistryFake, } from './testing/registryFake';
|
|
2
|
-
export { createMemoryPluginStorage, createTestingCalls, TestingSubscriptionSink, } from './testing/support';
|
|
3
|
-
export type { MockBackendOpenForgeAPI, MockFrontendOpenForgeAPI, TestingBackgroundServiceContribution, TestingBackendMethodContribution, TestingCommandContribution, TestingContributionBase, TestingExternalTextFile, TestingExternalTextFileChunksCall, TestingEventListenerContribution, TestingInjectionPointContribution, TestingOpenForgeApiCalls, TestingOpenForgeApiOptions, TestingOpenForgeRegistrySnapshot, TestingRuntimeKind, TestingRuntimeScope, TestingSettingsSectionContribution, TestingTaskPaneTabContribution, TestingTaskUISectionContribution, TestingViewContribution, } from './testing/contracts';
|
|
1
|
+
export { createMockBackendOpenForgeApi, createMockFrontendOpenForgeApi, createMockOpenForgeApi, createMockPluginContext, createOpenForgeRegistryFake, TestingOpenForgeRegistryFake, } from './testing/registryFake.js';
|
|
2
|
+
export { createMemoryPluginStorage, createTestingCalls, TestingSubscriptionSink, } from './testing/support.js';
|
|
3
|
+
export type { MockBackendOpenForgeAPI, MockFrontendOpenForgeAPI, TestingBackgroundServiceContribution, TestingBackendMethodContribution, TestingCommandContribution, TestingContributionBase, TestingExternalTextFile, TestingExternalTextFileChunksCall, TestingEventListenerContribution, TestingInjectionPointContribution, TestingOpenForgeApiCalls, TestingOpenForgeApiOptions, TestingOpenForgeRegistrySnapshot, TestingRuntimeKind, TestingRuntimeScope, TestingSettingsSectionContribution, TestingTaskPaneTabContribution, TestingTaskUISectionContribution, TestingViewContribution, } from './testing/contracts.js';
|
package/dist/testing.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { createMockBackendOpenForgeApi, createMockFrontendOpenForgeApi, createMockOpenForgeApi, createMockPluginContext, createOpenForgeRegistryFake, TestingOpenForgeRegistryFake, } from './testing/registryFake';
|
|
2
|
-
export { createMemoryPluginStorage, createTestingCalls, TestingSubscriptionSink, } from './testing/support';
|
|
1
|
+
export { createMockBackendOpenForgeApi, createMockFrontendOpenForgeApi, createMockOpenForgeApi, createMockPluginContext, createOpenForgeRegistryFake, TestingOpenForgeRegistryFake, } from './testing/registryFake.js';
|
|
2
|
+
export { createMemoryPluginStorage, createTestingCalls, TestingSubscriptionSink, } from './testing/support.js';
|
package/dist/types.d.ts
CHANGED
|
@@ -440,8 +440,15 @@ export interface ShellResizeRequest extends ShellSessionRequest {
|
|
|
440
440
|
cols: number;
|
|
441
441
|
rows: number;
|
|
442
442
|
}
|
|
443
|
+
export interface TerminalViewSnapshot {
|
|
444
|
+
instanceId: number;
|
|
445
|
+
watermark: number;
|
|
446
|
+
data: string;
|
|
447
|
+
}
|
|
443
448
|
export interface PtyBufferState {
|
|
449
|
+
authority?: 'xterm-authoritative' | 'ghostty-authoritative';
|
|
444
450
|
buffer: string | null;
|
|
451
|
+
snapshot?: TerminalViewSnapshot | null;
|
|
445
452
|
isLive: boolean;
|
|
446
453
|
instanceId: number | null;
|
|
447
454
|
}
|
package/dist/types.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import packageMetadataSchemaData from './openforgePackageMetadataSchema.json';
|
|
1
|
+
import packageMetadataSchemaData from './openforgePackageMetadataSchema.json' with { type: 'json' };
|
|
2
2
|
function readSupportedOpenForgeApiVersions() {
|
|
3
3
|
const versions = packageMetadataSchemaData.properties.apiVersion.enum;
|
|
4
4
|
if (!Array.isArray(versions) || versions.length === 0 || !versions.every((version) => typeof version === 'number' && Number.isInteger(version))) {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
3
|
+
import { collapsedSections, isSectionCollapsed, toggleSection } from '../collapsibleSectionState'
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
// Stable, global key used to persist the collapsed/expanded state. Plugins must
|
|
7
|
+
// build this with `pluginSectionKey(pluginId, key)` so two plugins cannot collide.
|
|
8
|
+
sectionKey: string
|
|
9
|
+
title: string
|
|
10
|
+
// aria-label for the section landmark (defaults to the title).
|
|
11
|
+
label?: string
|
|
12
|
+
// data-task-info-card value (defaults to the section key).
|
|
13
|
+
cardId?: string
|
|
14
|
+
ariaLive?: 'polite' | 'off'
|
|
15
|
+
// A 14px glyph identifying the section, drawn between the disclosure caret and the
|
|
16
|
+
// title. Keep it decorative: the host hides it from assistive tech so it cannot
|
|
17
|
+
// pollute the toggle's accessible name.
|
|
18
|
+
icon?: Snippet
|
|
19
|
+
// Optional per-section controls (refresh, edit, counts). Rendered as a sibling of
|
|
20
|
+
// the toggle button — never nested inside it — so nested <button>s stay valid.
|
|
21
|
+
actions?: Snippet
|
|
22
|
+
children: Snippet
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let { sectionKey, title, label, cardId, ariaLive, icon, actions, children }: Props = $props()
|
|
26
|
+
|
|
27
|
+
let collapsed = $derived(isSectionCollapsed($collapsedSections, sectionKey))
|
|
28
|
+
let contentId = $derived(`info-section-${sectionKey}`)
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<!-- `--section-inset` is the distance from the card edge to the caret. Hosts override it
|
|
32
|
+
per surface (the task inspector pushes it out to 1.5rem); the header and the body
|
|
33
|
+
both read it so they can never drift apart. `--section-caret-column` is the caret
|
|
34
|
+
plus the gap after it, so body content lines up with the icon and title rather than
|
|
35
|
+
starting under the caret. Keep it equal to the caret's `w-3` plus the header `gap-2`. -->
|
|
36
|
+
<section
|
|
37
|
+
data-task-info-card={cardId ?? sectionKey}
|
|
38
|
+
data-card-sizing="natural"
|
|
39
|
+
class="rounded-lg border border-base-300/70 bg-base-100 overflow-hidden shrink-0 [--section-inset:0.75rem] [--section-caret-column:1.25rem]"
|
|
40
|
+
aria-label={label ?? title}
|
|
41
|
+
aria-live={ariaLive}
|
|
42
|
+
>
|
|
43
|
+
<div class="flex items-stretch {collapsed ? '' : 'border-b border-base-300/70'}">
|
|
44
|
+
<h3 class="m-0 min-w-0 flex-1">
|
|
45
|
+
<button
|
|
46
|
+
type="button"
|
|
47
|
+
class="flex w-full items-center gap-2 px-[var(--section-inset)] py-2 text-left text-sm font-semibold text-base-content hover:bg-base-200/40 focus-visible:ring-2 focus-visible:ring-primary rounded"
|
|
48
|
+
aria-expanded={!collapsed}
|
|
49
|
+
aria-controls={contentId}
|
|
50
|
+
onclick={() => toggleSection(sectionKey)}
|
|
51
|
+
>
|
|
52
|
+
<!-- Fixed-width caret column so every section title starts at the same x,
|
|
53
|
+
including the single-row cards that render a blank column instead. -->
|
|
54
|
+
<span
|
|
55
|
+
class="w-3 shrink-0 text-center text-[0.7rem] leading-none text-base-content/40 transition-transform duration-150 {collapsed ? '-rotate-90' : ''}"
|
|
56
|
+
aria-hidden="true"
|
|
57
|
+
>▾</span>
|
|
58
|
+
{#if icon}
|
|
59
|
+
<span class="flex shrink-0 items-center text-base-content/50" aria-hidden="true">{@render icon()}</span>
|
|
60
|
+
{/if}
|
|
61
|
+
<span class="truncate">{title}</span>
|
|
62
|
+
</button>
|
|
63
|
+
</h3>
|
|
64
|
+
{#if actions}
|
|
65
|
+
<div class="flex shrink-0 items-center gap-2 pr-2">
|
|
66
|
+
{@render actions()}
|
|
67
|
+
</div>
|
|
68
|
+
{/if}
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
{#if !collapsed}
|
|
72
|
+
<div
|
|
73
|
+
id={contentId}
|
|
74
|
+
class="pl-[calc(var(--section-inset)_+_var(--section-caret-column))] pr-[var(--section-inset)]"
|
|
75
|
+
>
|
|
76
|
+
{@render children()}
|
|
77
|
+
</div>
|
|
78
|
+
{/if}
|
|
79
|
+
</section>
|
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
type ResolvedMarkdownMedia,
|
|
9
9
|
} from '../markdown'
|
|
10
10
|
|
|
11
|
+
interface MarkdownImageOpenRequest {
|
|
12
|
+
src: string
|
|
13
|
+
alt: string
|
|
14
|
+
openLink?: () => void
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
interface Props {
|
|
12
18
|
content: string
|
|
13
19
|
imageBaseUrl?: string | null
|
|
@@ -16,6 +22,7 @@
|
|
|
16
22
|
resolveRemoteMedia?: (url: string) => Promise<ResolvedMarkdownMedia | null>
|
|
17
23
|
onOpenRepositoryPath?: (repositoryPath: string, suffix: string) => void | Promise<void>
|
|
18
24
|
onOpenUrl?: (url: string) => void | Promise<void>
|
|
25
|
+
onOpenImage?: (request: MarkdownImageOpenRequest) => void
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
let {
|
|
@@ -26,6 +33,7 @@
|
|
|
26
33
|
resolveRemoteMedia,
|
|
27
34
|
onOpenRepositoryPath,
|
|
28
35
|
onOpenUrl,
|
|
36
|
+
onOpenImage,
|
|
29
37
|
}: Props = $props()
|
|
30
38
|
|
|
31
39
|
let root = $state<HTMLDivElement | null>(null)
|
|
@@ -37,6 +45,35 @@
|
|
|
37
45
|
deferRemoteMedia: Boolean(resolveRemoteMedia),
|
|
38
46
|
}))
|
|
39
47
|
|
|
48
|
+
function imageTrigger(image: HTMLImageElement): HTMLElement {
|
|
49
|
+
return image.closest('a') ?? image
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function updateInteractiveImage(image: HTMLImageElement) {
|
|
53
|
+
const trigger = imageTrigger(image)
|
|
54
|
+
if (!onOpenImage || !image.getAttribute('src')) {
|
|
55
|
+
if (trigger.dataset.markdownImageTrigger === 'true') {
|
|
56
|
+
trigger.removeAttribute('role')
|
|
57
|
+
trigger.removeAttribute('tabindex')
|
|
58
|
+
trigger.removeAttribute('aria-label')
|
|
59
|
+
delete trigger.dataset.markdownImageTrigger
|
|
60
|
+
}
|
|
61
|
+
image.classList.remove('cursor-zoom-in')
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const imageName = image.alt.trim()
|
|
66
|
+
trigger.setAttribute('role', 'button')
|
|
67
|
+
if (trigger === image) trigger.setAttribute('tabindex', '0')
|
|
68
|
+
trigger.setAttribute('aria-label', imageName ? `Open ${imageName} image` : 'Open image preview')
|
|
69
|
+
trigger.dataset.markdownImageTrigger = 'true'
|
|
70
|
+
image.classList.add('cursor-zoom-in')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function updateInteractiveImages() {
|
|
74
|
+
if (!root) return
|
|
75
|
+
root.querySelectorAll('img').forEach(updateInteractiveImage)
|
|
76
|
+
}
|
|
40
77
|
async function resolveRepositoryImages(runId: number) {
|
|
41
78
|
if (!root || !markdownFilePath || !resolveRepositoryImage) return
|
|
42
79
|
|
|
@@ -50,6 +87,7 @@
|
|
|
50
87
|
if (runId !== imageResolutionId || !resolvedSrc) return
|
|
51
88
|
image.setAttribute('src', resolvedSrc)
|
|
52
89
|
image.removeAttribute('data-markdown-repository-path')
|
|
90
|
+
updateInteractiveImage(image)
|
|
53
91
|
} catch {
|
|
54
92
|
// Leave the image inert when an asset is missing or cannot be previewed.
|
|
55
93
|
}
|
|
@@ -61,6 +99,7 @@
|
|
|
61
99
|
|
|
62
100
|
if (element instanceof HTMLImageElement) {
|
|
63
101
|
element.setAttribute('src', resolved?.url ?? url)
|
|
102
|
+
updateInteractiveImage(element)
|
|
64
103
|
return
|
|
65
104
|
}
|
|
66
105
|
|
|
@@ -102,6 +141,7 @@
|
|
|
102
141
|
const runId = ++imageResolutionId
|
|
103
142
|
void html
|
|
104
143
|
if (!root) return
|
|
144
|
+
updateInteractiveImages()
|
|
105
145
|
|
|
106
146
|
void resolveRepositoryImages(runId)
|
|
107
147
|
void resolveRemoteMediaElements(runId)
|
|
@@ -111,17 +151,10 @@
|
|
|
111
151
|
imageResolutionId++
|
|
112
152
|
})
|
|
113
153
|
|
|
114
|
-
function
|
|
115
|
-
if (!(event.target instanceof Element)) return
|
|
116
|
-
|
|
117
|
-
const anchor = event.target.closest('a')
|
|
118
|
-
const href = anchor?.getAttribute('href')
|
|
119
|
-
if (!anchor || !href) return
|
|
120
|
-
|
|
154
|
+
function openMarkdownLink(href: string, absoluteHref: string) {
|
|
121
155
|
if (markdownFilePath) {
|
|
122
156
|
const repositoryPath = resolveMarkdownRepositoryPath(href, markdownFilePath)
|
|
123
157
|
if (repositoryPath) {
|
|
124
|
-
event.preventDefault()
|
|
125
158
|
void onOpenRepositoryPath?.(repositoryPath, getMarkdownRepositoryLinkSuffix(href))
|
|
126
159
|
return
|
|
127
160
|
}
|
|
@@ -129,14 +162,62 @@
|
|
|
129
162
|
|
|
130
163
|
if (href.startsWith('#')) return
|
|
131
164
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
void onOpenUrl(href.startsWith('//') ? `https:${href}` : anchor.href)
|
|
165
|
+
if (onOpenUrl && absoluteHref) {
|
|
166
|
+
void onOpenUrl(href.startsWith('//') ? `https:${href}` : absoluteHref)
|
|
135
167
|
}
|
|
136
168
|
}
|
|
169
|
+
|
|
170
|
+
function openImage(image: HTMLImageElement) {
|
|
171
|
+
const src = image.getAttribute('src')
|
|
172
|
+
if (!onOpenImage || !src) return
|
|
173
|
+
|
|
174
|
+
const anchor = image.closest('a')
|
|
175
|
+
const href = anchor?.getAttribute('href')
|
|
176
|
+
onOpenImage({
|
|
177
|
+
src,
|
|
178
|
+
alt: image.alt,
|
|
179
|
+
openLink: href ? () => openMarkdownLink(href, anchor?.href ?? '') : undefined,
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function findEventImage(target: Element): HTMLImageElement | null {
|
|
184
|
+
const image = target.closest('img')
|
|
185
|
+
if (image instanceof HTMLImageElement) return image
|
|
186
|
+
|
|
187
|
+
return target.closest('a')?.querySelector('img') ?? null
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function handleClick(event: MouseEvent) {
|
|
191
|
+
if (!(event.target instanceof Element)) return
|
|
192
|
+
|
|
193
|
+
const image = findEventImage(event.target)
|
|
194
|
+
if (image && onOpenImage && image.getAttribute('src')) {
|
|
195
|
+
event.preventDefault()
|
|
196
|
+
imageTrigger(image).focus()
|
|
197
|
+
openImage(image)
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const anchor = event.target.closest('a')
|
|
202
|
+
const href = anchor?.getAttribute('href')
|
|
203
|
+
if (!anchor || !href) return
|
|
204
|
+
|
|
205
|
+
if (!href.startsWith('#')) event.preventDefault()
|
|
206
|
+
openMarkdownLink(href, anchor.href)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
210
|
+
if (event.key !== 'Enter' && event.key !== ' ') return
|
|
211
|
+
if (!(event.target instanceof Element)) return
|
|
212
|
+
|
|
213
|
+
const image = findEventImage(event.target)
|
|
214
|
+
if (!image) return
|
|
215
|
+
|
|
216
|
+
event.preventDefault()
|
|
217
|
+
openImage(image)
|
|
218
|
+
}
|
|
137
219
|
</script>
|
|
138
220
|
|
|
139
|
-
|
|
140
|
-
<div bind:this={root} role="presentation" class="markdown-body" onclick={handleClick}>
|
|
221
|
+
<div bind:this={root} role="presentation" class="markdown-body" onclick={handleClick} onkeydown={handleKeydown}>
|
|
141
222
|
{@html html}
|
|
142
223
|
</div>
|
package/dist/vite.js
CHANGED
|
@@ -39,6 +39,7 @@ const OPENFORGE_PLUGIN_SDK_SOURCE_ENTRYPOINTS = Object.freeze([
|
|
|
39
39
|
['@openforge-app/plugin-sdk/sanitize', 'packages/plugin-sdk/src/sanitize.ts'],
|
|
40
40
|
['@openforge-app/plugin-sdk/pluginIcons', 'packages/plugin-sdk/src/pluginIcons.ts'],
|
|
41
41
|
['@openforge-app/plugin-sdk/fileIcons', 'packages/plugin-sdk/src/fileIcons.ts'],
|
|
42
|
+
['@openforge-app/plugin-sdk/collapsibleSectionState', 'packages/plugin-sdk/src/collapsibleSectionState.ts'],
|
|
42
43
|
...OPENFORGE_PLUGIN_SDK_PUBLIC_UI_EXPORTS.map(({ importSpecifier, workspaceSourcePath }) => [importSpecifier, workspaceSourcePath]),
|
|
43
44
|
['@openforge-app/plugin-sdk', 'packages/plugin-sdk/src/index.ts'],
|
|
44
45
|
]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openforge-app/plugin-sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -64,6 +64,10 @@
|
|
|
64
64
|
"types": "./dist/fileIcons.d.ts",
|
|
65
65
|
"default": "./dist/fileIcons.js"
|
|
66
66
|
},
|
|
67
|
+
"./collapsibleSectionState": {
|
|
68
|
+
"types": "./dist/collapsibleSectionState.d.ts",
|
|
69
|
+
"default": "./dist/collapsibleSectionState.js"
|
|
70
|
+
},
|
|
67
71
|
"./ui/Button.svelte": "./dist/ui/Button.svelte",
|
|
68
72
|
"./ui/Checkbox.svelte": "./dist/ui/Checkbox.svelte",
|
|
69
73
|
"./ui/MarkdownContent.svelte": "./dist/ui/MarkdownContent.svelte",
|
|
@@ -72,7 +76,8 @@
|
|
|
72
76
|
"./ui/PluginPageHeader.svelte": "./dist/ui/PluginPageHeader.svelte",
|
|
73
77
|
"./ui/PluginViewState.svelte": "./dist/ui/PluginViewState.svelte",
|
|
74
78
|
"./ui/PluginSidebarLink.svelte": "./dist/ui/PluginSidebarLink.svelte",
|
|
75
|
-
"./ui/FileTypeIcon.svelte": "./dist/ui/FileTypeIcon.svelte"
|
|
79
|
+
"./ui/FileTypeIcon.svelte": "./dist/ui/FileTypeIcon.svelte",
|
|
80
|
+
"./ui/CollapsibleSection.svelte": "./dist/ui/CollapsibleSection.svelte"
|
|
76
81
|
},
|
|
77
82
|
"files": [
|
|
78
83
|
"dist",
|