mirror of
https://github.com/nick-de-bruin/obsidian-extended-file-support.git
synced 2026-07-22 12:00:32 +00:00
Compare commits
14 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7f531517a | ||
|
|
6b6f37d903 | ||
|
|
d2dc1ef4cb | ||
|
|
fcd8f9daf6 | ||
|
|
a0ed4b06ad | ||
|
|
3fe799c975 | ||
|
|
926932a7cc | ||
|
|
4e976fb48b | ||
|
|
4a8c2bfb8a | ||
|
|
57191175f1 | ||
|
|
5308033e31 | ||
|
|
a5e9d0d94a | ||
|
|
3790aa24de | ||
|
|
6bbc919f72 |
21 changed files with 3485 additions and 613 deletions
30
README.md
30
README.md
|
|
@ -6,6 +6,14 @@ This is considered to be outside of the scope of this plugin.
|
|||
|
||||
File types can be toggled in the settings.
|
||||
|
||||

|
||||
|
||||
*`.kra` file provided and made by Tio Tzen Ann ([Artstation](https://www.artstation.com/greasetanker), [Instagram](https://www.instagram.com/bullyinggnomes/)).*
|
||||
|
||||

|
||||
|
||||
*Also works in Canvas.*
|
||||
|
||||
# Currently Supported
|
||||
|
||||
The file types that are currently supported are:
|
||||
|
|
@ -13,10 +21,32 @@ 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)
|
||||
|
||||
## Settings
|
||||
|
||||
When embedding files, the following settings can be added as `![[file.ext|setting1=value;setting2=value]]` or ``.
|
||||
|
||||
**All files**:
|
||||
|
||||
- `width` or `widthxheight` in which both `width` and `height` are numbers. Follows the way images are embedded in Obsidian, and allows for the width and height of the embed to be changed.
|
||||
|
||||
**3d files** (`.obj`, `.gltf`, `.glb`, `.stl`):
|
||||
|
||||
- `spin=true` or `spin=false`, overrides the default setting on whether to animate 3d models.
|
||||
- `camPos=x,y,z` in which `x`, `y`, and `z` are the coordinates the camera should have.
|
||||
- `camLookAt=x,y,z` in which `x`, `y`, and `z` are the coordinates the camera should look at.
|
||||
|
||||
Note that with spin/animation enabled, `camPos` will not work, and `camLookAt` may not have the desired behavior, because the camera moves around.
|
||||
Additionally, 3d objects that are loaded in are placed on `(0, 0, 0)` and scaled to fit in a unit cube.
|
||||
|
||||
**`.ai` files**:
|
||||
|
||||
- `scale=number`, will override the default setting for `.ai` file scaling. High values might cause high loading time.
|
||||
|
||||
## Contributing
|
||||
|
||||
If you wish to contribute to the plugin, feel free to open a pull-request or an issue.
|
||||
|
|
|
|||
BIN
assets/embed_canvas.png
Normal file
BIN
assets/embed_canvas.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 138 KiB |
BIN
assets/embed_kra.png
Normal file
BIN
assets/embed_kra.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 462 KiB |
|
|
@ -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,
|
||||
});
|
||||
|
||||
|
|
|
|||
17
main.ts
17
main.ts
|
|
@ -21,7 +21,7 @@ export default class ExtendedFileSupport extends Plugin {
|
|||
this.registerExtensions([extension_type], extension.view_type);
|
||||
|
||||
embedRegistry.registerExtension(extension_type, (context, file, _) => {
|
||||
return new extension.component(context.containerEl, this, file, context.linktext);
|
||||
return new extension.component(context.containerEl, this, file, context.containerEl.getAttr('alt'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ export default class ExtendedFileSupport extends Plugin {
|
|||
this.registerExtensions(e.types, e.view_type);
|
||||
|
||||
embedRegistry.registerExtension(extension, (context, file, _) => {
|
||||
return new e.component(context.containerEl, this, file, context.linktext);
|
||||
return new e.component(context.containerEl, this, file, context.containerEl.getAttr('alt'));
|
||||
});
|
||||
} else {
|
||||
this.app.viewRegistry.unregisterExtensions([extension]);
|
||||
|
|
@ -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.")
|
||||
|
|
@ -121,7 +132,7 @@ class ExtendedFileSupportSettingTab extends PluginSettingTab {
|
|||
|
||||
new Setting(containerEl)
|
||||
.setName(".ai render scale")
|
||||
.setDesc("Render scale for Illustrator files. Higher means higher resolution, but longer load times. Default: 1.")
|
||||
.setDesc("Render scale for Illustrator files. Higher means higher resolution, but longer load times. Default: 1.5.")
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOptions({"0.5": "0.5", "1.0": "1.0", "1.5": "1.5", "2.0": "2.0", "2.5": "2.5", "3.0": "3.0"})
|
||||
.setValue(this.plugin.settings.ai_render_scale.toFixed(1))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "extended-file-support",
|
||||
"name": "Extended File Support",
|
||||
"version": "1.2.1",
|
||||
"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",
|
||||
|
|
|
|||
3649
package-lock.json
generated
3649
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "extended-file-support",
|
||||
"version": "1.2.1",
|
||||
"version": "1.4.1",
|
||||
"description": "Extended file support for Obsidian",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
@ -14,11 +14,12 @@
|
|||
"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",
|
||||
"builtin-modules": "3.3.0",
|
||||
"esbuild": "0.17.3",
|
||||
"esbuild": "0.25.0",
|
||||
"obsidian": "latest",
|
||||
"obsidian-typings": "^2.11.1",
|
||||
"tslib": "2.4.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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ExtensionComponent } from "src/extensionComponent";
|
||||
import { AltTextParsed, ExtensionComponent } from "src/extensionComponent";
|
||||
import { ExtensionView } from "src/extensionView";
|
||||
import * as THREE from 'three';
|
||||
|
||||
|
|
@ -8,23 +8,69 @@ export abstract class ThreeJSComponent extends ExtensionComponent {
|
|||
renderer?: THREE.WebGLRenderer;
|
||||
scene?: THREE.Scene;
|
||||
resizeObserver?: ResizeObserver;
|
||||
spin: boolean;
|
||||
camLookAt: THREE.Vector3;
|
||||
camPos: THREE.Vector3;
|
||||
|
||||
abstract loadModel(resource: string): void;
|
||||
|
||||
parseLinkText(settings: AltTextParsed): void {
|
||||
//#region spin
|
||||
if (settings["spin"] && settings["spin"].toLowerCase() == "true") {
|
||||
this.spin = true;
|
||||
} else if (settings["spin"] && settings["spin"].toLowerCase() == "false") {
|
||||
this.spin = false
|
||||
} else {
|
||||
this.spin = this.plugin.settings.animate_3d_objects;
|
||||
}
|
||||
//#endregion spin
|
||||
|
||||
//#region camLookAt
|
||||
let camLookAtValid = false;
|
||||
if (settings["camLookAt"]) {
|
||||
const [x, y, z] = settings["camLookAt"].split(',');
|
||||
if (Number(x) && Number(y) && Number(z)) {
|
||||
this.camLookAt = new THREE.Vector3(Number(x), Number(y), Number(z));
|
||||
camLookAtValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!camLookAtValid) {
|
||||
this.camLookAt = new THREE.Vector3(0, 0, 0);
|
||||
}
|
||||
//#endregion camLookAt
|
||||
|
||||
//#region camPos
|
||||
let camPosValid = false;
|
||||
if (settings["camPos"]) {
|
||||
const [x, y, z] = settings["camPos"].split(',');
|
||||
if (Number(x) && Number(y) && Number(z)) {
|
||||
this.camPos = new THREE.Vector3(Number(x), Number(y), Number(z));
|
||||
camPosValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!camPosValid) {
|
||||
this.camPos = new THREE.Vector3(1, 0.5, 1);
|
||||
}
|
||||
//#endregion camPos
|
||||
}
|
||||
|
||||
async loadFile(): Promise<void> {
|
||||
this.scene = new THREE.Scene();
|
||||
|
||||
// Subtract 1 to remove the scrollbar...
|
||||
let width = this.contentEl.innerWidth - 1;
|
||||
let width = this.width ?? this.contentEl.innerWidth - 1;
|
||||
width = width > 0 ? width : 1;
|
||||
const height = this.height ?? width;
|
||||
|
||||
this.renderer = new THREE.WebGLRenderer();
|
||||
this.renderer.setSize(width, width);
|
||||
this.renderer.setSize(width, height);
|
||||
this.renderer.setClearColor(0x000000, 0);
|
||||
|
||||
const camera = new THREE.PerspectiveCamera( 75, 1, 0.1, 1000 );
|
||||
camera.position.set(1, 0.5, 1);
|
||||
camera.lookAt(0, 0, 0);
|
||||
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000 );
|
||||
camera.position.set(this.camPos.x, this.camPos.y, this.camPos.z);
|
||||
camera.lookAt(this.camLookAt);
|
||||
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
|
||||
this.scene.add(ambientLight);
|
||||
|
|
@ -47,12 +93,12 @@ export abstract class ThreeJSComponent extends ExtensionComponent {
|
|||
|
||||
this.renderer.setAnimationLoop(() => {
|
||||
if (this.scene && this.renderer) {
|
||||
if (this.plugin.settings.animate_3d_objects) {
|
||||
if (this.spin) {
|
||||
angle += 0.01;
|
||||
camera.position.x = radius * Math.cos(angle);
|
||||
camera.position.z = radius * Math.sin(angle);
|
||||
|
||||
camera.lookAt(0, 0, 0);
|
||||
camera.lookAt(this.camLookAt);
|
||||
}
|
||||
|
||||
this.renderer.render(this.scene, camera);
|
||||
|
|
@ -60,20 +106,17 @@ export abstract class ThreeJSComponent extends ExtensionComponent {
|
|||
});
|
||||
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
let width = this.contentEl.innerWidth - 1;
|
||||
|
||||
if (this.linkText && !isNaN(Number(this.linkText))) {
|
||||
width = Number(this.linkText);
|
||||
if (!this.width) {
|
||||
const width = this.contentEl.innerWidth - 1;
|
||||
this.renderer?.setSize(width, width);
|
||||
}
|
||||
|
||||
this.renderer?.setSize(width, width);
|
||||
camera.updateProjectionMatrix();
|
||||
});
|
||||
|
||||
this.resizeObserver.observe(this.contentEl);
|
||||
}
|
||||
|
||||
onunload(): void {
|
||||
cleanup(): void {
|
||||
this.renderer?.setAnimationLoop(null);
|
||||
this.renderer?.dispose();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,65 @@
|
|||
import ExtendedFileSupport from "main";
|
||||
import { Component, TFile } from "obsidian";
|
||||
import { Component, EventRef, TFile } from "obsidian";
|
||||
|
||||
export type AltTextParsed = {[key: string]: string}
|
||||
|
||||
export abstract class ExtensionComponent extends Component {
|
||||
protected plugin: ExtendedFileSupport;
|
||||
protected contentEl: HTMLElement;
|
||||
protected file: TFile;
|
||||
protected linkText?: string;
|
||||
protected linkText: string | null;
|
||||
protected width?: number;
|
||||
protected height?: number;
|
||||
protected fileModify?: EventRef;
|
||||
|
||||
public constructor(contentEl: HTMLElement, plugin: ExtendedFileSupport, file: TFile, linkText?: string) {
|
||||
public constructor(contentEl: HTMLElement, plugin: ExtendedFileSupport, file: TFile, linkText: string | null) {
|
||||
super();
|
||||
this.contentEl = contentEl;
|
||||
this.plugin = plugin;
|
||||
this.file = file;
|
||||
this.linkText = linkText;
|
||||
|
||||
const parsed: AltTextParsed = {};
|
||||
|
||||
for (const val of linkText?.split(';') ?? []) {
|
||||
const [key, value] = val.split('=');
|
||||
if (key && value !== undefined) {
|
||||
parsed[key] = value;
|
||||
} else {
|
||||
if (Number(val)) {
|
||||
this.width = Number(val);
|
||||
} else {
|
||||
const [width, height] = val.split("x");
|
||||
if (Number(width) && Number(height)) {
|
||||
this.width = Number(width);
|
||||
this.height = Number(height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Number(contentEl.getAttr("width"))) {
|
||||
this.width = Number(contentEl.getAttr("width"));
|
||||
}
|
||||
if (Number(contentEl.getAttr("height"))) {
|
||||
this.height = Number(contentEl.getAttr("height"));
|
||||
}
|
||||
|
||||
this.parseLinkText(parsed);
|
||||
|
||||
this.fileModify = this.plugin.app.vault.on("modify", (f) => {
|
||||
if (f === this.file) {
|
||||
this.contentEl.addClass("extended-file-loading");
|
||||
this.contentEl.empty();
|
||||
this.contentEl.createEl("i", { text: `Reloading ${this.file?.name}...`});
|
||||
|
||||
this.loadFile();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
abstract parseLinkText(settings: AltTextParsed): void;
|
||||
|
||||
abstract loadFile(): void;
|
||||
|
||||
onload() {
|
||||
|
|
@ -22,5 +67,13 @@ export abstract class ExtensionComponent extends Component {
|
|||
this.contentEl.createEl("i", { text: `Loading ${this.file?.name}...`});
|
||||
}
|
||||
|
||||
abstract onunload(): void;
|
||||
onunload(): void {
|
||||
if (this.fileModify) {
|
||||
this.plugin.app.vault.offref(this.fileModify);
|
||||
}
|
||||
|
||||
this.cleanup();
|
||||
}
|
||||
|
||||
abstract cleanup(): void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export abstract class ExtensionView<T extends ExtensionComponent> extends FileVi
|
|||
|
||||
async onLoadFile(file: TFile) {
|
||||
const ComponentClass = this.getComponent();
|
||||
this.component = new ComponentClass(this.contentEl, this.plugin, file);
|
||||
this.component = new ComponentClass(this.contentEl, this.plugin, file, null);
|
||||
this.component.loadFile();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,20 @@
|
|||
import { loadPdfJs } from "obsidian";
|
||||
import { ExtensionComponent } from "src/extensionComponent";
|
||||
import { AltTextParsed, ExtensionComponent } from "src/extensionComponent";
|
||||
import { ExtensionView } from "src/extensionView";
|
||||
import type { PDFDocumentProxy } from "pdfjs-dist"
|
||||
|
||||
export const VIEW_TYPE_AI = "extended-file-support-ai";
|
||||
|
||||
export class AIComponent extends ExtensionComponent {
|
||||
scale: number;
|
||||
|
||||
parseLinkText(settings: AltTextParsed): void {
|
||||
if (settings["scale"] && Number(settings["scale"])) {
|
||||
this.scale = Number(settings["scale"]);
|
||||
} else {
|
||||
this.scale = this.plugin.settings.ai_render_scale;
|
||||
}
|
||||
}
|
||||
|
||||
async loadFile(): Promise<void> {
|
||||
const pdfJS = await loadPdfJs();
|
||||
|
|
@ -14,22 +23,36 @@ export class AIComponent extends ExtensionComponent {
|
|||
|
||||
const page = await doc.getPage(1);
|
||||
|
||||
console.log(this.plugin.settings.ai_render_scale);
|
||||
|
||||
const SCALE = this.plugin.settings.ai_render_scale;
|
||||
const SCALE = this.scale;
|
||||
const viewport = page.getViewport({scale: SCALE});
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = viewport.width;
|
||||
canvas.height = viewport.height;
|
||||
canvas.addClass("full-width");
|
||||
canvas.width = this.width ?? viewport.width;
|
||||
canvas.height = this.height ?? (this.width ? (viewport.height / viewport.width * this.width) : viewport.height);
|
||||
|
||||
const context = canvas.getContext('2d');
|
||||
|
||||
if (context) {
|
||||
await page.render({ canvasContext: context, viewport: viewport}).promise;
|
||||
|
||||
if (this.width) {
|
||||
const tempCanvas = document.createElement("canvas");
|
||||
tempCanvas.width = viewport.width;
|
||||
tempCanvas.height = viewport.height;
|
||||
const tempContext = tempCanvas.getContext("2d");
|
||||
|
||||
if (tempContext && context) {
|
||||
await page.render({ canvasContext: tempContext, viewport: viewport}).promise;
|
||||
|
||||
context.drawImage(tempCanvas, 0, 0, tempCanvas.width, tempCanvas.height, 0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
} else {
|
||||
if (context) {
|
||||
await page.render({ canvasContext: context, viewport: viewport}).promise;
|
||||
}
|
||||
|
||||
canvas.addClass("full-width");
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.contentEl.empty();
|
||||
this.contentEl.removeClass("extended-file-loading");
|
||||
this.contentEl.addClasses(["media-embed", "image-embed"]);
|
||||
|
|
@ -42,7 +65,7 @@ export class AIComponent extends ExtensionComponent {
|
|||
}
|
||||
}
|
||||
|
||||
onunload(): void {}
|
||||
cleanup(): void {}
|
||||
}
|
||||
|
||||
export class AIView extends ExtensionView<AIComponent> {
|
||||
|
|
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import ExtendedFileSupport from "main";
|
||||
import { TFile } from "obsidian";
|
||||
import { ThreeJSComponent, ThreeJSView } from "src/abstractions/threejsComponent";
|
||||
import { ExtensionComponent } from "src/extensionComponent";
|
||||
import { GLTFLoader } from "three/examples/jsm/Addons.js";
|
||||
|
||||
export const VIEW_TYPE_GLB = "extended-file-support-GLB";
|
||||
|
|
@ -20,7 +19,7 @@ export class GLBComponent extends ThreeJSComponent {
|
|||
}
|
||||
|
||||
export class GLBView extends ThreeJSView<GLBComponent> {
|
||||
getComponent(): new (contentEl: HTMLElement, plugin: ExtendedFileSupport, file: TFile, linkText?: string | undefined) => GLBComponent {
|
||||
getComponent(): new (...args: ConstructorParameters<typeof ExtensionComponent>) => GLBComponent {
|
||||
return GLBComponent;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { BlobReader, BlobWriter, ZipReader } from "@zip.js/zip.js";
|
||||
import { ExtensionComponent } from "src/extensionComponent";
|
||||
import { AltTextParsed, ExtensionComponent } from "src/extensionComponent";
|
||||
import { ExtensionView } from "src/extensionView";
|
||||
|
||||
export const VIEW_TYPE_KRA = "extended-file-support-kra";
|
||||
|
|
@ -7,6 +7,8 @@ export const VIEW_TYPE_KRA = "extended-file-support-kra";
|
|||
export class KRAComponent extends ExtensionComponent {
|
||||
private objectURL?: string;
|
||||
|
||||
parseLinkText(_: AltTextParsed): void { }
|
||||
|
||||
async loadFile(): Promise<void> {
|
||||
const MERGED_PATH = "mergedimage.png";
|
||||
|
||||
|
|
@ -29,6 +31,13 @@ export class KRAComponent extends ExtensionComponent {
|
|||
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"]);
|
||||
|
|
@ -39,7 +48,7 @@ export class KRAComponent extends ExtensionComponent {
|
|||
}
|
||||
}
|
||||
|
||||
onunload(): void {
|
||||
cleanup(): void {
|
||||
if (this.objectURL) {
|
||||
URL.revokeObjectURL(this.objectURL);
|
||||
this.objectURL = undefined;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import ExtendedFileSupport from "main";
|
||||
import { TFile } from "obsidian";
|
||||
import { ThreeJSComponent, ThreeJSView } from "src/abstractions/threejsComponent";
|
||||
import { ExtensionComponent } from "src/extensionComponent";
|
||||
import { OBJLoader } from 'three/examples/jsm/Addons.js';
|
||||
|
||||
export const VIEW_TYPE_OBJ = "extended-file-support-obj";
|
||||
|
|
@ -21,7 +20,7 @@ export class OBJComponent extends ThreeJSComponent {
|
|||
}
|
||||
|
||||
export class OBJView extends ThreeJSView<OBJComponent> {
|
||||
getComponent(): new (contentEl: HTMLElement, plugin: ExtendedFileSupport, file: TFile, linkText?: string | undefined) => OBJComponent {
|
||||
getComponent(): new (...args: ConstructorParameters<typeof ExtensionComponent>) => OBJComponent {
|
||||
return OBJComponent;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import { ExtensionComponent } from "src/extensionComponent";
|
||||
import { AltTextParsed, ExtensionComponent } from "src/extensionComponent";
|
||||
import { ExtensionView } from "src/extensionView";
|
||||
import Psd from "@webtoon/psd";
|
||||
|
||||
export const VIEW_TYPE_PSD = "extended-file-support-psd";
|
||||
|
||||
export class PSDComponent extends ExtensionComponent {
|
||||
parseLinkText(_: AltTextParsed): void { }
|
||||
|
||||
async loadFile(): Promise<void> {
|
||||
const resource = this.plugin.app.vault.getResourcePath(this.file);
|
||||
const res = await fetch(resource);
|
||||
|
|
@ -18,19 +20,33 @@ export class PSDComponent extends ExtensionComponent {
|
|||
|
||||
const imageData = new ImageData(compositeBuffer, psd.width, psd.height);
|
||||
|
||||
canvasEl.width = psd.width;
|
||||
canvasEl.height = psd.height;
|
||||
canvasEl.width = this.width ?? psd.width;
|
||||
canvasEl.height = this.height ?? (this.width ? (psd.height / psd.width * this.width) : psd.height);
|
||||
|
||||
canvasEl.addClass("full-width");
|
||||
if (this.width) {
|
||||
const tempCanvas = document.createElement("canvas");
|
||||
tempCanvas.width = imageData.width;
|
||||
tempCanvas.height = imageData.height;
|
||||
const tempContext = tempCanvas.getContext("2d");
|
||||
|
||||
if (tempContext) {
|
||||
tempContext.putImageData(imageData, 0, 0);
|
||||
|
||||
context?.drawImage(tempCanvas, 0, 0, tempCanvas.width, tempCanvas.height, 0, 0, canvasEl.width, canvasEl.height);
|
||||
}
|
||||
} else {
|
||||
canvasEl.addClass("full-width");
|
||||
context?.putImageData(imageData, 0, 0);
|
||||
}
|
||||
|
||||
context?.putImageData(imageData, 0, 0);
|
||||
|
||||
this.contentEl.empty();
|
||||
this.contentEl.removeClass("extended-file-loading");
|
||||
this.contentEl.addClasses(["media-embed", "image-embed"]);
|
||||
this.contentEl.append(canvasEl);
|
||||
}
|
||||
|
||||
onunload(): void {}
|
||||
cleanup(): void {}
|
||||
}
|
||||
|
||||
export class PSDView extends ExtensionView<PSDComponent> {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import ExtendedFileSupport from "main";
|
||||
import { TFile } from "obsidian";
|
||||
import { ThreeJSComponent, ThreeJSView } from "src/abstractions/threejsComponent";
|
||||
import { STLLoader } from "three/examples/jsm/Addons.js";
|
||||
import * as THREE from 'three';
|
||||
import { ExtensionComponent } from "src/extensionComponent";
|
||||
|
||||
export const VIEW_TYPE_STL = "extended-file-support-STL";
|
||||
|
||||
|
|
@ -30,7 +29,7 @@ export class STLComponent extends ThreeJSComponent {
|
|||
}
|
||||
|
||||
export class STLView extends ThreeJSView<STLComponent> {
|
||||
getComponent(): new (contentEl: HTMLElement, plugin: ExtendedFileSupport, file: TFile, linkText?: string | undefined) => STLComponent {
|
||||
getComponent(): new (...args: ConstructorParameters<typeof ExtensionComponent>) => STLComponent {
|
||||
return STLComponent;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
{
|
||||
"1.0.0": "0.18.0",
|
||||
"1.0.1": "0.18.0",
|
||||
"1.2.1": "0.18.0"
|
||||
"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.4.0": "0.18.0",
|
||||
"1.4.1": "0.18.0"
|
||||
}
|
||||
Loading…
Reference in a new issue