Compare commits

..

No commits in common. "main" and "1.0.0" have entirely different histories.
main ... 1.0.0

19 changed files with 176 additions and 2805 deletions

View File

@ -1,10 +0,0 @@
# top-most EditorConfig file
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
tab_width = 4

View File

@ -1,3 +0,0 @@
node_modules/
main.js

View File

@ -1,23 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"env": { "node": true },
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"sourceType": "module"
},
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
"@typescript-eslint/ban-ts-comment": "off",
"no-prototype-builtins": "off",
"@typescript-eslint/no-empty-function": "off"
}
}

View File

@ -1,84 +0,0 @@
name: Build obsidian plugin
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- "*" # Push events to matching any tag format, i.e. 1.0, 20.15.10
env:
PLUGIN_NAME: swarnam # Change this to the name of your plugin-id folder
jobs:
build:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: "20.x"
- name: Build
id: build
run: |
npm i
npm run build
mkdir ${{ env.PLUGIN_NAME }}
cp main.js manifest.json styles.css ${{ env.PLUGIN_NAME }}
zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }}
ls
echo "::set-output name=tag_name::$(git tag --sort version:refname | tail -n 1)"
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ github.ref }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
draft: false
prerelease: false
- name: Upload zip file
id: upload-zip
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./${{ env.PLUGIN_NAME }}.zip
asset_name: ${{ env.PLUGIN_NAME }}-${{ steps.build.outputs.tag_name }}.zip
asset_content_type: application/zip
- name: Upload main.js
id: upload-main
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./main.js
asset_name: main.js
asset_content_type: text/javascript
- name: Upload manifest.json
id: upload-manifest
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./manifest.json
asset_name: manifest.json
asset_content_type: application/json
- name: Upload styles.css
id: upload-css
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./styles.css
asset_name: styles.css
asset_content_type: text/css

17
.gitignore vendored
View File

@ -1,22 +1,11 @@
# vscode
.vscode
# Intellij # Intellij
*.iml *.iml
.idea .idea
# npm # npm
node_modules node_modules
package-lock.json
# Don't include the compiled main.js file in the repo. # build
# They should be uploaded to GitHub releases instead.
main.js main.js
*.js.map
# Exclude sourcemaps
*.map
# obsidian
data.json
# Exclude macOS Finder (System Explorer) View States
.DS_Store

1
.npmrc
View File

@ -1 +0,0 @@
tag-version-prefix=""

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 KiB

View File

@ -1,20 +0,0 @@
Copyright (c) 2024 Sangeeth Sudheer
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,76 +1,22 @@
<div align="center"> ## Obsidian Sample Plugin
<img src="./3dicons-co-icon.png" height="60px"> This is a sample plugin for Obsidian (https://obsidian.md).
# Swarnam This project uses Typescript to provide type checking and documentation.
The repo contains the latest plugin API (obsidian.d.ts) in Typescript Definition format, which contains TSDoc comments describing what it does.
A no-frills web playground plugin for Obsidian. Inspired by [MDN playgrounds](https://developer.mozilla.org/en-US/docs/Web/CSS/grid). **Note:** The Obsidian API is still in early alpha and is subject to change at any time!
<sup><i>Swarnam (സ്വർണം)</i> means <i>gold</i> in Malayalam.<br/></sup> ### How to use
<sup>(icon credit: [3dicons](https://3dicons.co))</sup>
</div> - Clone this repo.
- `npm i` or `yarn` to install dependencies
- `npm run dev` to start compilation in watch mode.
## Demo ### How to install the plugin
https://github.com/runofthemillgeek/swarnam-obsidian/assets/2759499/333424b3-2f82-4c1e-9e50-68c837227433 - Copy over `main.js`, `styles.css`, `manifest.json` to your vault `vault/.obsidian/plugins/plugin-id/`.
## Usage ### API Documentation
Install the extension from Obsidian community plugins list (you need to enable this first from See https://github.com/obsidianmd/obsidian-api
Settings), enable the plugin and then restart Obsidian.
To create a new Swarnam block, you need to open a block code snippet and give it the `swarnam` tag
like so:
```swarnam
<h1>Hello, world</h1>
```
When you preview this, it'll show the source and render the HTML side-by-side. You can hover on the
top-right and click the `</>` icon to edit the snippet to make some changes.
You can also add CSS and JS by separating them with `---*---` like so:
```swarnam
<h1 id="h1">Hello world</h1>
---*---
h1 {
font-family: "Manjari";
color: red;
animation: rainbow 5s ease infinite forwards;
}
@keyframes rainbow {
0% { filter: hue-rotate(0); }
100% { filter: hue-rotate(360deg); }
}
---*---
let i = 0;
setInterval(() => {
h1.textContent = `${["Hello", "Hola", "നമസ്കാരം"][i++ % 3]} Obsidian!`;
}, 1500)
```
This'll render the three snippets on the left side and show the preview of a web page that has all
these 3 blocks injected.
## How does it work
We split the code block snippet into 3 parts and form an HTML document string by injecting the CSS
and JS pieces into `<style>` and `<script>` tags. Once we have the final HTML document, we convert
this into Base64 and create a data URI. Finally, an `<iframe>` element is created and the data URI
is given as the `src`. Data URIs of mime type `text/html` and base64 enoding can be rendered by most
browsers including the Chromium renderer Obsidian is built on top of. There are no additional build
steps involved and thus, this is not a full-blown replacement for something like Sandpack.
## Contributing
Spot any issue? Have a feature request or idea? Feel free to create a new issue in GitHub to
discuss. (Pretty please do this before you spend your precious time on a PR that might not make into
this repo).

View File

@ -1,48 +0,0 @@
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
const banner =
`/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
`;
const prod = (process.argv[2] === "production");
const context = await esbuild.context({
banner: {
js: banner,
},
entryPoints: ["main.ts"],
bundle: true,
external: [
"obsidian",
"electron",
"@codemirror/autocomplete",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins],
format: "cjs",
target: "es2018",
logLevel: "info",
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: "main.js",
});
if (prod) {
await context.rebuild();
process.exit(0);
} else {
await context.watch();
}

200
main.ts
View File

@ -1,141 +1,77 @@
import { Plugin } from "obsidian"; import { App, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
const PREFIX = "swarnam"; export default class MyPlugin extends Plugin {
onInit() {
function base64ToBytes(base64: string) {
const binString = atob(base64);
// @ts-expect-error
return Uint8Array.from(binString, (m) => m.codePointAt(0));
}
function bytesToBase64(bytes: Uint8Array) {
const binString = Array.from(bytes, (byte) =>
String.fromCodePoint(byte)
).join("");
return btoa(binString);
}
function asBase64(text: string) {
const b64 = bytesToBase64(new TextEncoder().encode(text));
return b64;
}
function getIframeDoc(htmlSource: string, cssSource: string, jsSource: string) {
const isDarkMode = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
return `
<style>
body { font-family: sans-serif; color: ${isDarkMode ? "#fff" : "#000"} }
</style>
<style>
${cssSource}
</style>
<div class="${PREFIX}-html-container">
${htmlSource}
</div>
<script>
${jsSource}
</script>
`;
}
function showError(msg: string, root: HTMLElement) {
root.classList.add("error");
root.createEl("p", { cls: "icon", text: "⚠️" });
root.createEl("p", { text: msg });
}
export default class SwarnamPlugin extends Plugin {
async onload() {
this.registerMarkdownCodeBlockProcessor(
"swarnam",
(source, el, ctx) => {
const root = el.createDiv({ cls: `${PREFIX}-root` });
let [
htmlSource = "",
cssSource = "",
jsSource = "",
// eslint-disable-next-line prefer-const
...others
] = source.split(/^\s*---\*---\s*$/m);
if (others.length > 0) {
showError(
"Swarnam only supports HTML, CSS and JS blocks but your snippet has more than 3 blocks.",
root
);
return;
} }
htmlSource = htmlSource.trim(); onload() {
cssSource = cssSource.trim(); console.log('loading plugin');
jsSource = jsSource.trim();
if (!htmlSource) { this.addRibbonIcon('dice', 'Sample Plugin', () => {
showError( new Notice('This is a notice!');
"A Swarnam block must at least contain HTML", });
root
); this.addStatusBarItem().setText('Status Bar Text');
return;
this.addCommand({
id: 'open-sample-modal',
name: 'Open Sample Modal',
// callback: () => {
// console.log('Simple Callback');
// },
checkCallback: (checking: boolean) => {
let leaf = this.app.workspace.activeLeaf;
if (leaf) {
if (!checking) {
new SampleModal(this.app).open();
}
return true;
}
return false;
}
});
this.addSettingTab(new SampleSettingTab(this.app, this));
} }
const sourceRoot = root.createDiv({ onunload() {
cls: `${PREFIX}-source-root`, console.log('unloading plugin');
}); }
}
const htmlContainer = sourceRoot.createDiv({
cls: `${PREFIX}-source-container`, class SampleModal extends Modal {
}); constructor(app: App) {
const htmlEl = htmlContainer.createEl("pre", { super(app);
cls: `${PREFIX}-source ${PREFIX}-html-source`, }
});
htmlContainer.createDiv({ onOpen() {
text: "HTML", let {contentEl} = this;
cls: `${PREFIX}-badge ${PREFIX}-html-badge`, contentEl.setText('Woah!');
}); }
htmlEl.setText(htmlSource);
onClose() {
if (cssSource) { let {contentEl} = this;
const cssContainer = sourceRoot.createDiv({ contentEl.empty();
cls: `${PREFIX}-source-container`, }
}); }
const cssEl = cssContainer.createEl("pre", {
cls: `${PREFIX}-source ${PREFIX}-css-source`, class SampleSettingTab extends PluginSettingTab {
}); display(): void {
cssContainer.createDiv({ let {containerEl} = this;
text: "CSS",
cls: `${PREFIX}-badge ${PREFIX}-css-badge`, containerEl.empty();
});
cssEl.setText(cssSource); containerEl.createEl('h2', {text: 'Settings for my awesome plugin.'});
}
new Setting(containerEl)
if (jsSource) { .setName('Setting #1')
const jsContainer = sourceRoot.createDiv({ .setDesc('It\'s a secret')
cls: `${PREFIX}-source-container`, .addText(text => text.setPlaceholder('Enter your secret')
}); .setValue('')
.onChange((value) => {
const jsEl = jsContainer.createEl("pre", { console.log('Secret: ' + value);
cls: `${PREFIX}-source ${PREFIX}-js-source`, }));
});
jsContainer.createDiv({
text: "JS",
cls: `${PREFIX}-badge ${PREFIX}-js-badge`,
});
jsEl.setText(jsSource);
}
const iframeEl = root.createEl("iframe", {
cls: `${PREFIX}-preview`,
});
iframeEl.src = `data:text/html;base64;charset=UTF-8,${asBase64(
getIframeDoc(htmlSource, cssSource, jsSource)
)}`;
}
);
} }
} }

View File

@ -1,11 +1,8 @@
{ {
"id": "swarnam", "id": "obsidian-sample-plugin",
"name": "Swarnam", "name": "Sample Plugin",
"version": "1.0.1", "version": "1.0.0",
"minAppVersion": "1.5.12", "description": "This is a sample plugin for Obsidian (https://obsidian.md)",
"description": "A no-frills web playground plugin for Obsidian.", "author": "Licat",
"author": "runofthemillgeek", "isDesktopOnly": false
"authorUrl": "https://dg.sangeeth.dev",
"fundingUrl": "https://www.buymeacoffee.com/runofthemillgeek",
"isDesktopOnly": true
} }

2229
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +1,23 @@
{ {
"name": "obsidian-sample-plugin", "name": "obsidian-sample-plugin",
"version": "1.0.1", "version": "0.9.7",
"description": "This is a sample plugin for Obsidian (https://obsidian.md)", "description": "This is a sample plugin for Obsidian (https://obsidian.md)",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
"dev": "node esbuild.config.mjs", "dev": "rollup --config rollup.config.js -w",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", "build": "rollup --config rollup.config.js"
"version": "node version-bump.mjs && git add manifest.json versions.json"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@types/node": "^16.11.6", "@rollup/plugin-commonjs": "^15.1.0",
"@typescript-eslint/eslint-plugin": "5.29.0", "@rollup/plugin-node-resolve": "^9.0.0",
"@typescript-eslint/parser": "5.29.0", "@rollup/plugin-typescript": "^6.0.0",
"builtin-modules": "3.3.0", "@types/node": "^14.14.2",
"esbuild": "0.17.3", "obsidian": "https://github.com/obsidianmd/obsidian-api/tarball/master",
"obsidian": "latest", "rollup": "^2.32.1",
"tslib": "2.4.0", "tslib": "^2.0.3",
"typescript": "4.7.4" "typescript": "^4.0.3"
} }
} }

19
rollup.config.js Normal file
View File

@ -0,0 +1,19 @@
import typescript from '@rollup/plugin-typescript';
import {nodeResolve} from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
export default {
input: 'main.ts',
output: {
dir: '.',
sourcemap: 'inline',
format: 'cjs',
exports: 'default'
},
external: ['obsidian'],
plugins: [
typescript(),
nodeResolve({browser: true}),
commonjs(),
]
};

View File

@ -1,81 +1,4 @@
.swarnam-root { /* Sets all the text color to red! */
--swarnam-html-color: #e34c26; body {
--swarnam-css-color: #2965f1; color: red;
--swarnam-js-color: #f0db4f;
--swarnam-box-padding: var(--size-4-3) var(--size-4-4);
--swarnam-border-color: var(--divider-color);
--swarnam-border-radius: var(--code-radius);
--swarnam-source-font: var(--font-monospace);
--swarnam-source-font-size: var(--code-size);
display: flex;
align-items: stretch;
width: 100%;
}
.swarnam-root.error {
display: block;
padding: var(--swarnam-box-padding);
background-color: var(--code-background);
text-align: center;
.icon {
font-family: "Apple Emoji Color", sans-serif;
font-size: 1.5em;
}
}
.swarnam-source-root {
flex: 1 1 0;
overflow-x: scroll;
display: flex;
align-items: stretch;
flex-direction: column;
}
.swarnam-preview {
flex: 1 1 0;
border: 1px solid var(--swarnam-border-color);
border-left: 0;
border-top-right-radius: var(--swarnam-border-radius);
border-bottom-right-radius: var(--swarnam-border-radius);
padding: var(--swarnam-box-padding);
}
.swarnam-source-container {
position: relative;
flex: 1 1 0;
& + & {
border-top: 1px solid var(--swarnam-border-color);
border-top-left-radius: 0;
border-top-right-radius: 0;
}
}
.markdown-rendered .swarnam-source {
height: 100%;
margin: 0;
font-family: var(--swarnam-source-font);
font-size: var(--swarnam-source-font-size);
white-space: pre;
}
.swarnam-badge {
position: absolute;
top: calc(var(--size-4-3) / 1.5);
right: calc(var(--size-4-3) / 1.5);
font-size: 0.65em;
}
.swarnam-html-badge {
color: var(--swarnam-html-color);
}
.swarnam-css-badge {
color: var(--swarnam-css-color);
}
.swarnam-js-badge {
color: var(--swarnam-js-color);
} }

View File

@ -4,18 +4,16 @@
"inlineSourceMap": true, "inlineSourceMap": true,
"inlineSources": true, "inlineSources": true,
"module": "ESNext", "module": "ESNext",
"target": "ES6", "target": "es5",
"allowJs": true, "allowJs": true,
"noImplicitAny": true, "noImplicitAny": true,
"moduleResolution": "node", "moduleResolution": "node",
"importHelpers": true, "importHelpers": true,
"isolatedModules": true,
"strictNullChecks": true,
"lib": [ "lib": [
"DOM", "dom",
"ES5", "es5",
"ES6", "scripthost",
"ES7" "es2015"
] ]
}, },
"include": [ "include": [

View File

@ -1,14 +0,0 @@
import { readFileSync, writeFileSync } from "fs";
const targetVersion = process.env.npm_package_version;
// read minAppVersion from manifest.json and bump version to target version
let manifest = JSON.parse(readFileSync("manifest.json", "utf8"));
const { minAppVersion } = manifest;
manifest.version = targetVersion;
writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t"));
// update versions.json with target version and minAppVersion from manifest.json
let versions = JSON.parse(readFileSync("versions.json", "utf8"));
versions[targetVersion] = minAppVersion;
writeFileSync("versions.json", JSON.stringify(versions, null, "\t"));

View File

@ -1,4 +0,0 @@
{
"1.0.0": "0.15.0",
"1.0.1": "0.15.0"
}