1170 lines
42 KiB
JavaScript
Executable File
1170 lines
42 KiB
JavaScript
Executable File
/*
|
|
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
|
|
if you want to view the source visit the plugins github repository
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var obsidian = require('obsidian');
|
|
var require$$0 = require('electron');
|
|
var fs = require('fs');
|
|
var path = require('path');
|
|
|
|
function _interopNamespaceDefault(e) {
|
|
var n = Object.create(null);
|
|
if (e) {
|
|
Object.keys(e).forEach(function (k) {
|
|
if (k !== 'default') {
|
|
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
Object.defineProperty(n, k, d.get ? d : {
|
|
enumerable: true,
|
|
get: function () { return e[k]; }
|
|
});
|
|
}
|
|
});
|
|
}
|
|
n.default = e;
|
|
return Object.freeze(n);
|
|
}
|
|
|
|
var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
|
|
var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
|
|
|
|
const DEFAULT_SETTINGS = {
|
|
linkPrefix: "",
|
|
showFileEnding: false,
|
|
linkFolder: false,
|
|
useFolderName: false,
|
|
embedFile: false,
|
|
};
|
|
const SUPPORTED_EMBED_FILE_TYPES = [
|
|
".md",
|
|
".png",
|
|
".jpg",
|
|
".jpeg",
|
|
".gif",
|
|
".bmp",
|
|
".svg",
|
|
".mp3",
|
|
".webm",
|
|
".wav",
|
|
".m4a",
|
|
".ogg",
|
|
".3gp",
|
|
".flac",
|
|
".mp4",
|
|
".webm",
|
|
".ogv",
|
|
".pdf",
|
|
];
|
|
|
|
class FileLinkSettingTab extends obsidian.PluginSettingTab {
|
|
plugin;
|
|
constructor(app, plugin) {
|
|
super(app, plugin);
|
|
this.plugin = plugin;
|
|
}
|
|
display() {
|
|
let { containerEl } = this;
|
|
containerEl.empty();
|
|
containerEl.createEl("h2", { text: "Better File Link Settings" });
|
|
new obsidian.Setting(containerEl)
|
|
.setName("List style for multiple files")
|
|
.setDesc("Specify the characters shown before every file link.")
|
|
.addText((text) => text
|
|
.setPlaceholder("-")
|
|
.setValue(this.plugin.settings.linkPrefix)
|
|
.onChange(async (value) => {
|
|
this.plugin.settings.linkPrefix = value;
|
|
await this.plugin.saveSettings();
|
|
}));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Show file extension")
|
|
.setDesc("Will show file endings when activated.")
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.showFileEnding)
|
|
.onChange(async () => {
|
|
this.plugin.settings.showFileEnding = toggle.getValue();
|
|
await this.plugin.saveSettings();
|
|
}));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Embed file")
|
|
.setDesc("Will copy the file to Obsidian and embed it in the note.")
|
|
.addToggle((toggle) => toggle.setValue(this.plugin.settings.embedFile).onChange(async () => {
|
|
this.plugin.settings.embedFile = toggle.getValue();
|
|
await this.plugin.saveSettings();
|
|
}));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Link folder instead of file")
|
|
.setDesc("Link will open the folder where the file is located instead of opening the file itself.")
|
|
.addToggle((toggle) => toggle.setValue(this.plugin.settings.linkFolder).onChange(async () => {
|
|
this.plugin.settings.linkFolder = toggle.getValue();
|
|
await this.plugin.saveSettings();
|
|
}));
|
|
new obsidian.Setting(containerEl)
|
|
.setName("Use folder name")
|
|
.setDesc('When linking to a folder, show only the folder name instead of the full path (e.g. "baz" instead of "/Users/foo/bar/baz").')
|
|
.addToggle((toggle) => toggle
|
|
.setValue(this.plugin.settings.useFolderName)
|
|
.onChange(async () => {
|
|
this.plugin.settings.useFolderName = toggle.getValue();
|
|
await this.plugin.saveSettings();
|
|
}));
|
|
}
|
|
}
|
|
|
|
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
|
|
|
var renderer$1 = {};
|
|
|
|
var remote = {};
|
|
|
|
var callbacksRegistry = {};
|
|
|
|
var hasRequiredCallbacksRegistry;
|
|
|
|
function requireCallbacksRegistry () {
|
|
if (hasRequiredCallbacksRegistry) return callbacksRegistry;
|
|
hasRequiredCallbacksRegistry = 1;
|
|
Object.defineProperty(callbacksRegistry, "__esModule", { value: true });
|
|
callbacksRegistry.CallbacksRegistry = void 0;
|
|
class CallbacksRegistry {
|
|
constructor() {
|
|
this.nextId = 0;
|
|
this.callbacks = {};
|
|
this.callbackIds = new WeakMap();
|
|
this.locationInfo = new WeakMap();
|
|
}
|
|
add(callback) {
|
|
// The callback is already added.
|
|
let id = this.callbackIds.get(callback);
|
|
if (id != null)
|
|
return id;
|
|
id = this.nextId += 1;
|
|
this.callbacks[id] = callback;
|
|
this.callbackIds.set(callback, id);
|
|
// Capture the location of the function and put it in the ID string,
|
|
// so that release errors can be tracked down easily.
|
|
const regexp = /at (.*)/gi;
|
|
const stackString = (new Error()).stack;
|
|
if (!stackString)
|
|
return id;
|
|
let filenameAndLine;
|
|
let match;
|
|
while ((match = regexp.exec(stackString)) !== null) {
|
|
const location = match[1];
|
|
if (location.includes('(native)'))
|
|
continue;
|
|
if (location.includes('(<anonymous>)'))
|
|
continue;
|
|
if (location.includes('callbacks-registry.js'))
|
|
continue;
|
|
if (location.includes('remote.js'))
|
|
continue;
|
|
if (location.includes('@electron/remote/dist'))
|
|
continue;
|
|
const ref = /([^/^)]*)\)?$/gi.exec(location);
|
|
if (ref)
|
|
filenameAndLine = ref[1];
|
|
break;
|
|
}
|
|
this.locationInfo.set(callback, filenameAndLine);
|
|
return id;
|
|
}
|
|
get(id) {
|
|
return this.callbacks[id] || function () { };
|
|
}
|
|
getLocation(callback) {
|
|
return this.locationInfo.get(callback);
|
|
}
|
|
apply(id, ...args) {
|
|
return this.get(id).apply(commonjsGlobal, ...args);
|
|
}
|
|
remove(id) {
|
|
const callback = this.callbacks[id];
|
|
if (callback) {
|
|
this.callbackIds.delete(callback);
|
|
delete this.callbacks[id];
|
|
}
|
|
}
|
|
}
|
|
callbacksRegistry.CallbacksRegistry = CallbacksRegistry;
|
|
return callbacksRegistry;
|
|
}
|
|
|
|
var typeUtils = {};
|
|
|
|
var hasRequiredTypeUtils;
|
|
|
|
function requireTypeUtils () {
|
|
if (hasRequiredTypeUtils) return typeUtils;
|
|
hasRequiredTypeUtils = 1;
|
|
Object.defineProperty(typeUtils, "__esModule", { value: true });
|
|
typeUtils.deserialize = typeUtils.serialize = typeUtils.isSerializableObject = typeUtils.isPromise = void 0;
|
|
const electron_1 = require$$0;
|
|
function isPromise(val) {
|
|
return (val &&
|
|
val.then &&
|
|
val.then instanceof Function &&
|
|
val.constructor &&
|
|
val.constructor.reject &&
|
|
val.constructor.reject instanceof Function &&
|
|
val.constructor.resolve &&
|
|
val.constructor.resolve instanceof Function);
|
|
}
|
|
typeUtils.isPromise = isPromise;
|
|
const serializableTypes = [
|
|
Boolean,
|
|
Number,
|
|
String,
|
|
Date,
|
|
Error,
|
|
RegExp,
|
|
ArrayBuffer
|
|
];
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#Supported_types
|
|
function isSerializableObject(value) {
|
|
return value === null || ArrayBuffer.isView(value) || serializableTypes.some(type => value instanceof type);
|
|
}
|
|
typeUtils.isSerializableObject = isSerializableObject;
|
|
const objectMap = function (source, mapper) {
|
|
const sourceEntries = Object.entries(source);
|
|
const targetEntries = sourceEntries.map(([key, val]) => [key, mapper(val)]);
|
|
return Object.fromEntries(targetEntries);
|
|
};
|
|
function serializeNativeImage(image) {
|
|
const representations = [];
|
|
const scaleFactors = image.getScaleFactors();
|
|
// Use Buffer when there's only one representation for better perf.
|
|
// This avoids compressing to/from PNG where it's not necessary to
|
|
// ensure uniqueness of dataURLs (since there's only one).
|
|
if (scaleFactors.length === 1) {
|
|
const scaleFactor = scaleFactors[0];
|
|
const size = image.getSize(scaleFactor);
|
|
const buffer = image.toBitmap({ scaleFactor });
|
|
representations.push({ scaleFactor, size, buffer });
|
|
}
|
|
else {
|
|
// Construct from dataURLs to ensure that they are not lost in creation.
|
|
for (const scaleFactor of scaleFactors) {
|
|
const size = image.getSize(scaleFactor);
|
|
const dataURL = image.toDataURL({ scaleFactor });
|
|
representations.push({ scaleFactor, size, dataURL });
|
|
}
|
|
}
|
|
return { __ELECTRON_SERIALIZED_NativeImage__: true, representations };
|
|
}
|
|
function deserializeNativeImage(value) {
|
|
const image = electron_1.nativeImage.createEmpty();
|
|
// Use Buffer when there's only one representation for better perf.
|
|
// This avoids compressing to/from PNG where it's not necessary to
|
|
// ensure uniqueness of dataURLs (since there's only one).
|
|
if (value.representations.length === 1) {
|
|
const { buffer, size, scaleFactor } = value.representations[0];
|
|
const { width, height } = size;
|
|
image.addRepresentation({ buffer, scaleFactor, width, height });
|
|
}
|
|
else {
|
|
// Construct from dataURLs to ensure that they are not lost in creation.
|
|
for (const rep of value.representations) {
|
|
const { dataURL, size, scaleFactor } = rep;
|
|
const { width, height } = size;
|
|
image.addRepresentation({ dataURL, scaleFactor, width, height });
|
|
}
|
|
}
|
|
return image;
|
|
}
|
|
function serialize(value) {
|
|
if (value && value.constructor && value.constructor.name === 'NativeImage') {
|
|
return serializeNativeImage(value);
|
|
}
|
|
if (Array.isArray(value)) {
|
|
return value.map(serialize);
|
|
}
|
|
else if (isSerializableObject(value)) {
|
|
return value;
|
|
}
|
|
else if (value instanceof Object) {
|
|
return objectMap(value, serialize);
|
|
}
|
|
else {
|
|
return value;
|
|
}
|
|
}
|
|
typeUtils.serialize = serialize;
|
|
function deserialize(value) {
|
|
if (value && value.__ELECTRON_SERIALIZED_NativeImage__) {
|
|
return deserializeNativeImage(value);
|
|
}
|
|
else if (Array.isArray(value)) {
|
|
return value.map(deserialize);
|
|
}
|
|
else if (isSerializableObject(value)) {
|
|
return value;
|
|
}
|
|
else if (value instanceof Object) {
|
|
return objectMap(value, deserialize);
|
|
}
|
|
else {
|
|
return value;
|
|
}
|
|
}
|
|
typeUtils.deserialize = deserialize;
|
|
return typeUtils;
|
|
}
|
|
|
|
var moduleNames = {};
|
|
|
|
var getElectronBinding = {};
|
|
|
|
var hasRequiredGetElectronBinding;
|
|
|
|
function requireGetElectronBinding () {
|
|
if (hasRequiredGetElectronBinding) return getElectronBinding;
|
|
hasRequiredGetElectronBinding = 1;
|
|
Object.defineProperty(getElectronBinding, "__esModule", { value: true });
|
|
getElectronBinding.getElectronBinding = void 0;
|
|
const getElectronBinding$1 = (name) => {
|
|
if (process._linkedBinding) {
|
|
return process._linkedBinding('electron_common_' + name);
|
|
}
|
|
else if (process.electronBinding) {
|
|
return process.electronBinding(name);
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
};
|
|
getElectronBinding.getElectronBinding = getElectronBinding$1;
|
|
return getElectronBinding;
|
|
}
|
|
|
|
var hasRequiredModuleNames;
|
|
|
|
function requireModuleNames () {
|
|
if (hasRequiredModuleNames) return moduleNames;
|
|
hasRequiredModuleNames = 1;
|
|
(function (exports$1) {
|
|
var _a, _b;
|
|
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
exports$1.browserModuleNames = exports$1.commonModuleNames = void 0;
|
|
const get_electron_binding_1 = requireGetElectronBinding();
|
|
exports$1.commonModuleNames = [
|
|
'clipboard',
|
|
'nativeImage',
|
|
'shell',
|
|
];
|
|
exports$1.browserModuleNames = [
|
|
'app',
|
|
'autoUpdater',
|
|
'BaseWindow',
|
|
'BrowserView',
|
|
'BrowserWindow',
|
|
'contentTracing',
|
|
'crashReporter',
|
|
'dialog',
|
|
'globalShortcut',
|
|
'ipcMain',
|
|
'inAppPurchase',
|
|
'Menu',
|
|
'MenuItem',
|
|
'nativeTheme',
|
|
'net',
|
|
'netLog',
|
|
'MessageChannelMain',
|
|
'Notification',
|
|
'powerMonitor',
|
|
'powerSaveBlocker',
|
|
'protocol',
|
|
'pushNotifications',
|
|
'safeStorage',
|
|
'screen',
|
|
'session',
|
|
'ServiceWorkerMain',
|
|
'ShareMenu',
|
|
'systemPreferences',
|
|
'TopLevelWindow',
|
|
'TouchBar',
|
|
'Tray',
|
|
'utilityProcess',
|
|
'View',
|
|
'webContents',
|
|
'WebContentsView',
|
|
'webFrameMain',
|
|
].concat(exports$1.commonModuleNames);
|
|
const features = get_electron_binding_1.getElectronBinding('features');
|
|
if (((_a = features === null || features === void 0 ? void 0 : features.isDesktopCapturerEnabled) === null || _a === void 0 ? void 0 : _a.call(features)) !== false) {
|
|
exports$1.browserModuleNames.push('desktopCapturer');
|
|
}
|
|
if (((_b = features === null || features === void 0 ? void 0 : features.isViewApiEnabled) === null || _b === void 0 ? void 0 : _b.call(features)) !== false) {
|
|
exports$1.browserModuleNames.push('ImageView');
|
|
}
|
|
} (moduleNames));
|
|
return moduleNames;
|
|
}
|
|
|
|
var hasRequiredRemote;
|
|
|
|
function requireRemote () {
|
|
if (hasRequiredRemote) return remote;
|
|
hasRequiredRemote = 1;
|
|
(function (exports$1) {
|
|
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
exports$1.createFunctionWithReturnValue = exports$1.getGlobal = exports$1.getCurrentWebContents = exports$1.getCurrentWindow = exports$1.getBuiltin = void 0;
|
|
const callbacks_registry_1 = requireCallbacksRegistry();
|
|
const type_utils_1 = requireTypeUtils();
|
|
const electron_1 = require$$0;
|
|
const module_names_1 = requireModuleNames();
|
|
const get_electron_binding_1 = requireGetElectronBinding();
|
|
const { Promise } = commonjsGlobal;
|
|
const callbacksRegistry = new callbacks_registry_1.CallbacksRegistry();
|
|
const remoteObjectCache = new Map();
|
|
const finalizationRegistry = new FinalizationRegistry((id) => {
|
|
const ref = remoteObjectCache.get(id);
|
|
if (ref !== undefined && ref.deref() === undefined) {
|
|
remoteObjectCache.delete(id);
|
|
electron_1.ipcRenderer.send("REMOTE_BROWSER_DEREFERENCE" /* BROWSER_DEREFERENCE */, contextId, id, 0);
|
|
}
|
|
});
|
|
const electronIds = new WeakMap();
|
|
const isReturnValue = new WeakSet();
|
|
function getCachedRemoteObject(id) {
|
|
const ref = remoteObjectCache.get(id);
|
|
if (ref !== undefined) {
|
|
const deref = ref.deref();
|
|
if (deref !== undefined)
|
|
return deref;
|
|
}
|
|
}
|
|
function setCachedRemoteObject(id, value) {
|
|
const wr = new WeakRef(value);
|
|
remoteObjectCache.set(id, wr);
|
|
finalizationRegistry.register(value, id);
|
|
return value;
|
|
}
|
|
function getContextId() {
|
|
const v8Util = get_electron_binding_1.getElectronBinding('v8_util');
|
|
if (v8Util) {
|
|
return v8Util.getHiddenValue(commonjsGlobal, 'contextId');
|
|
}
|
|
else {
|
|
throw new Error('Electron >=v13.0.0-beta.6 required to support sandboxed renderers');
|
|
}
|
|
}
|
|
// An unique ID that can represent current context.
|
|
const contextId = process.contextId || getContextId();
|
|
// Notify the main process when current context is going to be released.
|
|
// Note that when the renderer process is destroyed, the message may not be
|
|
// sent, we also listen to the "render-view-deleted" event in the main process
|
|
// to guard that situation.
|
|
process.on('exit', () => {
|
|
const command = "REMOTE_BROWSER_CONTEXT_RELEASE" /* BROWSER_CONTEXT_RELEASE */;
|
|
electron_1.ipcRenderer.send(command, contextId);
|
|
});
|
|
const IS_REMOTE_PROXY = Symbol('is-remote-proxy');
|
|
// Convert the arguments object into an array of meta data.
|
|
function wrapArgs(args, visited = new Set()) {
|
|
const valueToMeta = (value) => {
|
|
// Check for circular reference.
|
|
if (visited.has(value)) {
|
|
return {
|
|
type: 'value',
|
|
value: null
|
|
};
|
|
}
|
|
if (value && value.constructor && value.constructor.name === 'NativeImage') {
|
|
return { type: 'nativeimage', value: type_utils_1.serialize(value) };
|
|
}
|
|
else if (Array.isArray(value)) {
|
|
visited.add(value);
|
|
const meta = {
|
|
type: 'array',
|
|
value: wrapArgs(value, visited)
|
|
};
|
|
visited.delete(value);
|
|
return meta;
|
|
}
|
|
else if (value instanceof Buffer) {
|
|
return {
|
|
type: 'buffer',
|
|
value
|
|
};
|
|
}
|
|
else if (type_utils_1.isSerializableObject(value)) {
|
|
return {
|
|
type: 'value',
|
|
value
|
|
};
|
|
}
|
|
else if (typeof value === 'object') {
|
|
if (type_utils_1.isPromise(value)) {
|
|
return {
|
|
type: 'promise',
|
|
then: valueToMeta(function (onFulfilled, onRejected) {
|
|
value.then(onFulfilled, onRejected);
|
|
})
|
|
};
|
|
}
|
|
else if (electronIds.has(value)) {
|
|
return {
|
|
type: 'remote-object',
|
|
id: electronIds.get(value)
|
|
};
|
|
}
|
|
const meta = {
|
|
type: 'object',
|
|
name: value.constructor ? value.constructor.name : '',
|
|
members: []
|
|
};
|
|
visited.add(value);
|
|
for (const prop in value) { // eslint-disable-line guard-for-in
|
|
meta.members.push({
|
|
name: prop,
|
|
value: valueToMeta(value[prop])
|
|
});
|
|
}
|
|
visited.delete(value);
|
|
return meta;
|
|
}
|
|
else if (typeof value === 'function' && isReturnValue.has(value)) {
|
|
return {
|
|
type: 'function-with-return-value',
|
|
value: valueToMeta(value())
|
|
};
|
|
}
|
|
else if (typeof value === 'function') {
|
|
return {
|
|
type: 'function',
|
|
id: callbacksRegistry.add(value),
|
|
location: callbacksRegistry.getLocation(value),
|
|
length: value.length
|
|
};
|
|
}
|
|
else {
|
|
return {
|
|
type: 'value',
|
|
value
|
|
};
|
|
}
|
|
};
|
|
return args.map(valueToMeta);
|
|
}
|
|
// Populate object's members from descriptors.
|
|
// The |ref| will be kept referenced by |members|.
|
|
// This matches |getObjectMemebers| in rpc-server.
|
|
function setObjectMembers(ref, object, metaId, members) {
|
|
if (!Array.isArray(members))
|
|
return;
|
|
for (const member of members) {
|
|
if (Object.prototype.hasOwnProperty.call(object, member.name))
|
|
continue;
|
|
const descriptor = { enumerable: member.enumerable };
|
|
if (member.type === 'method') {
|
|
const remoteMemberFunction = function (...args) {
|
|
let command;
|
|
if (this && this.constructor === remoteMemberFunction) {
|
|
command = "REMOTE_BROWSER_MEMBER_CONSTRUCTOR" /* BROWSER_MEMBER_CONSTRUCTOR */;
|
|
}
|
|
else {
|
|
command = "REMOTE_BROWSER_MEMBER_CALL" /* BROWSER_MEMBER_CALL */;
|
|
}
|
|
const ret = electron_1.ipcRenderer.sendSync(command, contextId, metaId, member.name, wrapArgs(args));
|
|
return metaToValue(ret);
|
|
};
|
|
let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name);
|
|
descriptor.get = () => {
|
|
descriptorFunction.ref = ref; // The member should reference its object.
|
|
return descriptorFunction;
|
|
};
|
|
// Enable monkey-patch the method
|
|
descriptor.set = (value) => {
|
|
descriptorFunction = value;
|
|
return value;
|
|
};
|
|
descriptor.configurable = true;
|
|
}
|
|
else if (member.type === 'get') {
|
|
descriptor.get = () => {
|
|
const command = "REMOTE_BROWSER_MEMBER_GET" /* BROWSER_MEMBER_GET */;
|
|
const meta = electron_1.ipcRenderer.sendSync(command, contextId, metaId, member.name);
|
|
return metaToValue(meta);
|
|
};
|
|
if (member.writable) {
|
|
descriptor.set = (value) => {
|
|
const args = wrapArgs([value]);
|
|
const command = "REMOTE_BROWSER_MEMBER_SET" /* BROWSER_MEMBER_SET */;
|
|
const meta = electron_1.ipcRenderer.sendSync(command, contextId, metaId, member.name, args);
|
|
if (meta != null)
|
|
metaToValue(meta);
|
|
return value;
|
|
};
|
|
}
|
|
}
|
|
Object.defineProperty(object, member.name, descriptor);
|
|
}
|
|
}
|
|
// Populate object's prototype from descriptor.
|
|
// This matches |getObjectPrototype| in rpc-server.
|
|
function setObjectPrototype(ref, object, metaId, descriptor) {
|
|
if (descriptor === null)
|
|
return;
|
|
const proto = {};
|
|
setObjectMembers(ref, proto, metaId, descriptor.members);
|
|
setObjectPrototype(ref, proto, metaId, descriptor.proto);
|
|
Object.setPrototypeOf(object, proto);
|
|
}
|
|
// Wrap function in Proxy for accessing remote properties
|
|
function proxyFunctionProperties(remoteMemberFunction, metaId, name) {
|
|
let loaded = false;
|
|
// Lazily load function properties
|
|
const loadRemoteProperties = () => {
|
|
if (loaded)
|
|
return;
|
|
loaded = true;
|
|
const command = "REMOTE_BROWSER_MEMBER_GET" /* BROWSER_MEMBER_GET */;
|
|
const meta = electron_1.ipcRenderer.sendSync(command, contextId, metaId, name);
|
|
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members);
|
|
};
|
|
return new Proxy(remoteMemberFunction, {
|
|
set: (target, property, value) => {
|
|
if (property !== 'ref')
|
|
loadRemoteProperties();
|
|
target[property] = value;
|
|
return true;
|
|
},
|
|
get: (target, property) => {
|
|
if (property === IS_REMOTE_PROXY)
|
|
return true;
|
|
if (!Object.prototype.hasOwnProperty.call(target, property))
|
|
loadRemoteProperties();
|
|
const value = target[property];
|
|
if (property === 'toString' && typeof value === 'function') {
|
|
return value.bind(target);
|
|
}
|
|
return value;
|
|
},
|
|
ownKeys: (target) => {
|
|
loadRemoteProperties();
|
|
return Object.getOwnPropertyNames(target);
|
|
},
|
|
getOwnPropertyDescriptor: (target, property) => {
|
|
const descriptor = Object.getOwnPropertyDescriptor(target, property);
|
|
if (descriptor)
|
|
return descriptor;
|
|
loadRemoteProperties();
|
|
return Object.getOwnPropertyDescriptor(target, property);
|
|
}
|
|
});
|
|
}
|
|
// Convert meta data from browser into real value.
|
|
function metaToValue(meta) {
|
|
if (!meta)
|
|
return {};
|
|
if (meta.type === 'value') {
|
|
return meta.value;
|
|
}
|
|
else if (meta.type === 'array') {
|
|
return meta.members.map((member) => metaToValue(member));
|
|
}
|
|
else if (meta.type === 'nativeimage') {
|
|
return type_utils_1.deserialize(meta.value);
|
|
}
|
|
else if (meta.type === 'buffer') {
|
|
return Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength);
|
|
}
|
|
else if (meta.type === 'promise') {
|
|
return Promise.resolve({ then: metaToValue(meta.then) });
|
|
}
|
|
else if (meta.type === 'error') {
|
|
return metaToError(meta);
|
|
}
|
|
else if (meta.type === 'exception') {
|
|
if (meta.value.type === 'error') {
|
|
throw metaToError(meta.value);
|
|
}
|
|
else {
|
|
throw new Error(`Unexpected value type in exception: ${meta.value.type}`);
|
|
}
|
|
}
|
|
else {
|
|
let ret;
|
|
if ('id' in meta) {
|
|
const cached = getCachedRemoteObject(meta.id);
|
|
if (cached !== undefined) {
|
|
return cached;
|
|
}
|
|
}
|
|
// A shadow class to represent the remote function object.
|
|
if (meta.type === 'function') {
|
|
const remoteFunction = function (...args) {
|
|
let command;
|
|
if (this && this.constructor === remoteFunction) {
|
|
command = "REMOTE_BROWSER_CONSTRUCTOR" /* BROWSER_CONSTRUCTOR */;
|
|
}
|
|
else {
|
|
command = "REMOTE_BROWSER_FUNCTION_CALL" /* BROWSER_FUNCTION_CALL */;
|
|
}
|
|
const obj = electron_1.ipcRenderer.sendSync(command, contextId, meta.id, wrapArgs(args));
|
|
return metaToValue(obj);
|
|
};
|
|
ret = remoteFunction;
|
|
}
|
|
else {
|
|
ret = {};
|
|
}
|
|
setObjectMembers(ret, ret, meta.id, meta.members);
|
|
setObjectPrototype(ret, ret, meta.id, meta.proto);
|
|
if (ret.constructor && ret.constructor[IS_REMOTE_PROXY]) {
|
|
Object.defineProperty(ret.constructor, 'name', { value: meta.name });
|
|
}
|
|
// Track delegate obj's lifetime & tell browser to clean up when object is GCed.
|
|
electronIds.set(ret, meta.id);
|
|
setCachedRemoteObject(meta.id, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
function metaToError(meta) {
|
|
const obj = meta.value;
|
|
for (const { name, value } of meta.members) {
|
|
obj[name] = metaToValue(value);
|
|
}
|
|
return obj;
|
|
}
|
|
function hasSenderId(input) {
|
|
return typeof input.senderId === "number";
|
|
}
|
|
function handleMessage(channel, handler) {
|
|
electron_1.ipcRenderer.on(channel, (event, passedContextId, id, ...args) => {
|
|
if (hasSenderId(event)) {
|
|
if (event.senderId !== 0 && event.senderId !== undefined) {
|
|
console.error(`Message ${channel} sent by unexpected WebContents (${event.senderId})`);
|
|
return;
|
|
}
|
|
}
|
|
if (passedContextId === contextId) {
|
|
handler(id, ...args);
|
|
}
|
|
else {
|
|
// Message sent to an un-exist context, notify the error to main process.
|
|
electron_1.ipcRenderer.send("REMOTE_BROWSER_WRONG_CONTEXT_ERROR" /* BROWSER_WRONG_CONTEXT_ERROR */, contextId, passedContextId, id);
|
|
}
|
|
});
|
|
}
|
|
const enableStacks = process.argv.includes('--enable-api-filtering-logging');
|
|
function getCurrentStack() {
|
|
const target = { stack: undefined };
|
|
if (enableStacks) {
|
|
Error.captureStackTrace(target, getCurrentStack);
|
|
}
|
|
return target.stack;
|
|
}
|
|
// Browser calls a callback in renderer.
|
|
handleMessage("REMOTE_RENDERER_CALLBACK" /* RENDERER_CALLBACK */, (id, args) => {
|
|
callbacksRegistry.apply(id, metaToValue(args));
|
|
});
|
|
// A callback in browser is released.
|
|
handleMessage("REMOTE_RENDERER_RELEASE_CALLBACK" /* RENDERER_RELEASE_CALLBACK */, (id) => {
|
|
callbacksRegistry.remove(id);
|
|
});
|
|
exports$1.require = (module) => {
|
|
const command = "REMOTE_BROWSER_REQUIRE" /* BROWSER_REQUIRE */;
|
|
const meta = electron_1.ipcRenderer.sendSync(command, contextId, module, getCurrentStack());
|
|
return metaToValue(meta);
|
|
};
|
|
// Alias to remote.require('electron').xxx.
|
|
function getBuiltin(module) {
|
|
const command = "REMOTE_BROWSER_GET_BUILTIN" /* BROWSER_GET_BUILTIN */;
|
|
const meta = electron_1.ipcRenderer.sendSync(command, contextId, module, getCurrentStack());
|
|
return metaToValue(meta);
|
|
}
|
|
exports$1.getBuiltin = getBuiltin;
|
|
function getCurrentWindow() {
|
|
const command = "REMOTE_BROWSER_GET_CURRENT_WINDOW" /* BROWSER_GET_CURRENT_WINDOW */;
|
|
const meta = electron_1.ipcRenderer.sendSync(command, contextId, getCurrentStack());
|
|
return metaToValue(meta);
|
|
}
|
|
exports$1.getCurrentWindow = getCurrentWindow;
|
|
// Get current WebContents object.
|
|
function getCurrentWebContents() {
|
|
const command = "REMOTE_BROWSER_GET_CURRENT_WEB_CONTENTS" /* BROWSER_GET_CURRENT_WEB_CONTENTS */;
|
|
const meta = electron_1.ipcRenderer.sendSync(command, contextId, getCurrentStack());
|
|
return metaToValue(meta);
|
|
}
|
|
exports$1.getCurrentWebContents = getCurrentWebContents;
|
|
// Get a global object in browser.
|
|
function getGlobal(name) {
|
|
const command = "REMOTE_BROWSER_GET_GLOBAL" /* BROWSER_GET_GLOBAL */;
|
|
const meta = electron_1.ipcRenderer.sendSync(command, contextId, name, getCurrentStack());
|
|
return metaToValue(meta);
|
|
}
|
|
exports$1.getGlobal = getGlobal;
|
|
// Get the process object in browser.
|
|
Object.defineProperty(exports$1, 'process', {
|
|
enumerable: true,
|
|
get: () => exports$1.getGlobal('process')
|
|
});
|
|
// Create a function that will return the specified value when called in browser.
|
|
function createFunctionWithReturnValue(returnValue) {
|
|
const func = () => returnValue;
|
|
isReturnValue.add(func);
|
|
return func;
|
|
}
|
|
exports$1.createFunctionWithReturnValue = createFunctionWithReturnValue;
|
|
const addBuiltinProperty = (name) => {
|
|
Object.defineProperty(exports$1, name, {
|
|
enumerable: true,
|
|
get: () => exports$1.getBuiltin(name)
|
|
});
|
|
};
|
|
module_names_1.browserModuleNames
|
|
.forEach(addBuiltinProperty);
|
|
} (remote));
|
|
return remote;
|
|
}
|
|
|
|
var hasRequiredRenderer$1;
|
|
|
|
function requireRenderer$1 () {
|
|
if (hasRequiredRenderer$1) return renderer$1;
|
|
hasRequiredRenderer$1 = 1;
|
|
(function (exports$1) {
|
|
var __createBinding = (renderer$1 && renderer$1.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __exportStar = (renderer$1 && renderer$1.__exportStar) || function(m, exports$1) {
|
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports$1, p)) __createBinding(exports$1, m, p);
|
|
};
|
|
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
if (process.type === 'browser')
|
|
throw new Error(`"@electron/remote" cannot be required in the browser process. Instead require("@electron/remote/main").`);
|
|
__exportStar(requireRemote(), exports$1);
|
|
} (renderer$1));
|
|
return renderer$1;
|
|
}
|
|
|
|
var renderer;
|
|
var hasRequiredRenderer;
|
|
|
|
function requireRenderer () {
|
|
if (hasRequiredRenderer) return renderer;
|
|
hasRequiredRenderer = 1;
|
|
renderer = requireRenderer$1();
|
|
return renderer;
|
|
}
|
|
|
|
var rendererExports = requireRenderer();
|
|
|
|
class FileEmbeder {
|
|
settings;
|
|
constructor(settings) {
|
|
this.settings = settings;
|
|
}
|
|
getEmbedMarkdownLink(filePath) {
|
|
const { ext } = this.getPathInformation(filePath);
|
|
if (!SUPPORTED_EMBED_FILE_TYPES.includes(ext)) {
|
|
new obsidian.Notice(`Files of this type are not supported for embedding in Obsidian.`);
|
|
}
|
|
return "!" + this.getMarkdownLink(filePath, false);
|
|
}
|
|
copyFile(sourcePath, targetDir) {
|
|
try {
|
|
fs__namespace.mkdirSync(targetDir, { recursive: true });
|
|
const filename = path__namespace.basename(sourcePath);
|
|
const destPath = path__namespace.join(targetDir, filename);
|
|
fs__namespace.copyFileSync(sourcePath, destPath);
|
|
return destPath;
|
|
}
|
|
catch (err) {
|
|
console.error("Copy failed:", err);
|
|
throw new Error(`File copy failed: ${err.message}`);
|
|
}
|
|
}
|
|
getPathInformation(filePath) {
|
|
const normalizedPath = path__namespace.normalize(filePath);
|
|
const parsedPath = path__namespace.parse(normalizedPath);
|
|
return {
|
|
fullPath: normalizedPath,
|
|
dir: parsedPath.dir,
|
|
filename: parsedPath.base,
|
|
name: parsedPath.name,
|
|
ext: parsedPath.ext,
|
|
};
|
|
}
|
|
getMarkdownLink(filePath, printPrefix) {
|
|
const pathInfo = this.getPathInformation(filePath);
|
|
const prefix = printPrefix ? this.settings.linkPrefix : "";
|
|
let linkText = pathInfo.name;
|
|
if (this.settings.linkFolder) {
|
|
linkText = this.settings.useFolderName
|
|
? path__namespace.basename(pathInfo.dir)
|
|
: pathInfo.dir;
|
|
}
|
|
if (this.settings.showFileEnding) {
|
|
linkText = pathInfo.filename;
|
|
}
|
|
const linkPath = this.settings.linkFolder
|
|
? pathInfo.dir
|
|
: pathInfo.fullPath;
|
|
return this.formatMarkdownLink(prefix, linkText, linkPath);
|
|
}
|
|
formatMarkdownLink(prefix, text, path) {
|
|
const space = prefix && prefix !== "!" ? " " : "";
|
|
return `${prefix}${space}[${text}](<file:///${path}>)\n`;
|
|
}
|
|
}
|
|
|
|
class FileLinkModal extends obsidian.Modal {
|
|
plugin;
|
|
selectedFilesDiv = document.createElement("div");
|
|
filePaths = [];
|
|
constructor(app, plugin) {
|
|
super(app);
|
|
this.plugin = plugin;
|
|
}
|
|
async onOpen() {
|
|
const { contentEl } = this;
|
|
const mainContainer = contentEl.createEl("div", {
|
|
cls: "bfl-container",
|
|
});
|
|
const fileSelectionSection = mainContainer.createEl("div", {
|
|
cls: "bfl-selection-section",
|
|
});
|
|
const fileButton = fileSelectionSection.createEl("button", {
|
|
text: "Select files",
|
|
cls: "mod-cta",
|
|
});
|
|
this.selectedFilesDiv = fileSelectionSection.createEl("div", {
|
|
cls: "bfl-selected-files",
|
|
});
|
|
this.displaySelectedFiles([]);
|
|
const checkboxContainer = mainContainer.createEl("div", {
|
|
cls: "bfl-checkbox-container",
|
|
});
|
|
const createCheckboxGroup = (id, label, initialValue) => {
|
|
const wrapper = checkboxContainer.createEl("div", {
|
|
cls: "bfl-checkbox-group",
|
|
});
|
|
const checkbox = wrapper.createEl("input", {
|
|
type: "checkbox",
|
|
attr: {
|
|
id: id,
|
|
style: "margin: 0;",
|
|
},
|
|
});
|
|
wrapper.createEl("label", {
|
|
text: label,
|
|
attr: {
|
|
for: id,
|
|
style: "margin: 0; user-select: none;",
|
|
},
|
|
});
|
|
checkbox.checked = initialValue;
|
|
return checkbox;
|
|
};
|
|
const checkboxEmbed = createCheckboxGroup("embed", "Embed file", this.plugin.settings.embedFile);
|
|
const checkboxFileFolder = createCheckboxGroup("file-folder", "Link folder", this.plugin.settings.linkFolder);
|
|
const checkboxFolderName = createCheckboxGroup("folder-name", "Use folder name", this.plugin.settings.useFolderName);
|
|
const checkboxFileEnding = createCheckboxGroup("file-ending", "Show file extension", this.plugin.settings.showFileEnding);
|
|
const buttonContainer = mainContainer.createEl("div", {
|
|
cls: "button-container",
|
|
attr: {
|
|
style: "margin-top: 5px;",
|
|
},
|
|
});
|
|
const submitButton = buttonContainer.createEl("button", {
|
|
text: "Add file link",
|
|
cls: "mod-cta",
|
|
});
|
|
contentEl.addEventListener("keydown", (e) => {
|
|
if (e.key === "Enter") {
|
|
e.preventDefault();
|
|
submitButton.click();
|
|
}
|
|
});
|
|
fileButton.addEventListener("click", async () => {
|
|
try {
|
|
const result = await rendererExports.dialog.showOpenDialog({
|
|
properties: ["openFile", "multiSelections"],
|
|
filters: [{ name: "All Files", extensions: ["*"] }],
|
|
});
|
|
if (!result.canceled && result.filePaths.length > 0) {
|
|
this.filePaths = result.filePaths;
|
|
this.displaySelectedFiles(this.filePaths);
|
|
}
|
|
}
|
|
catch (error) {
|
|
new obsidian.Notice("Error selecting files: " + error.message);
|
|
}
|
|
});
|
|
submitButton.addEventListener("click", () => {
|
|
if (this.filePaths.length > 0) {
|
|
// Update settings
|
|
this.plugin.settings.linkFolder = checkboxFileFolder.checked;
|
|
this.plugin.settings.useFolderName = checkboxFolderName.checked;
|
|
this.plugin.settings.showFileEnding = checkboxFileEnding.checked;
|
|
this.plugin.settings.embedFile = checkboxEmbed.checked;
|
|
const fe = new FileEmbeder(this.plugin.settings);
|
|
if (checkboxEmbed.checked) {
|
|
this.filePaths.forEach((file) => {
|
|
const embedMarkdownLink = fe.getEmbedMarkdownLink(file);
|
|
this.addAtCursor(embedMarkdownLink);
|
|
});
|
|
}
|
|
else {
|
|
let linkString = "";
|
|
this.filePaths.forEach((file) => {
|
|
linkString += fe.getMarkdownLink(file, this.filePaths.length > 1);
|
|
});
|
|
this.addAtCursor(linkString);
|
|
}
|
|
this.close();
|
|
new obsidian.Notice(`Added ${this.filePaths.length} file link(s)`);
|
|
}
|
|
else {
|
|
new obsidian.Notice("No files selected");
|
|
}
|
|
});
|
|
}
|
|
displaySelectedFiles(files) {
|
|
this.selectedFilesDiv.empty();
|
|
if (files.length === 0) {
|
|
this.selectedFilesDiv.createEl("p", {
|
|
text: "No files selected",
|
|
cls: "no-files-message",
|
|
attr: {
|
|
style: "color: var(--text-muted); font-style: italic; margin: 0; padding: 8px;",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
const fileList = this.selectedFilesDiv.createEl("ul", {
|
|
cls: "selected-files-list",
|
|
attr: {
|
|
style: "list-style: none; padding: 0; margin: 0;",
|
|
},
|
|
});
|
|
files.forEach((filePath) => {
|
|
const fileName = filePath.split(/[\\/]/).pop() || filePath;
|
|
fileList.createEl("li", {
|
|
text: fileName,
|
|
cls: "selected-file-item",
|
|
attr: {
|
|
style: "padding: 4px 8px; border-bottom: 1px solid var(--background-modifier-border);",
|
|
},
|
|
});
|
|
});
|
|
}
|
|
addAtCursor(s) {
|
|
const markdownView = this.app.workspace.getActiveViewOfType(obsidian.MarkdownView);
|
|
if (markdownView) {
|
|
const editor = markdownView.editor;
|
|
const currentLine = editor.getCursor();
|
|
editor.replaceRange(s, currentLine, currentLine);
|
|
}
|
|
}
|
|
onClose() {
|
|
const { contentEl } = this;
|
|
contentEl.empty();
|
|
}
|
|
}
|
|
|
|
const FILE_URI_REGEX = /file:\/\/[^\s)>"]+/g;
|
|
async function findBrokenFileLinks(vault) {
|
|
const results = new Map();
|
|
const files = vault.getMarkdownFiles();
|
|
for (const file of files) {
|
|
const content = await vault.read(file);
|
|
const broken = [];
|
|
let match;
|
|
FILE_URI_REGEX.lastIndex = 0;
|
|
while ((match = FILE_URI_REGEX.exec(content)) !== null) {
|
|
const uri = match[0];
|
|
const filePath = decodeURIComponent(new URL(uri).pathname);
|
|
if (!fs__namespace.existsSync(filePath)) {
|
|
broken.push(uri);
|
|
}
|
|
}
|
|
if (broken.length > 0) {
|
|
results.set(file, broken);
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
class BrokenLinksModal extends obsidian.Modal {
|
|
brokenLinks;
|
|
constructor(app, brokenLinks) {
|
|
super(app);
|
|
this.brokenLinks = brokenLinks;
|
|
}
|
|
onOpen() {
|
|
const { contentEl } = this;
|
|
contentEl.createEl("h2", { text: "Broken File Links" });
|
|
for (const [file, links] of this.brokenLinks) {
|
|
const header = contentEl.createEl("h3");
|
|
const noteLink = header.createEl("a", { text: file.basename });
|
|
noteLink.addEventListener("click", () => {
|
|
this.app.workspace.getLeaf().openFile(file);
|
|
this.close();
|
|
});
|
|
const list = contentEl.createEl("ul");
|
|
for (const link of links) {
|
|
const item = list.createEl("li");
|
|
item.createEl("span", {
|
|
text: link,
|
|
attr: { style: "word-break: break-all;" },
|
|
});
|
|
}
|
|
}
|
|
}
|
|
onClose() {
|
|
this.contentEl.empty();
|
|
}
|
|
}
|
|
|
|
class FileLink extends obsidian.Plugin {
|
|
settings = DEFAULT_SETTINGS;
|
|
async onload() {
|
|
console.log("loading plugin file-link");
|
|
await this.loadSettings();
|
|
this.addSettingTab(new FileLinkSettingTab(this.app, this));
|
|
this.addCommand({
|
|
id: "add-file-link",
|
|
name: "Add File Link",
|
|
editorCallback: () => {
|
|
new FileLinkModal(this.app, this).open();
|
|
},
|
|
});
|
|
this.addCommand({
|
|
id: "check-broken-file-links",
|
|
name: "Check for broken file links",
|
|
callback: async () => {
|
|
new obsidian.Notice("Scanning for broken file links...");
|
|
const brokenLinks = await findBrokenFileLinks(this.app.vault);
|
|
if (brokenLinks.size === 0) {
|
|
new obsidian.Notice("No broken file links found.");
|
|
return;
|
|
}
|
|
new BrokenLinksModal(this.app, brokenLinks).open();
|
|
},
|
|
});
|
|
}
|
|
onunload() {
|
|
console.log("unloading plugin file-link");
|
|
}
|
|
async loadSettings() {
|
|
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
|
}
|
|
async saveSettings() {
|
|
await this.saveData(this.settings);
|
|
}
|
|
}
|
|
|
|
module.exports = FileLink;
|
|
|
|
|
|
/* nosourcemap */ |