@openforge-app/plugin-sdk 0.2.9 → 0.2.10
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/collapsibleSectionState.js +30 -12
- package/dist/domain.d.ts +1 -0
- package/dist/domain.js +2 -1
- package/dist/frontend.d.ts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/manifest.d.ts +1 -0
- package/dist/manifest.js +10 -4
- package/dist/markdown.d.ts +9 -0
- package/dist/markdown.js +57 -1
- package/dist/openforgePackageMetadataSchema.json +2 -11
- package/dist/publicEntrypoints.d.mts +26 -0
- package/dist/publicEntrypoints.mjs +103 -0
- package/dist/publicUiExports.mjs +2 -0
- package/dist/taskBrowserDevToolsShortcuts.d.ts +12 -0
- package/dist/taskBrowserDevToolsShortcuts.js +20 -0
- package/dist/testing/commonApiFake.js +206 -6
- package/dist/testing/contracts.d.ts +15 -3
- package/dist/testing/frontendContributionFake.d.ts +2 -3
- package/dist/testing/frontendContributionFake.js +0 -32
- package/dist/testing/support.d.ts +5 -2
- package/dist/testing/support.js +12 -1
- package/dist/types.d.ts +80 -19
- package/dist/types.js +1 -1
- package/dist/ui/Modal.svelte +33 -3
- package/dist/ui/PluginPageHeader.svelte +11 -4
- package/dist/ui/PluginPageShell.svelte +20 -0
- package/dist/ui/ProjectFileTree.svelte +192 -0
- package/dist/vite.js +7 -17
- package/package.json +9 -2
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, ExternalReadTextFileChunksRequest, ExternalReadFileSystemAPI, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, NavigationAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot } from './types';
|
|
1
|
+
import type { BackendFileSystemAPI, BackendMethodRegistration, BackendMethodRegistry, BackendOpenForgeAPI, BackendPlugin, BackendPluginContext, BackgroundServiceRegistration, BackgroundServiceRegistry, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, Disposable, ExternalFileMetadata, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadTextFileChunksRequest, ExternalReadFileSystemAPI, UserDataDirectoryRequest, UserDataFileAppendResult, 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, ExternalReadTextFileChunksRequest, ExternalReadFileSystemAPI, UserDataDirectoryRequest, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, NavigationAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, };
|
|
3
|
+
export type { BackendFileSystemAPI, BackendMethodRegistration, BackendMethodRegistry, BackendOpenForgeAPI, BackendPlugin, BackendPluginContext, BackgroundServiceRegistration, BackgroundServiceRegistry, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, Disposable, ExternalFileMetadata, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadTextFileChunksRequest, ExternalReadFileSystemAPI, UserDataDirectoryRequest, UserDataFileAppendResult, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, NavigationAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, };
|
|
@@ -2,6 +2,7 @@ import { writable } from 'svelte/store';
|
|
|
2
2
|
// The store started life in the host app as src/lib/infoPanelSectionState.ts. The key is
|
|
3
3
|
// deliberately unchanged: renaming it would reset every user's collapsed sections.
|
|
4
4
|
export const COLLAPSED_SECTIONS_STORAGE_KEY = 'openforge.infoPanelSectionCollapse.v1';
|
|
5
|
+
const SHARED_STATE_SYMBOL = Symbol.for('com.openforge.plugin-sdk.collapsible-section-state.v1');
|
|
5
6
|
export function pluginSectionKey(pluginId, sectionKey) {
|
|
6
7
|
return `plugin:${pluginId}:${sectionKey}`;
|
|
7
8
|
}
|
|
@@ -13,10 +14,10 @@ function getLocalStorage() {
|
|
|
13
14
|
return null;
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
|
-
function readPersisted() {
|
|
17
|
+
function readPersisted(fallback = {}) {
|
|
17
18
|
const storage = getLocalStorage();
|
|
18
19
|
if (storage === null)
|
|
19
|
-
return
|
|
20
|
+
return fallback;
|
|
20
21
|
try {
|
|
21
22
|
const rawValue = storage.getItem(COLLAPSED_SECTIONS_STORAGE_KEY);
|
|
22
23
|
if (rawValue === null)
|
|
@@ -32,7 +33,7 @@ function readPersisted() {
|
|
|
32
33
|
return state;
|
|
33
34
|
}
|
|
34
35
|
catch {
|
|
35
|
-
return
|
|
36
|
+
return fallback;
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
function writePersisted(state) {
|
|
@@ -56,23 +57,40 @@ function writePersisted(state) {
|
|
|
56
57
|
// Persistence is best effort and must not block the section from rendering.
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
+
function updatePersistedSection(current, key, collapsed) {
|
|
61
|
+
// localStorage is the cross-realm source of truth. Re-read it for every mutation so
|
|
62
|
+
// a bundle that cannot share this page's singleton still cannot overwrite newer keys.
|
|
63
|
+
const next = { ...readPersisted(current) };
|
|
64
|
+
if (collapsed)
|
|
65
|
+
next[key] = true;
|
|
66
|
+
else
|
|
67
|
+
delete next[key];
|
|
68
|
+
writePersisted(next);
|
|
69
|
+
return next;
|
|
70
|
+
}
|
|
71
|
+
function getSharedState() {
|
|
72
|
+
const registry = globalThis;
|
|
73
|
+
const existing = registry[SHARED_STATE_SYMBOL];
|
|
74
|
+
if (existing !== undefined)
|
|
75
|
+
return existing;
|
|
76
|
+
// Trusted Plugin bundles contain their own copy of this module. Symbol.for gives those
|
|
77
|
+
// copies and the host one live access to the same Svelte store in this renderer realm.
|
|
78
|
+
const shared = { store: writable(readPersisted()) };
|
|
79
|
+
registry[SHARED_STATE_SYMBOL] = shared;
|
|
80
|
+
return shared;
|
|
81
|
+
}
|
|
82
|
+
const { subscribe, set, update } = getSharedState().store;
|
|
60
83
|
export const collapsedSections = { subscribe };
|
|
61
84
|
export function isSectionCollapsed(state, key) {
|
|
62
85
|
return state[key] === true;
|
|
63
86
|
}
|
|
64
87
|
export function setSectionCollapsed(key, collapsed) {
|
|
65
|
-
update((current) =>
|
|
66
|
-
const next = { ...current, [key]: collapsed };
|
|
67
|
-
writePersisted(next);
|
|
68
|
-
return next;
|
|
69
|
-
});
|
|
88
|
+
update((current) => updatePersistedSection(current, key, collapsed));
|
|
70
89
|
}
|
|
71
90
|
export function toggleSection(key) {
|
|
72
91
|
update((current) => {
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
return next;
|
|
92
|
+
const latest = readPersisted(current);
|
|
93
|
+
return updatePersistedSection(latest, key, !isSectionCollapsed(latest, key));
|
|
76
94
|
});
|
|
77
95
|
}
|
|
78
96
|
export function clearCollapsedSections() {
|
package/dist/domain.d.ts
CHANGED
|
@@ -179,6 +179,7 @@ export interface MergeReadinessInfo extends MergeStatusInfo {
|
|
|
179
179
|
review_status?: string | null;
|
|
180
180
|
draft?: boolean;
|
|
181
181
|
is_queued?: boolean;
|
|
182
|
+
merge_queue_required?: boolean | null;
|
|
182
183
|
unaddressed_comment_count?: number;
|
|
183
184
|
head_sha?: string | null;
|
|
184
185
|
updated_at?: number | null;
|
package/dist/domain.js
CHANGED
|
@@ -199,7 +199,8 @@ export function getMergeReadiness(pr, options = {}) {
|
|
|
199
199
|
warnings.push(mergeReadinessDetail('unprotected_fallback', 'Using simple mergeability because no protected-branch checks or review state are available.'));
|
|
200
200
|
}
|
|
201
201
|
if (hasDirectMergeability || isUnprotectedFallback) {
|
|
202
|
-
|
|
202
|
+
const mergeQueueRequired = options.requireMergeQueue === true || pr.merge_queue_required === true;
|
|
203
|
+
return mergeReadinessResult(pr, mergeQueueRequired ? 'ready_to_enqueue' : 'ready_to_merge', mergeQueueRequired ? 'enqueue' : 'merge', blockers, warnings);
|
|
203
204
|
}
|
|
204
205
|
if (mergeableState === 'unknown' || pr.mergeable === null || (mergeableState === null && pr.mergeable !== false)) {
|
|
205
206
|
warnings.push(mergeReadinessDetail('mergeability_unknown', 'GitHub has not reported definitive mergeability yet.'));
|
package/dist/frontend.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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, FrontendReviewUIRegistry, FrontendTaskUIRegistry, FrontendViewRegistry, InjectionPointLocation, NavigationAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot,
|
|
2
|
+
import type { CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, Disposable, FrontendBackendBridge, FrontendOpenForgeAPI, FrontendPlugin, FrontendPluginContext, FrontendSettingsRegistry, FrontendTaskPaneRegistry, FrontendInjectionPointRegistry, FrontendTaskStartRegistry, FrontendReviewUIRegistry, FrontendTaskUIRegistry, FrontendViewRegistry, InjectionPointLocation, NavigationAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, PluginIcon, PluginSidebarNavigationProps, PluginSidebarViewIdentity, PluginInjectionPointProps, PluginInjectionPointRegistration, PluginSettingsSectionProps, PluginSettingsSectionRegistration, PluginSettingsSectionScope, PluginStorageScope, PluginSvgIcon, PluginTaskPaneProps, PluginTaskPaneTabRegistration, PluginReviewRowActionProps, PluginReviewRowActionRegistration, 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;
|
|
@@ -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, 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, FrontendReviewUIRegistry, FrontendTaskUIRegistry, FrontendViewRegistry, InjectionPointLocation, NavigationAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot,
|
|
10
|
+
export type { CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, Disposable, FrontendBackendBridge, FrontendOpenForgeAPI, FrontendPlugin, FrontendPluginContext, FrontendSettingsRegistry, FrontendTaskPaneRegistry, FrontendInjectionPointRegistry, FrontendTaskStartRegistry, FrontendReviewUIRegistry, FrontendTaskUIRegistry, FrontendViewRegistry, InjectionPointLocation, NavigationAPI, OpenForgeContextChangeHandler, OpenForgeContextSnapshot, OpenForgeNavigationRequest, OpenForgeNavigationSnapshot, PluginIcon, PluginSidebarNavigationProps, PluginSidebarViewIdentity, PluginInjectionPointProps, PluginInjectionPointRegistration, PluginSettingsSectionProps, PluginSettingsSectionRegistration, PluginSettingsSectionScope, PluginStorageScope, PluginSvgIcon, PluginTaskPaneProps, PluginTaskPaneTabRegistration, PluginReviewRowActionProps, PluginReviewRowActionRegistration, PluginTaskUISectionProps, PluginTaskUISectionRegistration, PluginViewProps, PluginViewRegistration, TaskStartPrefixContext, TaskStartPrefixProviderRegistration, PtyBufferState, TerminalImageProtocol, TerminalViewSnapshot, };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ 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 { 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, TerminalViewSnapshot, ShellSessionRequest, ShellSpawnRequest,
|
|
5
|
+
export { MAX_AGENT_SESSION_PAGE_SIZE, 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 { AgentSessionCursor, AgentSessionOverlap, AgentSessionsAPI, AgentSessionSummary, AgentSessionSummaryPage, AgentSessionTaskSummary, AgentSessionWorkspace, AgentCommandDescriptor, AgentCommandMetadata, AgentCommandRuntime, AttentionAPI, BackendReadyState, CommandDescriptor, CommandRegistry, CommandRegistration, CommandShortcutMetadata, ComposeTaskRequest, ComposeTaskResult, ConfigureStartPromptContributionRequest, CreateTaskRequest, Disposable, BackendFileSystemAPI, ExternalFileMetadata, ExternalReadDirectoryRequest, ExternalReadFileRequest, ExternalReadTextFileChunksRequest, ExternalReadFileSystemAPI, FileSystemAPI, UserDataDirectoryRequest, UserDataFileAppendResult, UserDataFileRequest, UserDataFileSystemAPI, UserDataFileWriteRequest, ImplementationRun, ListAgentSessionsRequest, ListTaskSessionsRequest, 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, ShellWriteRequest, StartPromptContribution, SendTaskFollowUpRequest, StartTaskImplementationRequest, TaskFollowUpDisposition, TaskFollowUpErrorCode, TaskFollowUpReceipt, SubscriptionSink, SupportedOpenForgeApiVersion, SystemAPI, 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/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 { 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';
|
|
4
|
+
export { MAX_AGENT_SESSION_PAGE_SIZE, 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
package/dist/manifest.js
CHANGED
|
@@ -28,6 +28,15 @@ function validateOptionalString(value, path) {
|
|
|
28
28
|
}
|
|
29
29
|
return [];
|
|
30
30
|
}
|
|
31
|
+
function validateBackendEntry(value) {
|
|
32
|
+
const errors = validateOptionalString(value, 'backend');
|
|
33
|
+
if (!isNonEmptyString(value))
|
|
34
|
+
return errors;
|
|
35
|
+
if (!['.mjs', '.js', '.cjs'].some(extension => value.endsWith(extension))) {
|
|
36
|
+
errors.push({ path: 'backend', message: 'Must point to a built .mjs, .js, or .cjs artifact' });
|
|
37
|
+
}
|
|
38
|
+
return errors;
|
|
39
|
+
}
|
|
31
40
|
function validateEnablement(value) {
|
|
32
41
|
if (value === undefined || value === 'app' || value === 'project') {
|
|
33
42
|
return [];
|
|
@@ -117,7 +126,7 @@ export function validateOpenForgePackageMetadata(data) {
|
|
|
117
126
|
if (data.frontendStyles !== undefined && !isNonEmptyString(data.frontend)) {
|
|
118
127
|
errors.push({ path: 'frontendStyles', message: 'Requires a frontend entry' });
|
|
119
128
|
}
|
|
120
|
-
errors.push(...
|
|
129
|
+
errors.push(...validateBackendEntry(data.backend));
|
|
121
130
|
errors.push(...validateRequires(data.requires));
|
|
122
131
|
if (data.enablement === 'app' && (!Array.isArray(data.requires) || !data.requires.includes('appEnablement'))) {
|
|
123
132
|
errors.push({ path: 'requires', message: 'App enablement requires the appEnablement capability' });
|
|
@@ -125,9 +134,6 @@ export function validateOpenForgePackageMetadata(data) {
|
|
|
125
134
|
if (Array.isArray(data.requires) && data.requires.includes('browserSurfaces') && !isNonEmptyString(data.frontend)) {
|
|
126
135
|
errors.push({ path: 'requires', message: 'browserSurfaces capability requires a frontend entry' });
|
|
127
136
|
}
|
|
128
|
-
if (Array.isArray(data.requires) && data.requires.includes('taskLinks') && !isNonEmptyString(data.frontend)) {
|
|
129
|
-
errors.push({ path: 'requires', message: 'taskLinks capability requires a frontend entry' });
|
|
130
|
-
}
|
|
131
137
|
if (data.contributes !== undefined) {
|
|
132
138
|
errors.push({ path: 'contributes', message: 'Manifest contribution arrays are not supported; register contributions at runtime' });
|
|
133
139
|
}
|
package/dist/markdown.d.ts
CHANGED
|
@@ -11,6 +11,15 @@ export interface ResolvedMarkdownMedia {
|
|
|
11
11
|
}
|
|
12
12
|
/** Attribute holding the original URL of a `<img>`/`<a>` awaiting resolution. */
|
|
13
13
|
export declare const MARKDOWN_REMOTE_MEDIA_ATTRIBUTE = "data-markdown-remote-src";
|
|
14
|
+
export interface RenderedMarkdownCacheStats {
|
|
15
|
+
capacity: number;
|
|
16
|
+
size: number;
|
|
17
|
+
hits: number;
|
|
18
|
+
misses: number;
|
|
19
|
+
evictions: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function getRenderedMarkdownCacheStats(): Readonly<RenderedMarkdownCacheStats>;
|
|
22
|
+
export declare function clearRenderedMarkdownCache(): void;
|
|
14
23
|
export declare function resolveMarkdownRepositoryPath(value: string | null, markdownFilePath: string): string | null;
|
|
15
24
|
export declare function getMarkdownRepositoryLinkSuffix(value: string): string;
|
|
16
25
|
export declare function resolveMarkdownImageSrc(src: string | null, imageBaseUrl: string | null | undefined, markdownFilePath?: string): string | null;
|
package/dist/markdown.js
CHANGED
|
@@ -6,6 +6,56 @@ const markedOptions = {
|
|
|
6
6
|
gfm: true,
|
|
7
7
|
breaks: true,
|
|
8
8
|
};
|
|
9
|
+
const RENDERED_MARKDOWN_CACHE_CAPACITY = 100;
|
|
10
|
+
const renderedMarkdownCache = new Map();
|
|
11
|
+
let renderedMarkdownCacheHits = 0;
|
|
12
|
+
let renderedMarkdownCacheMisses = 0;
|
|
13
|
+
let renderedMarkdownCacheEvictions = 0;
|
|
14
|
+
function renderedMarkdownCacheKey(content, options) {
|
|
15
|
+
return JSON.stringify([
|
|
16
|
+
content,
|
|
17
|
+
options.imageBaseUrl ?? null,
|
|
18
|
+
options.markdownFilePath ?? null,
|
|
19
|
+
Boolean(options.deferRepositoryImages),
|
|
20
|
+
Boolean(options.deferRemoteMedia),
|
|
21
|
+
]);
|
|
22
|
+
}
|
|
23
|
+
function readRenderedMarkdownCache(key) {
|
|
24
|
+
const cached = renderedMarkdownCache.get(key);
|
|
25
|
+
if (cached === undefined) {
|
|
26
|
+
renderedMarkdownCacheMisses++;
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
renderedMarkdownCacheHits++;
|
|
30
|
+
renderedMarkdownCache.delete(key);
|
|
31
|
+
renderedMarkdownCache.set(key, cached);
|
|
32
|
+
return cached;
|
|
33
|
+
}
|
|
34
|
+
function writeRenderedMarkdownCache(key, html) {
|
|
35
|
+
if (renderedMarkdownCache.size >= RENDERED_MARKDOWN_CACHE_CAPACITY) {
|
|
36
|
+
const oldestKey = renderedMarkdownCache.keys().next().value;
|
|
37
|
+
if (oldestKey !== undefined) {
|
|
38
|
+
renderedMarkdownCache.delete(oldestKey);
|
|
39
|
+
renderedMarkdownCacheEvictions++;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
renderedMarkdownCache.set(key, html);
|
|
43
|
+
}
|
|
44
|
+
export function getRenderedMarkdownCacheStats() {
|
|
45
|
+
return {
|
|
46
|
+
capacity: RENDERED_MARKDOWN_CACHE_CAPACITY,
|
|
47
|
+
size: renderedMarkdownCache.size,
|
|
48
|
+
hits: renderedMarkdownCacheHits,
|
|
49
|
+
misses: renderedMarkdownCacheMisses,
|
|
50
|
+
evictions: renderedMarkdownCacheEvictions,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function clearRenderedMarkdownCache() {
|
|
54
|
+
renderedMarkdownCache.clear();
|
|
55
|
+
renderedMarkdownCacheHits = 0;
|
|
56
|
+
renderedMarkdownCacheMisses = 0;
|
|
57
|
+
renderedMarkdownCacheEvictions = 0;
|
|
58
|
+
}
|
|
9
59
|
function hasAbsoluteOrSpecialUrl(value) {
|
|
10
60
|
return /^[a-z][a-z\d+.-]*:/i.test(value) || value.startsWith('//') || value.startsWith('#');
|
|
11
61
|
}
|
|
@@ -144,6 +194,12 @@ function prepareMarkdownMediaSources(html, options) {
|
|
|
144
194
|
return template.innerHTML;
|
|
145
195
|
}
|
|
146
196
|
export function renderMarkdownHtml(content, options = {}) {
|
|
197
|
+
const cacheKey = renderedMarkdownCacheKey(content, options);
|
|
198
|
+
const cached = readRenderedMarkdownCache(cacheKey);
|
|
199
|
+
if (cached !== undefined)
|
|
200
|
+
return cached;
|
|
147
201
|
const rawHtml = marked.parse(content, markedOptions);
|
|
148
|
-
|
|
202
|
+
const html = sanitizeHtml(prepareMarkdownMediaSources(rawHtml, options));
|
|
203
|
+
writeRenderedMarkdownCache(cacheKey, html);
|
|
204
|
+
return html;
|
|
149
205
|
}
|
|
@@ -19,15 +19,6 @@
|
|
|
19
19
|
},
|
|
20
20
|
"then": { "required": ["frontend"] }
|
|
21
21
|
},
|
|
22
|
-
{
|
|
23
|
-
"if": {
|
|
24
|
-
"properties": {
|
|
25
|
-
"requires": { "contains": { "const": "taskLinks" } }
|
|
26
|
-
},
|
|
27
|
-
"required": ["requires"]
|
|
28
|
-
},
|
|
29
|
-
"then": { "required": ["frontend"] }
|
|
30
|
-
},
|
|
31
22
|
{
|
|
32
23
|
"if": {
|
|
33
24
|
"properties": { "enablement": { "const": "app" } },
|
|
@@ -106,7 +97,8 @@
|
|
|
106
97
|
"backend": {
|
|
107
98
|
"type": "string",
|
|
108
99
|
"minLength": 1,
|
|
109
|
-
"
|
|
100
|
+
"pattern": "\\.(?:mjs|js|cjs)$",
|
|
101
|
+
"description": "Path to the built backend JavaScript artifact. Use .mjs for new plugins; .js and .cjs remain supported for compatibility. The host runs each backend in a replaceable worker."
|
|
110
102
|
},
|
|
111
103
|
"requires": {
|
|
112
104
|
"type": "array",
|
|
@@ -136,7 +128,6 @@
|
|
|
136
128
|
"config",
|
|
137
129
|
"projectConfig",
|
|
138
130
|
"browserSurfaces",
|
|
139
|
-
"taskLinks",
|
|
140
131
|
"appEnablement",
|
|
141
132
|
"customSidebarNavigation",
|
|
142
133
|
"reviewUI"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type OpenForgePluginSdkConditionalExport = Readonly<{
|
|
2
|
+
types: `./dist/${string}.d.ts`
|
|
3
|
+
default: `./dist/${string}.js`
|
|
4
|
+
}>
|
|
5
|
+
|
|
6
|
+
export type OpenForgePluginSdkPublicEntrypoint = Readonly<{
|
|
7
|
+
packageSubpath: '.' | `./${string}`
|
|
8
|
+
importSpecifier: '@openforge-app/plugin-sdk' | `@openforge-app/plugin-sdk/${string}`
|
|
9
|
+
sourcePath: `src/${string}`
|
|
10
|
+
workspaceSourcePath: `packages/plugin-sdk/src/${string}`
|
|
11
|
+
packageExport: string | OpenForgePluginSdkConditionalExport
|
|
12
|
+
}>
|
|
13
|
+
|
|
14
|
+
export const OPENFORGE_PLUGIN_SDK_PUBLIC_ENTRYPOINTS: readonly OpenForgePluginSdkPublicEntrypoint[]
|
|
15
|
+
|
|
16
|
+
export function createOpenForgePluginSdkPackageExports(): Record<
|
|
17
|
+
string,
|
|
18
|
+
string | { types: string; default: string }
|
|
19
|
+
>
|
|
20
|
+
|
|
21
|
+
export function createOpenForgePluginSdkTypeScriptPaths(): Record<string, [string]>
|
|
22
|
+
|
|
23
|
+
export function assertOpenForgePluginSdkEntrypointRegistries(registries: {
|
|
24
|
+
packageExports: unknown
|
|
25
|
+
typeScriptPaths: unknown
|
|
26
|
+
}): void
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from 'node:util'
|
|
2
|
+
import { OPENFORGE_PLUGIN_SDK_PUBLIC_UI_EXPORTS } from './publicUiExports.mjs'
|
|
3
|
+
|
|
4
|
+
const PLUGIN_SDK_PACKAGE_NAME = '@openforge-app/plugin-sdk'
|
|
5
|
+
|
|
6
|
+
function moduleEntrypoint(packageSubpath, sourceName) {
|
|
7
|
+
const importSuffix = packageSubpath === '.' ? '' : packageSubpath.slice(1)
|
|
8
|
+
return Object.freeze({
|
|
9
|
+
packageSubpath,
|
|
10
|
+
importSpecifier: `${PLUGIN_SDK_PACKAGE_NAME}${importSuffix}`,
|
|
11
|
+
sourcePath: `src/${sourceName}.ts`,
|
|
12
|
+
workspaceSourcePath: `packages/plugin-sdk/src/${sourceName}.ts`,
|
|
13
|
+
packageExport: Object.freeze({
|
|
14
|
+
types: `./dist/${sourceName}.d.ts`,
|
|
15
|
+
default: `./dist/${sourceName}.js`,
|
|
16
|
+
}),
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const PUBLIC_MODULE_ENTRYPOINTS = [
|
|
21
|
+
moduleEntrypoint('.', 'index'),
|
|
22
|
+
moduleEntrypoint('./frontend', 'frontend'),
|
|
23
|
+
moduleEntrypoint('./backend', 'backend'),
|
|
24
|
+
moduleEntrypoint('./testing', 'testing'),
|
|
25
|
+
moduleEntrypoint('./vite', 'vite'),
|
|
26
|
+
moduleEntrypoint('./domain', 'domain'),
|
|
27
|
+
moduleEntrypoint('./prStatusPresentation', 'prStatusPresentation'),
|
|
28
|
+
moduleEntrypoint('./markdown', 'markdown'),
|
|
29
|
+
moduleEntrypoint('./numberParsing', 'numberParsing'),
|
|
30
|
+
moduleEntrypoint('./projectFileTree', 'projectFileTree'),
|
|
31
|
+
moduleEntrypoint('./sanitize', 'sanitize'),
|
|
32
|
+
moduleEntrypoint('./pluginIcons', 'pluginIcons'),
|
|
33
|
+
moduleEntrypoint('./fileIcons', 'fileIcons'),
|
|
34
|
+
moduleEntrypoint('./collapsibleSectionState', 'collapsibleSectionState'),
|
|
35
|
+
moduleEntrypoint('./taskBrowserDevToolsShortcuts', 'taskBrowserDevToolsShortcuts'),
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
const PACKAGE_METADATA_SCHEMA_ENTRYPOINT = Object.freeze({
|
|
39
|
+
packageSubpath: './package-metadata-schema.json',
|
|
40
|
+
importSpecifier: `${PLUGIN_SDK_PACKAGE_NAME}/package-metadata-schema.json`,
|
|
41
|
+
sourcePath: 'src/openforgePackageMetadataSchema.json',
|
|
42
|
+
workspaceSourcePath: 'packages/plugin-sdk/src/openforgePackageMetadataSchema.json',
|
|
43
|
+
packageExport: './dist/openforgePackageMetadataSchema.json',
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const PUBLIC_UI_ENTRYPOINTS = OPENFORGE_PLUGIN_SDK_PUBLIC_UI_EXPORTS.map((entrypoint) => Object.freeze({
|
|
47
|
+
packageSubpath: entrypoint.packageSubpath,
|
|
48
|
+
importSpecifier: entrypoint.importSpecifier,
|
|
49
|
+
sourcePath: entrypoint.sourcePath,
|
|
50
|
+
workspaceSourcePath: entrypoint.workspaceSourcePath,
|
|
51
|
+
packageExport: entrypoint.distPath,
|
|
52
|
+
}))
|
|
53
|
+
|
|
54
|
+
/** Canonical registrations for every public Plugin SDK entrypoint. */
|
|
55
|
+
export const OPENFORGE_PLUGIN_SDK_PUBLIC_ENTRYPOINTS = Object.freeze([
|
|
56
|
+
...PUBLIC_MODULE_ENTRYPOINTS.slice(0, 5),
|
|
57
|
+
PACKAGE_METADATA_SCHEMA_ENTRYPOINT,
|
|
58
|
+
...PUBLIC_MODULE_ENTRYPOINTS.slice(5),
|
|
59
|
+
...PUBLIC_UI_ENTRYPOINTS,
|
|
60
|
+
])
|
|
61
|
+
|
|
62
|
+
export function createOpenForgePluginSdkPackageExports() {
|
|
63
|
+
return Object.fromEntries(
|
|
64
|
+
OPENFORGE_PLUGIN_SDK_PUBLIC_ENTRYPOINTS.map(({ packageSubpath, packageExport }) => [
|
|
65
|
+
packageSubpath,
|
|
66
|
+
typeof packageExport === 'string' ? packageExport : { ...packageExport },
|
|
67
|
+
]),
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function createOpenForgePluginSdkTypeScriptPaths() {
|
|
72
|
+
return Object.fromEntries(
|
|
73
|
+
OPENFORGE_PLUGIN_SDK_PUBLIC_ENTRYPOINTS.map(({ importSpecifier, workspaceSourcePath }) => [
|
|
74
|
+
importSpecifier,
|
|
75
|
+
[`./${workspaceSourcePath}`],
|
|
76
|
+
]),
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function assertOpenForgePluginSdkEntrypointRegistries({ packageExports, typeScriptPaths }) {
|
|
81
|
+
assertRegistryMatches('package exports', packageExports, createOpenForgePluginSdkPackageExports())
|
|
82
|
+
assertRegistryMatches('root TypeScript paths', typeScriptPaths, createOpenForgePluginSdkTypeScriptPaths())
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function assertRegistryMatches(registryName, actual, expected) {
|
|
86
|
+
if (!actual || typeof actual !== 'object' || Array.isArray(actual)) {
|
|
87
|
+
throw new Error(`Plugin SDK ${registryName} must be an object`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const missingOrMismatched = Object.entries(expected)
|
|
91
|
+
.filter(([key, value]) => !isDeepStrictEqual(actual[key], value))
|
|
92
|
+
.map(([key]) => key)
|
|
93
|
+
const unexpected = Object.keys(actual).filter((key) => !(key in expected))
|
|
94
|
+
|
|
95
|
+
if (missingOrMismatched.length === 0 && unexpected.length === 0) return
|
|
96
|
+
|
|
97
|
+
const details = [
|
|
98
|
+
missingOrMismatched.length > 0 ? `missing or mismatched: ${missingOrMismatched.join(', ')}` : null,
|
|
99
|
+
unexpected.length > 0 ? `not in the canonical manifest: ${unexpected.join(', ')}` : null,
|
|
100
|
+
].filter(Boolean)
|
|
101
|
+
|
|
102
|
+
throw new Error(`Plugin SDK ${registryName} drifted from the canonical manifest (${details.join('; ')})`)
|
|
103
|
+
}
|
package/dist/publicUiExports.mjs
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type TaskBrowserDevToolsShortcutPlatform = 'macos' | 'other';
|
|
2
|
+
export type TaskBrowserDevToolsShortcut = 'toggle' | 'elements' | 'console';
|
|
3
|
+
export interface TaskBrowserDevToolsShortcutInput {
|
|
4
|
+
key: string;
|
|
5
|
+
keyDown: boolean;
|
|
6
|
+
repeat: boolean;
|
|
7
|
+
control: boolean;
|
|
8
|
+
shift: boolean;
|
|
9
|
+
alt: boolean;
|
|
10
|
+
meta: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function classifyTaskBrowserDevToolsShortcut(platform: TaskBrowserDevToolsShortcutPlatform, input: TaskBrowserDevToolsShortcutInput): TaskBrowserDevToolsShortcut | null;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function classifyTaskBrowserDevToolsShortcut(platform, input) {
|
|
2
|
+
if (!input.keyDown || input.repeat)
|
|
3
|
+
return null;
|
|
4
|
+
if (input.key === 'f12')
|
|
5
|
+
return 'toggle';
|
|
6
|
+
if (input.key === 'c') {
|
|
7
|
+
const elementsModified = platform === 'macos'
|
|
8
|
+
? input.meta && input.shift && !input.control && !input.alt
|
|
9
|
+
: input.control && input.shift && !input.meta && !input.alt;
|
|
10
|
+
return elementsModified ? 'elements' : null;
|
|
11
|
+
}
|
|
12
|
+
const modified = platform === 'macos'
|
|
13
|
+
? input.meta && input.alt && !input.control && !input.shift
|
|
14
|
+
: input.control && input.shift && !input.meta && !input.alt;
|
|
15
|
+
if (!modified)
|
|
16
|
+
return null;
|
|
17
|
+
if (input.key === 'i')
|
|
18
|
+
return 'toggle';
|
|
19
|
+
return input.key === 'j' ? 'console' : null;
|
|
20
|
+
}
|