Skip to content

Commit e6386ff

Browse files
committed
chore: rename files
1 parent 7cb07fe commit e6386ff

File tree

2 files changed

+493
-0
lines changed

2 files changed

+493
-0
lines changed

src/editor.ts

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
import * as vscode from 'vscode';
2+
import { fileUriToScptUri } from './util.ts';
3+
4+
/**
5+
* Custom editor provider for binary AppleScript (.scpt) files
6+
*
7+
* Shows a warning card view before allowing users to edit the file.
8+
* When the user clicks "Edit File", it opens the file using the scpt:
9+
* FileSystemProvider for decompilation and editing.
10+
*/
11+
export class ScptEditorProvider implements vscode.CustomReadonlyEditorProvider<ScptDocument> {
12+
public static readonly viewType = 'applescript.binary';
13+
14+
constructor(
15+
private readonly context: vscode.ExtensionContext,
16+
// private readonly osaToolsAvailable: boolean,
17+
) {}
18+
19+
/**
20+
* Called when a .scpt file is opened
21+
*/
22+
async openCustomDocument(uri: vscode.Uri): Promise<ScptDocument> {
23+
return new ScptDocument(uri);
24+
}
25+
26+
/**
27+
* Called to resolve the custom editor view
28+
*/
29+
async resolveCustomEditor(document: ScptDocument, webviewPanel: vscode.WebviewPanel): Promise<void> {
30+
// Configure webview
31+
webviewPanel.webview.options = {
32+
enableScripts: true,
33+
};
34+
35+
// Set the HTML content
36+
webviewPanel.webview.html = this.getHtmlForWebview(webviewPanel.webview, document);
37+
38+
// Handle messages from the webview
39+
webviewPanel.webview.onDidReceiveMessage(async (message) => {
40+
switch (message.command) {
41+
case 'editFile':
42+
await this.openFileForEditing(document.uri, webviewPanel);
43+
break;
44+
45+
case 'cancel':
46+
// Just close the webview panel
47+
webviewPanel.dispose();
48+
break;
49+
}
50+
});
51+
}
52+
53+
/**
54+
* Opens the file for editing using the scpt: FileSystemProvider
55+
*/
56+
private async openFileForEditing(uri: vscode.Uri, webviewPanel: vscode.WebviewPanel): Promise<void> {
57+
if (!this.context) {
58+
vscode.window.showErrorMessage('Binary AppleScript files require macOS with osadecompile/osacompile tools');
59+
return;
60+
}
61+
62+
try {
63+
// Convert file: URI to scpt: URI for virtual filesystem
64+
const scptUri = fileUriToScptUri(uri);
65+
66+
// Open in editor with AppleScript language
67+
const doc = await vscode.workspace.openTextDocument(scptUri);
68+
await vscode.languages.setTextDocumentLanguage(doc, 'applescript');
69+
await vscode.window.showTextDocument(doc, {
70+
preview: false,
71+
viewColumn: webviewPanel.viewColumn,
72+
});
73+
74+
// Close the warning view
75+
webviewPanel.dispose();
76+
} catch (error) {
77+
vscode.window.showErrorMessage(`Failed to open file: ${error instanceof Error ? error.message : error}`);
78+
}
79+
}
80+
81+
/**
82+
* Generates a random nonce for CSP
83+
*/
84+
private getNonce(): string {
85+
let text = '';
86+
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
87+
for (let i = 0; i < 32; i++) {
88+
text += possible.charAt(Math.floor(Math.random() * possible.length));
89+
}
90+
return text;
91+
}
92+
93+
/**
94+
* Generates the HTML content for the warning view
95+
*/
96+
private getHtmlForWebview(webview: vscode.Webview, _document: ScptDocument): string {
97+
const nonce = this.getNonce();
98+
99+
return `<!DOCTYPE html>
100+
<html lang="en">
101+
<head>
102+
<meta charset="UTF-8">
103+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource} 'unsafe-inline'; script-src 'nonce-${nonce}';">
104+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
105+
<title>Binary AppleScript File</title>
106+
<style>
107+
body {
108+
font-family: var(--vscode-font-family);
109+
font-size: 14px;
110+
color: var(--vscode-foreground);
111+
background-color: var(--vscode-editor-background);
112+
margin: 0;
113+
padding: 0;
114+
overflow: hidden;
115+
}
116+
117+
.monaco-editor-pane-placeholder {
118+
display: flex;
119+
flex-direction: column;
120+
align-items: center;
121+
justify-content: center;
122+
height: 100vh;
123+
width: 100%;
124+
padding: 20px;
125+
}
126+
127+
.editor-placeholder-icon-container {
128+
margin-bottom: 16px;
129+
}
130+
131+
.warning-icon {
132+
width: 48px;
133+
height: 48px;
134+
color: var(--vscode-editorWarning-foreground);
135+
}
136+
137+
.editor-placeholder-label-container {
138+
width: 600px;
139+
max-width: 90%;
140+
}
141+
142+
.editor-placeholder-label-container span {
143+
line-height: 1.5;
144+
color: var(--vscode-foreground);
145+
}
146+
147+
.editor-placeholder-details {
148+
margin-top: 20px;
149+
margin-bottom: 20px;
150+
width: 600px;
151+
max-width: 90%;
152+
text-align: left;
153+
}
154+
155+
.editor-placeholder-details h3 {
156+
font-weight: 600;
157+
margin-bottom: 8px;
158+
color: var(--vscode-foreground);
159+
}
160+
161+
.editor-placeholder-details ul {
162+
list-style: none;
163+
padding: 0;
164+
margin: 0 0 16px 0;
165+
font-size: 12px;
166+
line-height: 1.6;
167+
color: var(--vscode-descriptionForeground);
168+
}
169+
170+
.editor-placeholder-details li {
171+
padding: 2px 0;
172+
padding-left: 16px;
173+
position: relative;
174+
}
175+
176+
.editor-placeholder-details li::before {
177+
content: "•";
178+
position: absolute;
179+
left: 4px;
180+
}
181+
182+
.editor-placeholder-buttons-container {
183+
display: flex;
184+
gap: 8px;
185+
flex-wrap: wrap;
186+
justify-content: center;
187+
}
188+
189+
.monaco-button {
190+
display: inline-block;
191+
padding: 4px 14px;
192+
border: none;
193+
border-radius: 2px;
194+
font-family: var(--vscode-font-family);
195+
cursor: pointer;
196+
text-decoration: none;
197+
outline-offset: 2px;
198+
}
199+
200+
.monaco-text-button {
201+
color: var(--vscode-button-foreground);
202+
background-color: var(--vscode-button-background);
203+
}
204+
205+
.monaco-text-button:hover {
206+
background-color: var(--vscode-button-hoverBackground);
207+
}
208+
209+
.monaco-text-button:focus {
210+
outline: 1px solid var(--vscode-focusBorder);
211+
}
212+
213+
.monaco-button-secondary {
214+
color: var(--vscode-button-secondaryForeground);
215+
background-color: var(--vscode-button-secondaryBackground);
216+
}
217+
218+
.monaco-button-secondary:hover {
219+
background-color: var(--vscode-button-secondaryHoverBackground);
220+
}
221+
222+
.monaco-button-secondary:focus {
223+
outline: 1px solid var(--vscode-focusBorder);
224+
}
225+
</style>
226+
</head>
227+
<body>
228+
<div class="monaco-editor-pane-placeholder" tabindex="0" aria-label="Binary AppleScript file. The file is not displayed because it needs to be decompiled first.">
229+
<div class="editor-placeholder-icon-container">
230+
<svg class="warning-icon" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
231+
<path d="M7.56 1h.88l6.54 12.26-.44.74H1.44L1 13.26 7.56 1zM8 2.28L2.28 13H13.7L8 2.28zM8.625 12v-1h-1.25v1h1.25zm-1.25-2V6h1.25v4h-1.25z"/>
232+
</svg>
233+
</div>
234+
<div class="editor-placeholder-label-container">
235+
<span>The file is not displayed in the text editor because it is binary AppleScript. It will be decompiled when you click on "Edit File".</span>
236+
</div>
237+
<div class="editor-placeholder-details">
238+
<h3>What happens when you edit:</h3>
239+
<ul>
240+
<li>The file will be decompiled using <code>osadecompile</code></li>
241+
<li>You can edit the source code in the editor</li>
242+
<li>On save, the code will be compiled back to binary using <code>osacompile</code></li>
243+
</ul>
244+
<h3>Things to note:</h3>
245+
<ul>
246+
<li>Decompiled code may differ slightly from the original source</li>
247+
<li>Comments and formatting might not be preserved</li>
248+
<li>Compilation errors will prevent saving</li>
249+
</ul>
250+
</div>
251+
<div class="editor-placeholder-buttons-container">
252+
<a id="editButton" class="monaco-button monaco-text-button" tabindex="0" role="button" aria-disabled="false">Edit File</a>
253+
<a id="cancelButton" class="monaco-button monaco-button-secondary" tabindex="0" role="button" aria-disabled="false">Cancel</a>
254+
</div>
255+
</div>
256+
257+
<script nonce="${nonce}">
258+
const vscode = acquireVsCodeApi();
259+
260+
document.getElementById('editButton').addEventListener('click', function() {
261+
vscode.postMessage({ command: 'editFile' });
262+
});
263+
264+
document.getElementById('cancelButton').addEventListener('click', function() {
265+
vscode.postMessage({ command: 'cancel' });
266+
});
267+
</script>
268+
</body>
269+
</html>`;
270+
}
271+
}
272+
273+
/**
274+
* Represents a .scpt document in the custom editor
275+
*/
276+
class ScptDocument implements vscode.CustomDocument {
277+
constructor(public readonly uri: vscode.Uri) {}
278+
279+
dispose(): void {
280+
// No resources to clean up
281+
}
282+
}

0 commit comments

Comments
 (0)