mirror of
https://github.com/nick-de-bruin/obsidian-extended-file-support.git
synced 2026-07-22 12:00:32 +00:00
Compare commits
4 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7f531517a | ||
|
|
6b6f37d903 | ||
|
|
d2dc1ef4cb | ||
|
|
fcd8f9daf6 |
10 changed files with 2919 additions and 297 deletions
|
|
@ -21,6 +21,7 @@ The file types that are currently supported are:
|
|||
- `.kra` (Files made in Krita)
|
||||
- `.psd` (Photoshop, other visual programs)
|
||||
- `.ai` (Adobe Illustrator)
|
||||
- `.clip` (Clip Studio Paint)
|
||||
- `.gltf`, `.glb` (3D scene format)
|
||||
- `.obj` (3D object format)
|
||||
- `.stl` (3D format, often used for 3d printing)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import esbuild from "esbuild";
|
||||
import process from "process";
|
||||
import builtins from "builtin-modules";
|
||||
import { readFileSync } from "fs";
|
||||
import { join } from 'path';
|
||||
|
||||
const banner =
|
||||
`/*
|
||||
|
|
@ -11,6 +13,33 @@ if you want to view the source, please visit the github repository of this plugi
|
|||
|
||||
const prod = (process.argv[2] === "production");
|
||||
|
||||
const wasmPlugin = {
|
||||
name: 'wasm',
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /\.wasm$/ }, args => {
|
||||
if (args.resolveDir === '') return;
|
||||
return {
|
||||
path: join(args.resolveDir, args.path),
|
||||
namespace: 'wasm-binary',
|
||||
};
|
||||
});
|
||||
|
||||
build.onLoad({ filter: /\.wasm$/, namespace: 'wasm-binary' }, async (args) => {
|
||||
const contents = readFileSync(args.path);
|
||||
const wasmBase64 = contents.toString('base64');
|
||||
|
||||
return {
|
||||
contents: `
|
||||
const wasmBase64 = "${wasmBase64}";
|
||||
const wasmBinary = Uint8Array.from(atob(wasmBase64), c => c.charCodeAt(0));
|
||||
export default wasmBinary;
|
||||
`,
|
||||
loader: 'js',
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const context = await esbuild.context({
|
||||
banner: {
|
||||
js: banner,
|
||||
|
|
@ -38,6 +67,9 @@ const context = await esbuild.context({
|
|||
sourcemap: prod ? false : "inline",
|
||||
treeShaking: true,
|
||||
outfile: "main.js",
|
||||
plugins: [
|
||||
wasmPlugin
|
||||
],
|
||||
minify: prod,
|
||||
});
|
||||
|
||||
|
|
|
|||
11
main.ts
11
main.ts
|
|
@ -97,6 +97,17 @@ class ExtendedFileSupportSettingTab extends PluginSettingTab {
|
|||
this.plugin.toggleExtension("psd", value);
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(".clip")
|
||||
.setDesc("Files generated by Clip Studio Paint.")
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.clip)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.clip = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.toggleExtension("clip", value);
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(".kra")
|
||||
.setDesc("Files generated by Krita.")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "extended-file-support",
|
||||
"name": "Extended File Support",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.1",
|
||||
"minAppVersion": "0.18.0",
|
||||
"description": "Adds file support for various file types. Allows viewing and embedding these filetypes. Includes: .kra, .psd, .obj, .glb, .gltf, and more.",
|
||||
"author": "Nick de Bruin",
|
||||
|
|
|
|||
3044
package-lock.json
generated
3044
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "extended-file-support",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.1",
|
||||
"description": "Extended file support for Obsidian",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
"devDependencies": {
|
||||
"@types/node": "^16.11.6",
|
||||
"@types/pdfjs-dist": "^2.10.378",
|
||||
"@types/sql.js": "^1.4.9",
|
||||
"@types/three": "^0.172.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.29.0",
|
||||
"@typescript-eslint/parser": "5.29.0",
|
||||
|
|
@ -27,6 +28,7 @@
|
|||
"dependencies": {
|
||||
"@webtoon/psd": "^0.4.0",
|
||||
"@zip.js/zip.js": "^2.6.40",
|
||||
"sql.js": "^1.13.0",
|
||||
"three": "^0.172.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
114
src/extensions/clip.ts
Normal file
114
src/extensions/clip.ts
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import { AltTextParsed, ExtensionComponent } from "src/extensionComponent";
|
||||
import { ExtensionView } from "src/extensionView";
|
||||
|
||||
import initSqlJs from 'sql.js'
|
||||
// @ts-ignore
|
||||
import wasmBinary from '../../node_modules/sql.js/dist/sql-wasm.wasm';
|
||||
|
||||
export const VIEW_TYPE_CLIP = "extended-file-support-clip";
|
||||
|
||||
export class CLIPComponent extends ExtensionComponent {
|
||||
private sql: any = null
|
||||
private objectURL?: string;
|
||||
|
||||
parseLinkText(_: AltTextParsed): void { }
|
||||
|
||||
private async getSql() {
|
||||
if (!this.sql) {
|
||||
try{
|
||||
this.sql = await initSqlJs({
|
||||
wasmBinary: wasmBinary
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to load sql.js WebAssembly file required to read .clip files.', error);
|
||||
this.sql = false;
|
||||
}
|
||||
}
|
||||
return this.sql;
|
||||
}
|
||||
|
||||
async loadFile(): Promise<void> {
|
||||
const resource = this.plugin.app.vault.getResourcePath(this.file);
|
||||
const res = await fetch(resource);
|
||||
const arrayBuffer = await res.arrayBuffer();
|
||||
const offset = this.getDatabaseOffset(arrayBuffer);
|
||||
|
||||
if (offset !== -1) {
|
||||
const sqliteDB = arrayBuffer.slice(offset);
|
||||
const SQL = await this.getSql();
|
||||
try{
|
||||
const db = new SQL.Database(new Uint8Array(sqliteDB));
|
||||
const result = db.exec("SELECT imageData FROM CanvasPreview");
|
||||
if ( !result || result.length === 0 ) {
|
||||
throw new Error("No image data found in CanvasPreview table");
|
||||
}
|
||||
|
||||
const imageBytes = result[0].values[0][0] as Uint8Array;
|
||||
const image_file = new Blob([imageBytes]);
|
||||
this.objectURL = URL.createObjectURL(image_file);
|
||||
} catch (error) {
|
||||
console.error('Error accessing imageData from sqlite database.', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.objectURL) {
|
||||
const image = new Image();
|
||||
image.src = this.objectURL;
|
||||
image.alt = this.file.name;
|
||||
if (this.width) {
|
||||
image.width = this.width;
|
||||
}
|
||||
if (this.height) {
|
||||
image.height = this.height;
|
||||
}
|
||||
this.contentEl.empty();
|
||||
this.contentEl.removeClass("extended-file-loading");
|
||||
this.contentEl.addClasses(["media-embed", "image-embed"]);
|
||||
this.contentEl.append(image);
|
||||
} else {
|
||||
this.contentEl.empty();
|
||||
this.contentEl.createEl("i", { text: `Could not load ${this.file.path}` });
|
||||
}
|
||||
}
|
||||
|
||||
cleanup(): void {
|
||||
if (this.objectURL) {
|
||||
URL.revokeObjectURL(this.objectURL);
|
||||
this.objectURL = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
getDatabaseOffset(arrayBuffer: ArrayBuffer): number {
|
||||
const haystack = new Uint8Array(arrayBuffer);
|
||||
const needle = new TextEncoder().encode('SQLite format 3\0');
|
||||
|
||||
for (let i = 0; i <= haystack.length - needle.length; i++) {
|
||||
let found = true;
|
||||
for (let j = 0; j < needle.length; j++) {
|
||||
if (haystack[i + j] !== needle[j]) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
export class CLIPView extends ExtensionView<CLIPComponent> {
|
||||
getIcon(): string {
|
||||
return "image";
|
||||
}
|
||||
|
||||
getComponent(): new (...args: ConstructorParameters<typeof ExtensionComponent>) => CLIPComponent {
|
||||
return CLIPComponent;
|
||||
}
|
||||
|
||||
getViewType(): string {
|
||||
return VIEW_TYPE_CLIP;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import { ExtensionComponent } from "./extensionComponent"
|
|||
import { OBJComponent, OBJView, VIEW_TYPE_OBJ } from "./extensions/obj"
|
||||
import { GLBComponent, GLBView, VIEW_TYPE_GLB } from "./extensions/glb"
|
||||
import { PSDComponent, PSDView, VIEW_TYPE_PSD } from "./extensions/psd"
|
||||
import { CLIPComponent, CLIPView, VIEW_TYPE_CLIP } from "./extensions/clip"
|
||||
import { STLComponent, STLView, VIEW_TYPE_STL } from "./extensions/stl"
|
||||
import { AIComponent, AIView, VIEW_TYPE_AI } from "./extensions/ai"
|
||||
|
||||
|
|
@ -17,6 +18,7 @@ export type Extension = {
|
|||
// Type should match settings field
|
||||
export const EXTENSION_REGISTRY: Extension[] = [
|
||||
{ types: ["kra"], view_type: VIEW_TYPE_KRA, view: KRAView, component: KRAComponent },
|
||||
{ types: ["clip"], view_type: VIEW_TYPE_CLIP, view: CLIPView, component: CLIPComponent },
|
||||
{ types: ["obj"], view_type: VIEW_TYPE_OBJ, view: OBJView, component: OBJComponent },
|
||||
{ types: ["glb", "gltf"], view_type: VIEW_TYPE_GLB, view: GLBView, component: GLBComponent },
|
||||
{ types: ["psd"], view_type: VIEW_TYPE_PSD, view: PSDView, component: PSDComponent },
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export interface ExtendedFileSupportSettings {
|
||||
kra: boolean;
|
||||
clip: boolean;
|
||||
psd: boolean;
|
||||
ai: boolean;
|
||||
ai_render_scale: number;
|
||||
|
|
@ -14,6 +15,7 @@ export interface ExtendedFileSupportSettings {
|
|||
|
||||
export const DEFAULT_SETTINGS: ExtendedFileSupportSettings = {
|
||||
kra: true,
|
||||
clip: true,
|
||||
psd: true,
|
||||
ai: true,
|
||||
ai_render_scale: 1.5,
|
||||
|
|
|
|||
|
|
@ -4,5 +4,7 @@
|
|||
"1.2.1": "0.18.0",
|
||||
"1.2.4": "0.18.0",
|
||||
"1.2.5": "0.18.0",
|
||||
"1.3.0": "0.18.0"
|
||||
"1.3.0": "0.18.0",
|
||||
"1.4.0": "0.18.0",
|
||||
"1.4.1": "0.18.0"
|
||||
}
|
||||
Loading…
Reference in a new issue