Skip to content

Commit c92bc4c

Browse files
committed
feat: Add Custom File Type Handler API
1 parent 12383ff commit c92bc4c

File tree

4 files changed

+145
-5
lines changed

4 files changed

+145
-5
lines changed

src/lib/acode.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import actionStack from "lib/actionStack";
2323
import commands from "lib/commands";
2424
import EditorFile from "lib/editorFile";
2525
import files from "lib/fileList";
26+
import fileTypeHandler from "lib/fileTypeHandler";
2627
import fonts from "lib/fonts";
2728
import NotificationManager from "lib/notificationManager";
2829
import openFolder from "lib/openFolder";
@@ -495,4 +496,23 @@ export default class Acode {
495496
type,
496497
});
497498
}
499+
500+
/**
501+
* Register a custom file type handler
502+
* @param {string} id Unique identifier for the handler
503+
* @param {Object} options Handler configuration
504+
* @param {string[]} options.extensions File extensions to handle (without dots)
505+
* @param {function} options.handleFile Function that handles the file opening
506+
*/
507+
registerFileHandler(id, options) {
508+
fileTypeHandler.registerFileHandler(id, options);
509+
}
510+
511+
/**
512+
* Unregister a file type handler
513+
* @param {string} id The handler id to remove
514+
*/
515+
unregisterFileHandler(id) {
516+
fileTypeHandler.unregisterFileHandler(id);
517+
}
498518
}

src/lib/fileTypeHandler.js

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/**
2+
* @typedef {Object} FileTypeHandler
3+
* @property {string} id - Unique identifier for the handler
4+
* @property {string[]} extensions - File extensions this handler supports (without dots)
5+
* @property {function} handleFile - Function that handles the file
6+
*/
7+
8+
/**
9+
* @typedef {Object} FileInfo
10+
* @property {string} name - File name
11+
* @property {string} uri - File URI
12+
* @property {Object} fs - File system operations object
13+
* @property {Object} stats - File stats
14+
* @property {boolean} readOnly - Whether the file is read-only
15+
* @property {Object} options - Additional options passed during file open
16+
*/
17+
18+
class FileTypeHandlerRegistry {
19+
#handlers = new Map();
20+
21+
/**
22+
* Register a file type handler
23+
* @param {string} id - Unique identifier for the handler
24+
* @param {Object} options - Handler options
25+
* @param {string[]} options.extensions - File extensions to handle (without dots)
26+
* @param {function(FileInfo): Promise<void>} options.handleFile - Async function to handle the file
27+
* @throws {Error} If id is already registered or required options are missing
28+
*/
29+
registerFileHandler(id, { extensions, handleFile }) {
30+
if (this.#handlers.has(id)) {
31+
throw new Error(`Handler with id '${id}' is already registered`);
32+
}
33+
34+
if (!extensions?.length) {
35+
throw new Error("extensions array is required");
36+
}
37+
38+
if (typeof handleFile !== "function") {
39+
throw new Error("handleFile function is required");
40+
}
41+
42+
// Normalize extensions (remove dots if present, convert to lowercase)
43+
const normalizedExts = extensions.map((ext) =>
44+
ext.toLowerCase().replace(/^\./, ""),
45+
);
46+
47+
this.#handlers.set(id, {
48+
extensions: normalizedExts,
49+
handleFile,
50+
});
51+
}
52+
53+
/**
54+
* Unregister a file type handler
55+
* @param {string} id - The handler id to remove
56+
*/
57+
unregisterFileHandler(id) {
58+
this.#handlers.delete(id);
59+
}
60+
61+
/**
62+
* Get a file handler for a given filename
63+
* @param {string} filename
64+
* @returns {Object|null} The matching handler or null if none found
65+
*/
66+
getFileHandler(filename) {
67+
const ext = filename.split(".").pop().toLowerCase();
68+
69+
for (const [id, handler] of this.#handlers) {
70+
if (
71+
handler.extensions.includes(ext) ||
72+
handler.extensions.includes("*")
73+
) {
74+
return {
75+
id,
76+
...handler,
77+
};
78+
}
79+
}
80+
81+
return null;
82+
}
83+
84+
/**
85+
* Get all registered handlers
86+
* @returns {Map} Map of all registered handlers
87+
*/
88+
getHandlers() {
89+
return new Map(this.#handlers);
90+
}
91+
}
92+
93+
export const fileTypeHandler = new FileTypeHandlerRegistry();
94+
export default fileTypeHandler;

src/lib/openFile.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { reopenWithNewEncoding } from "palettes/changeEncoding";
77
import { decode } from "utils/encodings";
88
import helpers from "utils/helpers";
99
import EditorFile from "./editorFile";
10+
import fileTypeHandler from "./fileTypeHandler";
1011
import recents from "./recents";
1112
import appSettings from "./settings";
1213

@@ -97,6 +98,31 @@ export default async function openFile(file, options = {}) {
9798
});
9899
};
99100

101+
// Check for registered file handlers
102+
const customHandler = fileTypeHandler.getFileHandler(name);
103+
if (customHandler) {
104+
try {
105+
await customHandler.handleFile({
106+
name,
107+
uri,
108+
stats: fileInfo,
109+
readOnly,
110+
options: {
111+
cursorPos,
112+
render,
113+
onsave,
114+
encoding,
115+
mode,
116+
createEditor,
117+
},
118+
});
119+
return;
120+
} catch (error) {
121+
console.error(`File handler '${customHandler.id}' failed:`, error);
122+
// Continue with default handling if custom handler fails
123+
}
124+
}
125+
100126
if (text) {
101127
// If file is not opened and has unsaved text
102128
createEditor(true, text);

www/index.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,17 +165,17 @@
165165

166166
<title>Acode</title>
167167
<!--styles-->
168+
<link rel="stylesheet" href="./css/build/218.css">
169+
<link rel="stylesheet" href="./css/build/32.css">
170+
<link rel="stylesheet" href="./css/build/383.css">
171+
<link rel="stylesheet" href="./css/build/53.css">
172+
<link rel="stylesheet" href="./css/build/609.css">
168173
<link rel="stylesheet" href="./css/build/about.css">
169174
<link rel="stylesheet" href="./css/build/customTheme.css">
170175
<link rel="stylesheet" href="./css/build/donate.css">
171176
<link rel="stylesheet" href="./css/build/fileBrowser.css">
172177
<link rel="stylesheet" href="./css/build/main.css">
173178
<link rel="stylesheet" href="./css/build/plugins.css">
174-
<link rel="stylesheet" href="./css/build/src_pages_quickTools_quickTools_js.css">
175-
<link rel="stylesheet" href="./css/build/src_sidebarApps_extensions_index_js.css">
176-
<link rel="stylesheet" href="./css/build/src_sidebarApps_files_index_js.css">
177-
<link rel="stylesheet" href="./css/build/src_sidebarApps_notification_index_js.css">
178-
<link rel="stylesheet" href="./css/build/src_sidebarApps_searchInFiles_index_js.css">
179179
<link rel="stylesheet" href="./css/build/themeSetting.css">
180180
<!--styles_end-->
181181
</head>

0 commit comments

Comments
 (0)