@openforge-app/plugin-sdk 0.2.4 → 0.2.5
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/backend.d.ts +2 -2
- package/dist/context.d.ts +2 -1
- package/dist/context.js +3 -0
- package/dist/domain.d.ts +2 -0
- package/dist/frontend.d.ts +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/manifest.d.ts +4 -4
- package/dist/markdown.d.ts +8 -0
- package/dist/markdown.js +49 -3
- package/dist/testing/commonApiFake.js +31 -0
- package/dist/testing/contracts.d.ts +10 -1
- package/dist/testing/registryFake.js +1 -0
- package/dist/testing/support.d.ts +2 -1
- package/dist/testing/support.js +3 -0
- package/dist/testing.d.ts +1 -1
- package/dist/types.d.ts +16 -1
- package/dist/types.js +13 -0
- package/dist/ui/MarkdownContent.svelte +54 -2
- package/package.json +7 -7
package/dist/backend.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { BackendFileSystemAPI, BackendMethodRegistration, BackendMethodRegistry, BackendOpenForgeAPI, BackendPlugin, BackendPluginContext, BackgroundServiceRegistration, BackgroundServiceRegistry, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, Disposable, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadFileSystemAPI, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, NavigationAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot } from './types';
|
|
1
|
+
import type { BackendFileSystemAPI, BackendMethodRegistration, BackendMethodRegistry, BackendOpenForgeAPI, BackendPlugin, BackendPluginContext, BackgroundServiceRegistration, BackgroundServiceRegistry, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, Disposable, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadTextFileChunksRequest, ExternalReadFileSystemAPI, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, NavigationAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot } from './types';
|
|
2
2
|
export declare function defineBackendPlugin<const TPlugin extends BackendPlugin>(plugin: TPlugin): TPlugin;
|
|
3
|
-
export type { BackendFileSystemAPI, BackendMethodRegistration, BackendMethodRegistry, BackendOpenForgeAPI, BackendPlugin, BackendPluginContext, BackgroundServiceRegistration, BackgroundServiceRegistry, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, Disposable, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadFileSystemAPI, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, NavigationAPI, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, };
|
|
3
|
+
export type { BackendFileSystemAPI, BackendMethodRegistration, BackendMethodRegistry, BackendOpenForgeAPI, BackendPlugin, BackendPluginContext, BackgroundServiceRegistration, BackgroundServiceRegistry, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, Disposable, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadTextFileChunksRequest, ExternalReadFileSystemAPI, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, NavigationAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, };
|
package/dist/context.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Disposable, OpenForgePackageMetadata, OpenForgePluginContext, SubscriptionSink, SupportedOpenForgeApiVersion } from './types';
|
|
1
|
+
import type { Disposable, OpenForgeContextChangeHandler, OpenForgePackageMetadata, OpenForgePluginContext, SubscriptionSink, SupportedOpenForgeApiVersion } from './types';
|
|
2
2
|
declare class SubscriptionSet implements SubscriptionSink {
|
|
3
3
|
private readonly subscriptions;
|
|
4
4
|
add(subscription: Disposable | (() => void)): void;
|
|
@@ -9,6 +9,7 @@ export declare class PluginContextImpl implements OpenForgePluginContext {
|
|
|
9
9
|
readonly apiVersion: SupportedOpenForgeApiVersion;
|
|
10
10
|
readonly packageMetadata: OpenForgePackageMetadata;
|
|
11
11
|
readonly subscriptions: SubscriptionSet;
|
|
12
|
+
onDidChange(_handler: OpenForgeContextChangeHandler): Disposable;
|
|
12
13
|
constructor(pluginId: string, apiVersion: SupportedOpenForgeApiVersion, packageMetadata: OpenForgePackageMetadata);
|
|
13
14
|
}
|
|
14
15
|
export {};
|
package/dist/context.js
CHANGED
|
@@ -21,6 +21,9 @@ export class PluginContextImpl {
|
|
|
21
21
|
apiVersion;
|
|
22
22
|
packageMetadata;
|
|
23
23
|
subscriptions;
|
|
24
|
+
onDidChange(_handler) {
|
|
25
|
+
return { dispose: () => undefined };
|
|
26
|
+
}
|
|
24
27
|
constructor(pluginId, apiVersion, packageMetadata) {
|
|
25
28
|
this.pluginId = pluginId;
|
|
26
29
|
this.apiVersion = apiVersion;
|
package/dist/domain.d.ts
CHANGED
|
@@ -150,6 +150,7 @@ export interface PullRequestInfo {
|
|
|
150
150
|
merge_queue_state: string | null;
|
|
151
151
|
readiness_updated_at: number | null;
|
|
152
152
|
}
|
|
153
|
+
export type PollOutcome = 'completed' | 'missing_github_token' | 'github_token_unavailable' | 'failed' | 'rate_limited';
|
|
153
154
|
export interface PollResult {
|
|
154
155
|
new_comments: number;
|
|
155
156
|
ci_changes: number;
|
|
@@ -158,6 +159,7 @@ export interface PollResult {
|
|
|
158
159
|
errors: number;
|
|
159
160
|
rate_limited: boolean;
|
|
160
161
|
rate_limit_reset_at: number | null;
|
|
162
|
+
outcome: PollOutcome;
|
|
161
163
|
}
|
|
162
164
|
export interface MergeStatusInfo {
|
|
163
165
|
state: string;
|
package/dist/frontend.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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, 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';
|
|
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';
|
|
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;
|
|
@@ -7,4 +7,4 @@ export type MarkedFrontendPlugin<TPlugin extends FrontendPlugin = FrontendPlugin
|
|
|
7
7
|
export declare function defineFrontendPlugin<const TPlugin extends FrontendPlugin>(plugin: TPlugin): MarkedFrontendPlugin<TPlugin>;
|
|
8
8
|
export { BrowserSurfaceError, isAllowedBrowserSurfaceUrl } from './browserSurfaces';
|
|
9
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, 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, };
|
|
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, };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ export { BrowserSurfaceError, isAllowedBrowserSurfaceUrl } from './browserSurfac
|
|
|
2
2
|
export type { BrowserSurfaceCapture, BrowserSurfaceFeedbackSelection, BrowserSurfaceVisualFeedback, BrowserSurfaceRegion, BrowserSurfaceErrorCode, BrowserSurfaceNavigationError, BrowserSurfacesAPI, GetOrCreateBrowserSurfaceRequest, TaskBrowserSurfaceController, TaskBrowserSurfaceState, } from './browserSurfaces';
|
|
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
|
-
export { TaskFollowUpError, MAX_SUPPORTED_API_VERSION, MIN_SUPPORTED_API_VERSION, OPENFORGE_PLUGIN_API_VERSION, SUPPORTED_OPENFORGE_API_VERSIONS, isPluginViewKey, makePluginViewKey, parsePluginViewKey, } from './types';
|
|
6
|
-
export type { AgentCommandDescriptor, AgentCommandMetadata, AgentCommandRuntime, AttentionAPI, BackendReadyState, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, ConfigureStartPromptContributionRequest, CreateTaskRequest, Disposable, BackendFileSystemAPI, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadFileSystemAPI, FileSystemAPI, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, ImplementationRun, InjectionPointLocation, JsonObject, JsonPrimitive, JsonSchema, JsonValue, MaybePromise, KeyValueConfigAPI, NotificationRequest, NavigationAPI, NotificationsAPI, OpenForgeCommonAPI, 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, ShellWriteRequest, StartPromptContribution, SendTaskFollowUpRequest, StartTaskImplementationRequest, TaskFollowUpDisposition, TaskFollowUpErrorCode, TaskFollowUpReceipt, SubscriptionSink, SupportedOpenForgeApiVersion, SystemAPI, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, TasksAPI, ValidationError, } from './types';
|
|
7
|
-
export type { MockBackendOpenForgeAPI, MockFrontendOpenForgeAPI, TestingBackgroundServiceContribution, TestingBackendMethodContribution, TestingCommandContribution, TestingContributionBase, TestingEventListenerContribution, TestingOpenForgeApiCalls, TestingOpenForgeApiOptions, TestingOpenForgeRegistrySnapshot, TestingRuntimeKind, TestingRuntimeScope, TestingSettingsSectionContribution, TestingTaskPaneTabContribution, TestingTaskUISectionContribution, TestingViewContribution, } from './testing';
|
|
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, ShellWriteRequest, StartPromptContribution, SendTaskFollowUpRequest, StartTaskImplementationRequest, TaskFollowUpDisposition, TaskFollowUpErrorCode, TaskFollowUpReceipt, SubscriptionSink, SupportedOpenForgeApiVersion, SystemAPI, TaskLinkHandler, TaskLinkHandlerResult, TaskLinkOpenRequest, TaskLinksAPI, TasksAPI, ValidationError, } from './types';
|
|
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';
|
|
10
10
|
export type { ProjectFileTreeEntry, ProjectFileTreeItemAccessibility, ProjectFileTreeKeyboardAction, ProjectFileTreeNode, } from './projectFileTree';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { BrowserSurfaceError, isAllowedBrowserSurfaceUrl } from './browserSurfaces';
|
|
2
2
|
export { OPENFORGE_PACKAGE_METADATA_SCHEMA, OPENFORGE_PLUGIN_CAPABILITIES, isOpenForgePackageMetadata, isPluginPackageMetadata, isSupportedOpenForgeApiVersion, validateOpenForgePackageMetadata, validatePluginPackageMetadata, } from './manifest';
|
|
3
3
|
export { createMemoryPluginStorage, createMockBackendOpenForgeApi, createMockFrontendOpenForgeApi, createMockOpenForgeApi, createMockPluginContext, createOpenForgeRegistryFake, createTestingCalls, TestingOpenForgeRegistryFake, TestingSubscriptionSink, } from './testing';
|
|
4
|
-
export { TaskFollowUpError, MAX_SUPPORTED_API_VERSION, MIN_SUPPORTED_API_VERSION, OPENFORGE_PLUGIN_API_VERSION, SUPPORTED_OPENFORGE_API_VERSIONS, isPluginViewKey, makePluginViewKey, parsePluginViewKey, } from './types';
|
|
4
|
+
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';
|
|
5
5
|
export { parseStrictFiniteNumber } from './numberParsing';
|
|
6
6
|
export { buildProjectFileTree, flattenVisibleProjectFileTree, formatProjectFileTreeSize, getProjectFileTreeDepth, getProjectFileTreeItemAccessibility, getProjectFileTreeKeyboardAction, getProjectFileTreeParentPath, hasProjectFileTreeShortcutModifier, projectFileTreePathToId, } from './projectFileTree';
|
|
7
7
|
export { canMergePullRequest, getMergeReadiness, hasMergeConflicts, isClosedUnmergedPullRequest, isMergedPullRequest, isQueuedForMerge, isReadyToMerge, parseCheckRuns, preservePullRequestState, splitCheckRuns, } from './domain';
|
package/dist/manifest.d.ts
CHANGED
|
@@ -30,10 +30,10 @@ export declare const OPENFORGE_PACKAGE_METADATA_SCHEMA: {
|
|
|
30
30
|
} | {
|
|
31
31
|
if: {
|
|
32
32
|
properties: {
|
|
33
|
+
requires?: undefined;
|
|
33
34
|
enablement: {
|
|
34
35
|
const: string;
|
|
35
36
|
};
|
|
36
|
-
requires?: undefined;
|
|
37
37
|
};
|
|
38
38
|
required: string[];
|
|
39
39
|
};
|
|
@@ -73,13 +73,15 @@ export declare const OPENFORGE_PACKAGE_METADATA_SCHEMA: {
|
|
|
73
73
|
};
|
|
74
74
|
icon: {
|
|
75
75
|
oneOf: ({
|
|
76
|
+
properties?: undefined;
|
|
76
77
|
type: string;
|
|
77
78
|
minLength: number;
|
|
78
79
|
pattern: string;
|
|
79
80
|
additionalProperties?: undefined;
|
|
80
81
|
required?: undefined;
|
|
81
|
-
properties?: undefined;
|
|
82
82
|
} | {
|
|
83
|
+
minLength?: undefined;
|
|
84
|
+
pattern?: undefined;
|
|
83
85
|
type: string;
|
|
84
86
|
additionalProperties: boolean;
|
|
85
87
|
required: string[];
|
|
@@ -93,8 +95,6 @@ export declare const OPENFORGE_PACKAGE_METADATA_SCHEMA: {
|
|
|
93
95
|
pattern: string;
|
|
94
96
|
};
|
|
95
97
|
};
|
|
96
|
-
minLength?: undefined;
|
|
97
|
-
pattern?: undefined;
|
|
98
98
|
})[];
|
|
99
99
|
description: string;
|
|
100
100
|
};
|
package/dist/markdown.d.ts
CHANGED
|
@@ -2,7 +2,15 @@ export interface RenderMarkdownOptions {
|
|
|
2
2
|
imageBaseUrl?: string | null;
|
|
3
3
|
markdownFilePath?: string | null;
|
|
4
4
|
deferRepositoryImages?: boolean;
|
|
5
|
+
deferRemoteMedia?: boolean;
|
|
5
6
|
}
|
|
7
|
+
/** How a deferred remote URL should be presented once it has been resolved. */
|
|
8
|
+
export interface ResolvedMarkdownMedia {
|
|
9
|
+
url: string;
|
|
10
|
+
kind: 'image' | 'video';
|
|
11
|
+
}
|
|
12
|
+
/** Attribute holding the original URL of a `<img>`/`<a>` awaiting resolution. */
|
|
13
|
+
export declare const MARKDOWN_REMOTE_MEDIA_ATTRIBUTE = "data-markdown-remote-src";
|
|
6
14
|
export declare function resolveMarkdownRepositoryPath(value: string | null, markdownFilePath: string): string | null;
|
|
7
15
|
export declare function getMarkdownRepositoryLinkSuffix(value: string): string;
|
|
8
16
|
export declare function resolveMarkdownImageSrc(src: string | null, imageBaseUrl: string | null | undefined, markdownFilePath?: string): string | null;
|
package/dist/markdown.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { marked } from 'marked';
|
|
2
2
|
import { sanitizeHtml } from './sanitize';
|
|
3
|
+
/** Attribute holding the original URL of a `<img>`/`<a>` awaiting resolution. */
|
|
4
|
+
export const MARKDOWN_REMOTE_MEDIA_ATTRIBUTE = 'data-markdown-remote-src';
|
|
3
5
|
const markedOptions = {
|
|
4
6
|
gfm: true,
|
|
5
7
|
breaks: true,
|
|
@@ -71,13 +73,57 @@ export function resolveMarkdownImageSrc(src, imageBaseUrl, markdownFilePath = ''
|
|
|
71
73
|
return null;
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
|
-
function
|
|
75
|
-
|
|
76
|
+
function isRemoteUrl(value) {
|
|
77
|
+
return /^https?:\/\//i.test(value);
|
|
78
|
+
}
|
|
79
|
+
const TEXT_NODE = 3;
|
|
80
|
+
/**
|
|
81
|
+
* Whether nothing but a line break separates `anchor` from the end of its line.
|
|
82
|
+
* GitHub only turns a bare URL into an embedded player when it stands on its own
|
|
83
|
+
* line, so link markup mid-sentence stays a link.
|
|
84
|
+
*/
|
|
85
|
+
function isAtLineBoundary(from, step) {
|
|
86
|
+
let node = from;
|
|
87
|
+
while (node) {
|
|
88
|
+
if (node.nodeType !== TEXT_NODE)
|
|
89
|
+
return node.nodeName === 'BR';
|
|
90
|
+
if ((node.textContent ?? '').trim() !== '')
|
|
91
|
+
return false;
|
|
92
|
+
node = node[step];
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
function isBareLinkOnItsOwnLine(anchor, href) {
|
|
97
|
+
return (anchor.textContent ?? '').trim() === href
|
|
98
|
+
&& isAtLineBoundary(anchor.previousSibling, 'previousSibling')
|
|
99
|
+
&& isAtLineBoundary(anchor.nextSibling, 'nextSibling');
|
|
100
|
+
}
|
|
101
|
+
function deferRemoteMediaLinks(root) {
|
|
102
|
+
for (const anchor of root.querySelectorAll('a[href]')) {
|
|
103
|
+
const href = anchor.getAttribute('href')?.trim() ?? '';
|
|
104
|
+
if (!isRemoteUrl(href) || !isBareLinkOnItsOwnLine(anchor, href))
|
|
105
|
+
continue;
|
|
106
|
+
anchor.setAttribute(MARKDOWN_REMOTE_MEDIA_ATTRIBUTE, href);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function prepareMarkdownMediaSources(html, options) {
|
|
110
|
+
if (typeof document === 'undefined' ||
|
|
111
|
+
(!options.imageBaseUrl && !options.deferRepositoryImages && !options.deferRemoteMedia))
|
|
76
112
|
return html;
|
|
77
113
|
const template = document.createElement('template');
|
|
78
114
|
template.innerHTML = html;
|
|
115
|
+
if (options.deferRemoteMedia) {
|
|
116
|
+
deferRemoteMediaLinks(template.content);
|
|
117
|
+
}
|
|
79
118
|
for (const image of template.content.querySelectorAll('img[src]')) {
|
|
80
119
|
const source = image.getAttribute('src')?.trim() ?? '';
|
|
120
|
+
// Hold the remote URL back so the caller can exchange it for one the
|
|
121
|
+
// renderer can actually load, instead of flashing a broken image first.
|
|
122
|
+
if (options.deferRemoteMedia && isRemoteUrl(source)) {
|
|
123
|
+
image.removeAttribute('src');
|
|
124
|
+
image.setAttribute(MARKDOWN_REMOTE_MEDIA_ATTRIBUTE, source);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
81
127
|
const shouldDefer = options.deferRepositoryImages && source.length > 0 && !hasAbsoluteOrSpecialUrl(source);
|
|
82
128
|
if (shouldDefer) {
|
|
83
129
|
image.removeAttribute('src');
|
|
@@ -99,5 +145,5 @@ function prepareMarkdownImageSources(html, options) {
|
|
|
99
145
|
}
|
|
100
146
|
export function renderMarkdownHtml(content, options = {}) {
|
|
101
147
|
const rawHtml = marked.parse(content, markedOptions);
|
|
102
|
-
return sanitizeHtml(
|
|
148
|
+
return sanitizeHtml(prepareMarkdownMediaSources(rawHtml, options));
|
|
103
149
|
}
|
|
@@ -1,4 +1,22 @@
|
|
|
1
|
+
import { resolveExternalTextFileChunkSize } from '../types';
|
|
1
2
|
import { assertFunction, assertTitle, commandDescriptor, createDisposable, isJsonValue, normalizeAgentCommandMetadata, } from './support';
|
|
3
|
+
const UTF8_ENCODER = new TextEncoder();
|
|
4
|
+
function* splitExternalTextFile(content, maxBytes) {
|
|
5
|
+
let chunk = '';
|
|
6
|
+
let chunkBytes = 0;
|
|
7
|
+
for (const character of content) {
|
|
8
|
+
const characterBytes = UTF8_ENCODER.encode(character).byteLength;
|
|
9
|
+
if (chunkBytes + characterBytes > maxBytes && chunk.length > 0) {
|
|
10
|
+
yield chunk;
|
|
11
|
+
chunk = '';
|
|
12
|
+
chunkBytes = 0;
|
|
13
|
+
}
|
|
14
|
+
chunk += character;
|
|
15
|
+
chunkBytes += characterBytes;
|
|
16
|
+
}
|
|
17
|
+
if (chunk.length > 0)
|
|
18
|
+
yield chunk;
|
|
19
|
+
}
|
|
2
20
|
export class TestingCommonApiFake {
|
|
3
21
|
services;
|
|
4
22
|
commands = new Map();
|
|
@@ -211,6 +229,19 @@ export class TestingCommonApiFake {
|
|
|
211
229
|
this.services.calls.fsExternalReads.push(request);
|
|
212
230
|
return '';
|
|
213
231
|
},
|
|
232
|
+
readTextFileChunks: (request) => {
|
|
233
|
+
const chunkSizeBytes = resolveExternalTextFileChunkSize(request.chunkSizeBytes);
|
|
234
|
+
const { root, path, signal } = request;
|
|
235
|
+
this.services.calls.fsExternalReadTextFileChunks.push({ root, path, chunkSizeBytes });
|
|
236
|
+
const content = this.services.externalTextFiles.find(file => file.root === root && file.path === path)?.content ?? '';
|
|
237
|
+
return (async function* () {
|
|
238
|
+
for (const chunk of splitExternalTextFile(content, chunkSizeBytes)) {
|
|
239
|
+
signal?.throwIfAborted();
|
|
240
|
+
yield chunk;
|
|
241
|
+
}
|
|
242
|
+
signal?.throwIfAborted();
|
|
243
|
+
})();
|
|
244
|
+
},
|
|
214
245
|
},
|
|
215
246
|
},
|
|
216
247
|
};
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import type { BrowserSurfaceVisualFeedback } from '../browserSurfaces';
|
|
2
2
|
import type { TestingOpenForgeRegistryFake } from './registryFake';
|
|
3
|
-
import type { BackendMethodRegistration, BackendOpenForgeAPI, BackgroundServiceRegistration, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ConfigureStartPromptContributionRequest, CreateTaskRequest, FrontendOpenForgeAPI, InjectionPointLocation, JsonValue, NotificationRequest, OpenForgeNavigationRequest, OpenForgePackageMetadata, PluginSettingsSectionRegistration, PluginStorage, PluginTaskPaneTabRegistration, PluginTaskUISectionRegistration, PluginViewRegistration, ShellSpawnRequest, SendTaskFollowUpRequest, StartTaskImplementationRequest, TaskLinkOpenRequest, TaskStartPrefixContext, ExternalReadDirectoryRequest, ExternalReadFileRequest, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileWriteRequest } from '../types';
|
|
3
|
+
import type { BackendMethodRegistration, BackendOpenForgeAPI, BackgroundServiceRegistration, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ConfigureStartPromptContributionRequest, CreateTaskRequest, FrontendOpenForgeAPI, InjectionPointLocation, JsonValue, NotificationRequest, OpenForgeNavigationRequest, OpenForgePackageMetadata, PluginSettingsSectionRegistration, PluginStorage, PluginTaskPaneTabRegistration, PluginTaskUISectionRegistration, PluginViewRegistration, ShellSpawnRequest, SendTaskFollowUpRequest, StartTaskImplementationRequest, TaskLinkOpenRequest, TaskStartPrefixContext, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadTextFileChunksRequest, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileWriteRequest } from '../types';
|
|
4
4
|
import type { Task } from '../domain';
|
|
5
5
|
export type TestingRuntimeScope = 'global' | 'project' | 'task';
|
|
6
6
|
export type TestingRuntimeKind = 'commands' | 'events' | 'views' | 'taskPane' | 'taskUI' | 'settings' | 'backend' | 'background';
|
|
7
7
|
export type TestingMaybePromise<T> = T | Promise<T>;
|
|
8
8
|
export type TestingCommandHandler = (payload?: unknown) => TestingMaybePromise<unknown>;
|
|
9
9
|
export type TestingEventHandler = (payload: unknown) => void;
|
|
10
|
+
export interface TestingExternalTextFile extends ExternalReadFileRequest {
|
|
11
|
+
content: string;
|
|
12
|
+
}
|
|
13
|
+
export type TestingExternalTextFileChunksCall = Omit<ExternalReadTextFileChunksRequest, 'signal' | 'chunkSizeBytes'> & {
|
|
14
|
+
chunkSizeBytes: number;
|
|
15
|
+
};
|
|
10
16
|
export interface TestingOpenForgeApiOptions {
|
|
11
17
|
pluginId?: string;
|
|
12
18
|
projectId?: string | null;
|
|
@@ -21,6 +27,8 @@ export interface TestingOpenForgeApiOptions {
|
|
|
21
27
|
* mirroring the host capability. Defaults to an empty list.
|
|
22
28
|
*/
|
|
23
29
|
tasks?: Task[];
|
|
30
|
+
/** UTF-8 files returned by `fs.external.readTextFileChunks`. Defaults to none. */
|
|
31
|
+
externalTextFiles?: TestingExternalTextFile[];
|
|
24
32
|
}
|
|
25
33
|
export interface TestingOpenForgeApiCalls {
|
|
26
34
|
commandInvocations: Array<{
|
|
@@ -79,6 +87,7 @@ export interface TestingOpenForgeApiCalls {
|
|
|
79
87
|
fsUserDataWrites: UserDataFileWriteRequest[];
|
|
80
88
|
fsExternalReadDirs: ExternalReadDirectoryRequest[];
|
|
81
89
|
fsExternalReads: ExternalReadFileRequest[];
|
|
90
|
+
fsExternalReadTextFileChunks: TestingExternalTextFileChunksCall[];
|
|
82
91
|
shellSpawns: ShellSpawnRequest[];
|
|
83
92
|
shellWrites: Array<{
|
|
84
93
|
taskId: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentCommandMetadata, CommandDescriptor, Disposable, JsonValue, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, OpenForgePackageMetadata, PluginStorage, StartPromptContribution, SubscriptionSink } from '../types';
|
|
2
2
|
import type { Task } from '../domain';
|
|
3
|
-
import type { TestingCommandContribution, TestingMaybePromise, TestingOpenForgeApiCalls, TestingOpenForgeApiOptions, TestingRuntimeKind } from './contracts';
|
|
3
|
+
import type { TestingCommandContribution, TestingMaybePromise, TestingExternalTextFile, TestingOpenForgeApiCalls, TestingOpenForgeApiOptions, TestingRuntimeKind } from './contracts';
|
|
4
4
|
export declare function createDisposable(dispose: () => TestingMaybePromise<void>): Disposable;
|
|
5
5
|
export declare class TestingSubscriptionSink implements SubscriptionSink {
|
|
6
6
|
readonly subscriptions: Disposable[];
|
|
@@ -31,6 +31,7 @@ export declare class TestingRegistryServices {
|
|
|
31
31
|
readonly storage: PluginStorage;
|
|
32
32
|
readonly config: Map<string, JsonValue>;
|
|
33
33
|
readonly seededTasks: Task[];
|
|
34
|
+
readonly externalTextFiles: TestingExternalTextFile[];
|
|
34
35
|
readonly claims: TestingContributionClaims;
|
|
35
36
|
constructor(options?: TestingOpenForgeApiOptions);
|
|
36
37
|
localQualifiedId(kind: TestingRuntimeKind, id: string): string;
|
package/dist/testing/support.js
CHANGED
|
@@ -53,6 +53,7 @@ export function createTestingCalls() {
|
|
|
53
53
|
fsUserDataWrites: [],
|
|
54
54
|
fsExternalReadDirs: [],
|
|
55
55
|
fsExternalReads: [],
|
|
56
|
+
fsExternalReadTextFileChunks: [],
|
|
56
57
|
shellSpawns: [],
|
|
57
58
|
shellWrites: [],
|
|
58
59
|
shellResizes: [],
|
|
@@ -201,6 +202,7 @@ export class TestingRegistryServices {
|
|
|
201
202
|
storage;
|
|
202
203
|
config = new Map();
|
|
203
204
|
seededTasks;
|
|
205
|
+
externalTextFiles;
|
|
204
206
|
claims = new TestingContributionClaims();
|
|
205
207
|
constructor(options = {}) {
|
|
206
208
|
this.pluginId = options.pluginId ?? 'test-plugin';
|
|
@@ -216,6 +218,7 @@ export class TestingRegistryServices {
|
|
|
216
218
|
this.calls = createTestingCalls();
|
|
217
219
|
this.storage = options.storage ?? createMemoryPluginStorage(this.calls);
|
|
218
220
|
this.seededTasks = options.tasks ?? [];
|
|
221
|
+
this.externalTextFiles = options.externalTextFiles ?? [];
|
|
219
222
|
}
|
|
220
223
|
localQualifiedId(kind, id) {
|
|
221
224
|
assertLocalId(kind, id);
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { createMockBackendOpenForgeApi, createMockFrontendOpenForgeApi, createMockOpenForgeApi, createMockPluginContext, createOpenForgeRegistryFake, TestingOpenForgeRegistryFake, } from './testing/registryFake';
|
|
2
2
|
export { createMemoryPluginStorage, createTestingCalls, TestingSubscriptionSink, } from './testing/support';
|
|
3
|
-
export type { MockBackendOpenForgeAPI, MockFrontendOpenForgeAPI, TestingBackgroundServiceContribution, TestingBackendMethodContribution, TestingCommandContribution, TestingContributionBase, TestingEventListenerContribution, TestingInjectionPointContribution, TestingOpenForgeApiCalls, TestingOpenForgeApiOptions, TestingOpenForgeRegistrySnapshot, TestingRuntimeKind, TestingRuntimeScope, TestingSettingsSectionContribution, TestingTaskPaneTabContribution, TestingTaskUISectionContribution, TestingViewContribution, } from './testing/contracts';
|
|
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';
|
package/dist/types.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface ValidationError {
|
|
|
17
17
|
path: string;
|
|
18
18
|
message: string;
|
|
19
19
|
}
|
|
20
|
-
declare const OPENFORGE_PLUGIN_CAPABILITY_TYPE_MEMBERS: readonly [
|
|
20
|
+
declare const OPENFORGE_PLUGIN_CAPABILITY_TYPE_MEMBERS: readonly ['commands', 'events', 'views', 'injectionPoints', 'taskPane', 'taskStart', 'settings', 'background', 'backend', 'storage', 'context', 'navigation', 'tasks', 'projects', 'fs', 'shell', 'notifications', 'attention', 'system.openUrl', 'system.writeClipboardText', 'config', 'projectConfig', 'browserSurfaces', 'taskLinks', 'appEnablement', 'customSidebarNavigation'];
|
|
21
21
|
export type OpenForgePluginCapability = (typeof OPENFORGE_PLUGIN_CAPABILITY_TYPE_MEMBERS)[number];
|
|
22
22
|
export interface OpenForgePackageMetadata {
|
|
23
23
|
id: string;
|
|
@@ -57,6 +57,7 @@ export interface OpenForgeContextSnapshot {
|
|
|
57
57
|
projectId: string | null;
|
|
58
58
|
taskId?: string | null;
|
|
59
59
|
}
|
|
60
|
+
export type OpenForgeContextChangeHandler = (snapshot: OpenForgeContextSnapshot) => MaybePromise<void>;
|
|
60
61
|
export interface OpenForgeNavigationSnapshot {
|
|
61
62
|
activeProjectId: string | null;
|
|
62
63
|
currentView: string;
|
|
@@ -88,6 +89,7 @@ export interface OpenForgePluginContext {
|
|
|
88
89
|
apiVersion: SupportedOpenForgeApiVersion;
|
|
89
90
|
packageMetadata: OpenForgePackageMetadata;
|
|
90
91
|
subscriptions: SubscriptionSink;
|
|
92
|
+
onDidChange(handler: OpenForgeContextChangeHandler): Disposable;
|
|
91
93
|
}
|
|
92
94
|
export type FrontendPluginContext = OpenForgePluginContext;
|
|
93
95
|
export type BackendPluginContext = OpenForgePluginContext;
|
|
@@ -390,6 +392,17 @@ export interface ExternalReadFileRequest {
|
|
|
390
392
|
root: string;
|
|
391
393
|
path: string;
|
|
392
394
|
}
|
|
395
|
+
export declare const DEFAULT_EXTERNAL_TEXT_FILE_CHUNK_SIZE_BYTES: number;
|
|
396
|
+
export declare const MIN_EXTERNAL_TEXT_FILE_CHUNK_SIZE_BYTES = 4;
|
|
397
|
+
export declare const MAX_EXTERNAL_TEXT_FILE_CHUNK_SIZE_BYTES: number;
|
|
398
|
+
/** Applies the default external text chunk size and rejects values outside host limits. */
|
|
399
|
+
export declare function resolveExternalTextFileChunkSize(chunkSizeBytes?: number): number;
|
|
400
|
+
export interface ExternalReadTextFileChunksRequest extends ExternalReadFileRequest {
|
|
401
|
+
/** UTF-8 chunks contain at most this many bytes. Defaults to 64 KiB. */
|
|
402
|
+
chunkSizeBytes?: number;
|
|
403
|
+
/** Stops future reads. An in-flight host read may finish, but its result is discarded. */
|
|
404
|
+
signal?: AbortSignal;
|
|
405
|
+
}
|
|
393
406
|
export interface UserDataFileSystemAPI {
|
|
394
407
|
readDir(request?: UserDataDirectoryRequest): Promise<FileEntry[]>;
|
|
395
408
|
readTextFile(request: UserDataFileRequest): Promise<string>;
|
|
@@ -398,6 +411,8 @@ export interface UserDataFileSystemAPI {
|
|
|
398
411
|
export interface ExternalReadFileSystemAPI {
|
|
399
412
|
readDir(request: ExternalReadDirectoryRequest): Promise<FileEntry[]>;
|
|
400
413
|
readTextFile(request: ExternalReadFileRequest): Promise<string>;
|
|
414
|
+
/** Lazily reads a UTF-8 file without retaining a host file handle between chunks. */
|
|
415
|
+
readTextFileChunks(request: ExternalReadTextFileChunksRequest): AsyncIterable<string>;
|
|
401
416
|
}
|
|
402
417
|
export interface BackendFileSystemAPI extends FileSystemAPI {
|
|
403
418
|
userData: UserDataFileSystemAPI;
|
package/dist/types.js
CHANGED
|
@@ -49,6 +49,19 @@ function assertOpenForgePluginCapabilitiesMatchSchema() {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
assertOpenForgePluginCapabilitiesMatchSchema();
|
|
52
|
+
export const DEFAULT_EXTERNAL_TEXT_FILE_CHUNK_SIZE_BYTES = 64 * 1024;
|
|
53
|
+
export const MIN_EXTERNAL_TEXT_FILE_CHUNK_SIZE_BYTES = 4;
|
|
54
|
+
export const MAX_EXTERNAL_TEXT_FILE_CHUNK_SIZE_BYTES = 1024 * 1024;
|
|
55
|
+
/** Applies the default external text chunk size and rejects values outside host limits. */
|
|
56
|
+
export function resolveExternalTextFileChunkSize(chunkSizeBytes) {
|
|
57
|
+
const size = chunkSizeBytes ?? DEFAULT_EXTERNAL_TEXT_FILE_CHUNK_SIZE_BYTES;
|
|
58
|
+
if (!Number.isInteger(size)
|
|
59
|
+
|| size < MIN_EXTERNAL_TEXT_FILE_CHUNK_SIZE_BYTES
|
|
60
|
+
|| size > MAX_EXTERNAL_TEXT_FILE_CHUNK_SIZE_BYTES) {
|
|
61
|
+
throw new RangeError(`chunkSizeBytes must be an integer between ${MIN_EXTERNAL_TEXT_FILE_CHUNK_SIZE_BYTES} and ${MAX_EXTERNAL_TEXT_FILE_CHUNK_SIZE_BYTES}`);
|
|
62
|
+
}
|
|
63
|
+
return size;
|
|
64
|
+
}
|
|
52
65
|
export class TaskFollowUpError extends Error {
|
|
53
66
|
code;
|
|
54
67
|
constructor(code, message) {
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onDestroy } from 'svelte'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getMarkdownRepositoryLinkSuffix,
|
|
5
|
+
MARKDOWN_REMOTE_MEDIA_ATTRIBUTE,
|
|
6
|
+
renderMarkdownHtml,
|
|
7
|
+
resolveMarkdownRepositoryPath,
|
|
8
|
+
type ResolvedMarkdownMedia,
|
|
9
|
+
} from '../markdown'
|
|
4
10
|
|
|
5
11
|
interface Props {
|
|
6
12
|
content: string
|
|
7
13
|
imageBaseUrl?: string | null
|
|
8
14
|
markdownFilePath?: string | null
|
|
9
15
|
resolveRepositoryImage?: (repositoryPath: string) => Promise<string | null>
|
|
16
|
+
resolveRemoteMedia?: (url: string) => Promise<ResolvedMarkdownMedia | null>
|
|
10
17
|
onOpenRepositoryPath?: (repositoryPath: string, suffix: string) => void | Promise<void>
|
|
11
18
|
onOpenUrl?: (url: string) => void | Promise<void>
|
|
12
19
|
}
|
|
@@ -16,6 +23,7 @@
|
|
|
16
23
|
imageBaseUrl = null,
|
|
17
24
|
markdownFilePath = null,
|
|
18
25
|
resolveRepositoryImage,
|
|
26
|
+
resolveRemoteMedia,
|
|
19
27
|
onOpenRepositoryPath,
|
|
20
28
|
onOpenUrl,
|
|
21
29
|
}: Props = $props()
|
|
@@ -26,6 +34,7 @@
|
|
|
26
34
|
imageBaseUrl: resolveRepositoryImage ? null : imageBaseUrl,
|
|
27
35
|
markdownFilePath,
|
|
28
36
|
deferRepositoryImages: Boolean(resolveRepositoryImage),
|
|
37
|
+
deferRemoteMedia: Boolean(resolveRemoteMedia),
|
|
29
38
|
}))
|
|
30
39
|
|
|
31
40
|
async function resolveRepositoryImages(runId: number) {
|
|
@@ -47,12 +56,55 @@
|
|
|
47
56
|
}))
|
|
48
57
|
}
|
|
49
58
|
|
|
59
|
+
function applyResolvedMedia(element: Element, url: string, resolved: ResolvedMarkdownMedia | null) {
|
|
60
|
+
element.removeAttribute(MARKDOWN_REMOTE_MEDIA_ATTRIBUTE)
|
|
61
|
+
|
|
62
|
+
if (element instanceof HTMLImageElement) {
|
|
63
|
+
element.setAttribute('src', resolved?.url ?? url)
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// A bare link only becomes a player when the caller says the upload is one;
|
|
68
|
+
// everything else stays the link the Markdown asked for.
|
|
69
|
+
if (resolved?.kind !== 'video') return
|
|
70
|
+
|
|
71
|
+
const video = document.createElement('video')
|
|
72
|
+
video.src = resolved.url
|
|
73
|
+
video.controls = true
|
|
74
|
+
video.preload = 'metadata'
|
|
75
|
+
element.replaceWith(video)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Remote media is held back by the renderer so the caller can swap in a URL
|
|
79
|
+
// this app can load (GitHub only serves upload URLs to a browser session).
|
|
80
|
+
// Anything the caller does not handle falls back to what the Markdown said.
|
|
81
|
+
async function resolveRemoteMediaElements(runId: number) {
|
|
82
|
+
if (!root || !resolveRemoteMedia) return
|
|
83
|
+
|
|
84
|
+
const elements = Array.from(root.querySelectorAll(`[${MARKDOWN_REMOTE_MEDIA_ATTRIBUTE}]`))
|
|
85
|
+
await Promise.all(elements.map(async (element) => {
|
|
86
|
+
const url = element.getAttribute(MARKDOWN_REMOTE_MEDIA_ATTRIBUTE)
|
|
87
|
+
if (!url) return
|
|
88
|
+
|
|
89
|
+
let resolved: ResolvedMarkdownMedia | null = null
|
|
90
|
+
try {
|
|
91
|
+
resolved = await resolveRemoteMedia(url)
|
|
92
|
+
} catch {
|
|
93
|
+
// Fall back to the original URL when resolution is unavailable.
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (runId !== imageResolutionId) return
|
|
97
|
+
applyResolvedMedia(element, url, resolved)
|
|
98
|
+
}))
|
|
99
|
+
}
|
|
100
|
+
|
|
50
101
|
$effect(() => {
|
|
51
102
|
const runId = ++imageResolutionId
|
|
52
103
|
void html
|
|
53
|
-
if (!root
|
|
104
|
+
if (!root) return
|
|
54
105
|
|
|
55
106
|
void resolveRepositoryImages(runId)
|
|
107
|
+
void resolveRemoteMediaElements(runId)
|
|
56
108
|
})
|
|
57
109
|
|
|
58
110
|
onDestroy(() => {
|
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.5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -93,17 +93,17 @@
|
|
|
93
93
|
"test": "vitest run"
|
|
94
94
|
},
|
|
95
95
|
"dependencies": {
|
|
96
|
-
"dompurify": "
|
|
97
|
-
"marked": "
|
|
96
|
+
"dompurify": "catalog:",
|
|
97
|
+
"marked": "catalog:"
|
|
98
98
|
},
|
|
99
99
|
"peerDependencies": {
|
|
100
100
|
"svelte": "^5.0.0"
|
|
101
101
|
},
|
|
102
102
|
"devDependencies": {
|
|
103
|
-
"@types/node": "
|
|
104
|
-
"svelte": "
|
|
105
|
-
"typescript": "
|
|
106
|
-
"vitest": "
|
|
103
|
+
"@types/node": "catalog:pinned",
|
|
104
|
+
"svelte": "catalog:pinned",
|
|
105
|
+
"typescript": "catalog:",
|
|
106
|
+
"vitest": "catalog:",
|
|
107
107
|
"vscode-material-icons": "^0.1.1"
|
|
108
108
|
}
|
|
109
109
|
}
|