3.8 KB122 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import type {Json} from 'shared/typeUtils';
9import type {Platform} from '../platform';
10import type {OneIndexedLineNumber, PlatformName, RepoRelativePath} from '../types';
11
12import {LocalWebSocketEventBus} from '../LocalWebSocketEventBus';
13import {computeInitialParams} from '../urlParams';
14
15// important: this file should not try to import other code from 'isl',
16// since it will end up getting duplicated when bundling.
17
18export function browserClipboardCopy(text: string, html?: string) {
19 if (html) {
20 const htmlBlob = new Blob([html], {type: 'text/html'});
21 const textBlob = new Blob([text], {type: 'text/plain'});
22 const clipboardItem = new window.ClipboardItem({
23 'text/html': htmlBlob,
24 'text/plain': textBlob,
25 });
26 navigator.clipboard.write([clipboardItem]);
27 } else {
28 navigator.clipboard.writeText(text);
29 }
30}
31
32export const makeBrowserLikePlatformImpl = (platformName: PlatformName): Platform => {
33 const initialUrlParams = computeInitialParams(platformName === 'browser');
34 return {
35 platformName,
36 confirm: (message: string, details?: string) => {
37 const ok = window.confirm(message + '\n' + (details ?? ''));
38 return Promise.resolve(ok);
39 },
40
41 openFile: (path: RepoRelativePath, options?: {line?: OneIndexedLineNumber}) => {
42 window.clientToServerAPI?.postMessage({type: 'platform/openFile', path, options});
43 },
44 openFiles: (paths: Array<RepoRelativePath>, options?: {line?: OneIndexedLineNumber}) => {
45 window.clientToServerAPI?.postMessage({type: 'platform/openFiles', paths, options});
46 },
47 canCustomizeFileOpener: true,
48 upsellExternalMergeTool: true,
49
50 openContainingFolder: (path: RepoRelativePath) => {
51 window.clientToServerAPI?.postMessage({type: 'platform/openContainingFolder', path});
52 },
53
54 openExternalLink(url: string): void {
55 window.open(url, '_blank');
56 },
57
58 getPersistedState<T>(key: string): T | null {
59 try {
60 const found = localStorage.getItem(key) as string | null;
61 if (found == null) {
62 return null;
63 }
64 return JSON.parse(found) as T;
65 } catch {
66 return null;
67 }
68 },
69 setPersistedState<T>(key: string, value: T | undefined): void {
70 try {
71 if (value === undefined) {
72 localStorage.removeItem(key);
73 } else {
74 localStorage.setItem(key, JSON.stringify(value));
75 }
76 } catch {}
77 },
78 clearPersistedState(): void {
79 try {
80 localStorage.clear();
81 } catch {}
82 },
83 getAllPersistedState(): Json | undefined {
84 try {
85 return Object.fromEntries(
86 Object.entries({...localStorage})
87 .map(([key, value]: [string, unknown]) => {
88 try {
89 return [key, JSON.parse(value as string)];
90 } catch {
91 return null;
92 }
93 })
94 .filter((e): e is [string, Json] => e != null),
95 );
96 } catch {
97 return undefined;
98 }
99 },
100
101 clipboardCopy: browserClipboardCopy,
102
103 messageBus: new LocalWebSocketEventBus(
104 process.env.NODE_ENV === 'development'
105 ? // in dev mode, Vite hosts our files for hot-reloading.
106 // This means we can't host the ws server on the same port as the page.
107 'localhost:3001'
108 : // in production, we serve both the static files and ws from the same port
109 location.host,
110 WebSocket,
111 {
112 cwd: initialUrlParams.get('cwd'),
113 sessionId: initialUrlParams.get('sessionId'),
114 token: initialUrlParams.get('token'),
115 platformName,
116 },
117 ),
118
119 initialUrlParams,
120 };
121};
122