added docs, testing observers

This commit is contained in:
Kacper Kula 2024-05-01 22:54:41 +01:00
parent c92fc85cc3
commit 0e57c7bda2
22 changed files with 8200 additions and 5130 deletions

3
.gitignore vendored
View file

@ -20,3 +20,6 @@ data.json
# Exclude macOS Finder (System Explorer) View States
.DS_Store
docs/.vitepress/dist
docs/.vitepress/cache
rebuild

View file

@ -0,0 +1,31 @@
import { defineConfig } from 'vitepress'
// https://vitepress.dev/reference/site-config
export default defineConfig({
title: "Obsidian SQLSeal",
description: "Plugin enabling full SQL capabilities in Obsidian",
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
nav: [
{ text: 'Home', link: '/' },
{ text: 'Quick Start', link: '/quick-start' }
],
sidebar: [
{
text: 'Documentation',
items: [
{ text: 'Getting Started', link: '/getting-started' },
{ text: 'Quick Start', link: '/quick-start' },
{ text: 'Using properties', link: '/using-properties' },
{ text: 'Future Plans', link: '/future-plans' }
]
}
],
socialLinks: [
{ icon: 'github', link: 'https://github.com/vuejs/vitepress' }
]
}
})

View file

@ -0,0 +1,17 @@
// https://vitepress.dev/guide/custom-theme
import { h } from 'vue'
import type { Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import './style.css'
export default {
extends: DefaultTheme,
Layout: () => {
return h(DefaultTheme.Layout, null, {
// https://vitepress.dev/guide/extending-default-theme#layout-slots
})
},
enhanceApp({ app, router, siteData }) {
// ...
}
} satisfies Theme

View file

@ -0,0 +1,139 @@
/**
* Customize default theme styling by overriding CSS variables:
* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css
*/
/**
* Colors
*
* Each colors have exact same color scale system with 3 levels of solid
* colors with different brightness, and 1 soft color.
*
* - `XXX-1`: The most solid color used mainly for colored text. It must
* satisfy the contrast ratio against when used on top of `XXX-soft`.
*
* - `XXX-2`: The color used mainly for hover state of the button.
*
* - `XXX-3`: The color for solid background, such as bg color of the button.
* It must satisfy the contrast ratio with pure white (#ffffff) text on
* top of it.
*
* - `XXX-soft`: The color used for subtle background such as custom container
* or badges. It must satisfy the contrast ratio when putting `XXX-1` colors
* on top of it.
*
* The soft color must be semi transparent alpha channel. This is crucial
* because it allows adding multiple "soft" colors on top of each other
* to create a accent, such as when having inline code block inside
* custom containers.
*
* - `default`: The color used purely for subtle indication without any
* special meanings attched to it such as bg color for menu hover state.
*
* - `brand`: Used for primary brand colors, such as link text, button with
* brand theme, etc.
*
* - `tip`: Used to indicate useful information. The default theme uses the
* brand color for this by default.
*
* - `warning`: Used to indicate warning to the users. Used in custom
* container, badges, etc.
*
* - `danger`: Used to show error, or dangerous message to the users. Used
* in custom container, badges, etc.
* -------------------------------------------------------------------------- */
:root {
--vp-c-default-1: var(--vp-c-gray-1);
--vp-c-default-2: var(--vp-c-gray-2);
--vp-c-default-3: var(--vp-c-gray-3);
--vp-c-default-soft: var(--vp-c-gray-soft);
--vp-c-brand-1: var(--vp-c-indigo-1);
--vp-c-brand-2: var(--vp-c-indigo-2);
--vp-c-brand-3: var(--vp-c-indigo-3);
--vp-c-brand-soft: var(--vp-c-indigo-soft);
--vp-c-tip-1: var(--vp-c-brand-1);
--vp-c-tip-2: var(--vp-c-brand-2);
--vp-c-tip-3: var(--vp-c-brand-3);
--vp-c-tip-soft: var(--vp-c-brand-soft);
--vp-c-warning-1: var(--vp-c-yellow-1);
--vp-c-warning-2: var(--vp-c-yellow-2);
--vp-c-warning-3: var(--vp-c-yellow-3);
--vp-c-warning-soft: var(--vp-c-yellow-soft);
--vp-c-danger-1: var(--vp-c-red-1);
--vp-c-danger-2: var(--vp-c-red-2);
--vp-c-danger-3: var(--vp-c-red-3);
--vp-c-danger-soft: var(--vp-c-red-soft);
}
/**
* Component: Button
* -------------------------------------------------------------------------- */
:root {
--vp-button-brand-border: transparent;
--vp-button-brand-text: var(--vp-c-white);
--vp-button-brand-bg: var(--vp-c-brand-3);
--vp-button-brand-hover-border: transparent;
--vp-button-brand-hover-text: var(--vp-c-white);
--vp-button-brand-hover-bg: var(--vp-c-brand-2);
--vp-button-brand-active-border: transparent;
--vp-button-brand-active-text: var(--vp-c-white);
--vp-button-brand-active-bg: var(--vp-c-brand-1);
}
/**
* Component: Home
* -------------------------------------------------------------------------- */
:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(
120deg,
#bd34fe 30%,
#41d1ff
);
--vp-home-hero-image-background-image: linear-gradient(
-45deg,
#bd34fe 50%,
#47caff 50%
);
--vp-home-hero-image-filter: blur(44px);
}
@media (min-width: 640px) {
:root {
--vp-home-hero-image-filter: blur(56px);
}
}
@media (min-width: 960px) {
:root {
--vp-home-hero-image-filter: blur(68px);
}
}
/**
* Component: Custom Block
* -------------------------------------------------------------------------- */
:root {
--vp-custom-block-tip-border: transparent;
--vp-custom-block-tip-text: var(--vp-c-text-1);
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
}
/**
* Component: Algolia
* -------------------------------------------------------------------------- */
.DocSearch {
--docsearch-primary-color: var(--vp-c-brand-1) !important;
}

5
docs/future-plans.md Normal file
View file

@ -0,0 +1,5 @@
# Future Plans
In the short future the following features are planned for SQLSeal:
- Global tables: currently the tables are scoped to specific file to prevent name clashing. We plan to add settings panel to allow for setting up global tables
- Views: Allow to store your selects as views and query them.
- Dataview: Support for dataview data to be imported into SQLSeal.

55
docs/getting-started.md Normal file
View file

@ -0,0 +1,55 @@
# Getting Started
## Custom Containers
**Input**
```md
::: info
This is an info box.
:::
::: tip
This is a tip.
:::
::: warning
This is a warning.
:::
::: danger
This is a dangerous warning.
:::
::: details
This is a details block.
:::
```
**Output**
::: info
This is an info box.
:::
::: tip
This is a tip.
:::
::: warning
This is a warning.
:::
::: danger
This is a dangerous warning.
:::
::: details
This is a details block.
:::
## More
Check out the documentation for the [full list of markdown extensions](https://vitepress.dev/guide/markdown).

28
docs/index.md Normal file
View file

@ -0,0 +1,28 @@
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home
hero:
name: "Obsidian SQLSeal"
# text: "Plugin enabling full SQL capabilities in Obsidian"
tagline: "Plugin enabling full SQL capabilities in Obsidian"
image:
src: ./seal.png
alt: SQL Seal Logo (In Progress)
actions:
- theme: brand
text: Markdown Examples
link: /getting-started
- theme: alt
text: API Examples
link: /api-examples
features:
- title: Query data in your vault
details: Use full power of SQL to select, join, filter data for your liking
- title: Fully featured SQL Engine
details: With SQLite under the hood, you can use all functionality of the database
- title: Zero configuration
details: Just use SQL syntax to create and query the data
---

34
docs/quick-start.md Normal file
View file

@ -0,0 +1,34 @@
# Quick Start
In this guide you will learn:
- How to install SQLSeal in your vault
- How to save CSV and show it in the file explorer
- How to create your first query
## Installing SQLSeal
The easiest way to install SQLSeal is to download it from Community Plugins. Go to Settings -> Community Plugins and click on Browse button. Search for SQLSeal, install it and enable.
### Manual instalation
To install it manually clone the project:
```bash
git clone <URL>
```
## Save CSV in Obsidian
Obsidian does not natively support CSV and it hides them in the file explorer. To enable CSVs being shown on the sidebar, you need to enable "Detect all file extensions" in "Files and Links" setting panel. Then save the following CSV in your vault: [transactions.csv](./transactions.csv)
## Querying the data
To query the data simply create the codeblock in `sqlseal` language. SQLSeal language consists of the following:
- 0 or more `TABLE` command that define your tables
- Standard SQL SELECT query
The best is to use the following example:
```sqlseal
TABLE transactions = file(transactions.csv)
SELECT name, value FROM transactions
```
The code above creates table `transactions` based on the csv file provided and selects all names and values from it.

BIN
docs/seal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

5
docs/transactions.csv Normal file
View file

@ -0,0 +1,5 @@
id,name,customer,value
1,Bread,Mark,3.45
2,Milk,Mark,2.43
3,Press,Linda,4.55,
4,Cheese,Mark,3.22
1 id,name,customer,value
2 1,Bread,Mark,3.45
3 2,Milk,Mark,2.43
4 3,Press,Linda,4.55,
5 4,Cheese,Mark,3.22

7
docs/using-properties.md Normal file
View file

@ -0,0 +1,7 @@
# Using properties
You can use properties to customise your queries and make them dynamic. This can be helpful if you plan to reuse your queries accross multiple files or use them in the templates.
You can refer to your properties in all 3 ways SQLite allows for it:
- `@` symbol, for example `@year`
- `?` symbol, for example `?date`
- `:` symbol, for example `:category`

26
main.ts
View file

@ -1,4 +1,5 @@
import { MarkdownRenderer, MarkdownView, Plugin } from 'obsidian';
import { SealFileSync } from 'src/SealFileSync';
import { SqlSealSettings, SqlSealSettingsTab } from 'src/settings';
import { SqlSeal } from 'src/sqlSeal';
@ -9,25 +10,18 @@ export default class SqlSealPlugin extends Plugin {
async onload() {
await this.loadSettings();
const sqlSeal = new SqlSeal(this.app, true)
const sqlSeal = new SqlSeal(this.app, false)
this.registerMarkdownCodeBlockProcessor("sqlseal", sqlSeal.getHandler())
// or onload
this.app.workspace.iterateAllLeaves((leaf) => {
if (leaf.view && leaf.view instanceof MarkdownView) {
const editorView = (leaf.view.editor as any).cm;
console.log('EDITOR VIEW', editorView)
// ...
}
})
// })
// MarkdownRenderer.registerLanguage({
// id: "sqlseal",
// codeMirrorMode: "sql",
// name: "sqlseal",
// });
// const fileSync = new SealFileSync(this.app, sqlSeal)
// setTimeout(() => {
// fileSync.init()
// }, 5000)
// this.addSettingTab(new SqlSealSettingsTab(this.app, this));
this.addSettingTab(new SqlSealSettingsTab(this.app, this));
}

View file

@ -1,11 +1,11 @@
{
"id": "hypersphere-sqlseal",
"name": "SQL Seal",
"version": "1.0.0",
"version": "0.1.0",
"minAppVersion": "0.15.0",
"description": "Demonstrates some of the capabilities of the Obsidian API.",
"author": "Obsidian",
"authorUrl": "https://obsidian.md",
"fundingUrl": "https://obsidian.md/pricing",
"description": "Use SQL in your notes!",
"author": "hypersphere",
"authorUrl": "https://hypersphere.blog/",
"fundingUrl": "https://hypersphere.blog",
"isDesktopOnly": false
}

11357
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,39 +1,44 @@
{
"name": "obsidian-sample-plugin",
"version": "1.0.0",
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json",
"rebuild": "electron-rebuild -f -w better-sqlite3"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"@types/better-sqlite3": "^7.6.10",
"@types/node": "^16.11.6",
"@types/papaparse": "^5.3.14",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"electron": "^25.0.0",
"electron-rebuild": "^3.2.9",
"esbuild": "0.17.3",
"esbuild-plugin-replace": "^1.4.0",
"esbuild-plugin-replace-regex": "^0.0.2",
"obsidian": "latest",
"prettier": "3.2.5",
"tslib": "2.4.0",
"typescript": "4.7.4"
},
"dependencies": {
"better-sqlite3": "^9.5.0",
"gray-matter": "^4.0.3",
"node-sql-parser": "^5.0.0",
"papaparse": "^5.4.1",
"sqlite3": "^5.1.7"
}
"name": "obsidian-sample-plugin",
"version": "1.0.0",
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json",
"rebuild": "electron-rebuild -f -w better-sqlite3",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"@types/better-sqlite3": "^7.6.10",
"@types/node": "^16.11.6",
"@types/papaparse": "^5.3.14",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"electron": "^25.0.0",
"electron-rebuild": "^3.2.9",
"esbuild": "0.17.3",
"esbuild-plugin-replace": "^1.4.0",
"esbuild-plugin-replace-regex": "^0.0.2",
"obsidian": "latest",
"prettier": "3.2.5",
"tslib": "2.4.0",
"typescript": "4.7.4",
"vitepress": "^1.1.4",
"vue": "^3.4.26"
},
"dependencies": {
"better-sqlite3": "^9.5.0",
"gray-matter": "^4.0.3",
"node-sql-parser": "^5.0.0",
"papaparse": "^5.4.1",
"sqlite3": "^5.1.7"
}
}

56
src/SealFileSync.ts Normal file
View file

@ -0,0 +1,56 @@
import { App, TAbstractFile } from "obsidian";
import GrayMatter from "gray-matter";
import { SqlSeal } from "./sqlSeal";
async function extractFrontmatterFromFile(file: TAbstractFile) {
const content = await this.app.vault.read(file);
const gm = GrayMatter(content)
return gm.data
}
function fileData(file: TAbstractFile, frontmatter: Record<string, any>) {
return {
...frontmatter,
path: file.path,
name: file.name.replace(/\.[^/.]+$/, ""),
id: file.path
}
}
export class SealFileSync {
private currentSchema: Record<string, 'TEXT' | 'INTEGER' | 'REAL'> = {}
constructor(public readonly app: App, private readonly sqlSeal: SqlSeal) {
this.app.vault.on('modify', async (file) => {
console.log('Modified', file)
const frontmatter = await extractFrontmatterFromFile(file)
if (this.isFrontmatterSame([frontmatter])) {
// we need to update the row
await this.sqlSeal.db.updateData('files', [fileData(file, frontmatter)])
return
}
await this.init()
})
}
async init() {
const files = this.app.vault.getMarkdownFiles();
const data = []
for (const file of files) {
const frontmatter = await extractFrontmatterFromFile(file)
data.push(fileData(file, frontmatter))
}
const schema = await this.sqlSeal.db.createTableWithData('files', data)
this.currentSchema = schema
}
isFrontmatterSame(newFrontmatter: Array<Record<string, any>>) {
const newSchema = this.sqlSeal.db.getSchema(newFrontmatter)
return JSON.stringify(newSchema) === JSON.stringify(this.currentSchema)
}
}

123
src/SealObserver.ts Normal file
View file

@ -0,0 +1,123 @@
type Callback = () => void;
export class SealObserver {
private tables: Map<string, Set<Callback>>;
private verbose: boolean;
private files: Map<string, Set<string>>;
constructor(verbose = false) {
this.tables = new Map();
this.files = new Map();
this.verbose = verbose;
}
registerFile(fileName: string) {
if (!this.files.has(fileName)) {
this.files.set(fileName, new Set<string>());
if (this.verbose) {
console.log(`File "${fileName}" registered.`);
}
}
}
registerTableToFile(tableName: string, fileName: string) {
if (!this.files.has(fileName)) {
this.files.set(fileName, new Set<string>());
if (this.verbose) {
console.log(`File "${fileName}" registered.`);
}
}
const tables = this.files.get(fileName);
if (tables !== undefined) {
tables.add(tableName);
if (this.verbose) {
console.log(`Table "${tableName}" registered to file "${fileName}".`);
}
}
}
registerElementToFile(elementName: string, fileName: string) {
if (!this.files.has(fileName)) {
this.files.set(fileName, new Set<string>());
if (this.verbose) {
console.log(`File "${fileName}" registered.`);
}
}
const elements = this.files.get(fileName);
if (elements !== undefined) {
elements.add(elementName);
if (this.verbose) {
console.log(`Element "${elementName}" registered to file "${fileName}".`);
}
}
}
registerTable(tableName: string) {
if (!this.tables.has(tableName)) {
this.tables.set(tableName, new Set<Callback>());
if (this.verbose) {
console.log(`Table "${tableName}" registered.`);
}
}
}
registerObserver(tableNames: string | string[], observer: Callback) {
if (typeof tableNames === 'string') {
tableNames = [tableNames];
}
tableNames.forEach(tableName => {
if (!this.tables.has(tableName)) {
this.tables.set(tableName, new Set<Callback>());
if (this.verbose) {
console.log(`Table "${tableName}" registered.`);
}
}
const observers = this.tables.get(tableName);
if (observers !== undefined) {
observers.add(observer);
if (this.verbose) {
console.log(`Observer registered for table "${tableName}".`);
}
}
});
}
unregisterObserver(observer: Callback) {
this.tables.forEach(observers => {
observers.delete(observer);
});
if (this.verbose) {
console.log(`Observer unregistered.`);
}
}
fireObservers(tableName: string) {
if (this.tables.has(tableName)) {
const observers = this.tables.get(tableName);
if (observers) {
observers.forEach(observer => observer());
if (this.verbose) {
console.log(`Observers fired for table "${tableName}".`);
}
}
}
}
fireFilenameObservers(fileName: string) {
if (this.files.has(fileName)) {
const tables = this.files.get(fileName);
if (tables) {
tables.forEach(tableName => {
this.fireObservers(tableName);
});
}
}
this.tables.forEach(observers => {
observers.forEach(observer => observer());
});
if (this.verbose) {
console.log(`Observers fired for file "${fileName}".`);
}
}
}

View file

@ -13,10 +13,9 @@ function isNumeric(str: string) {
const toTypeStatements = (header: Array<string>, data: Array<Record<string, string>>) => {
let d: Array<Record<string, string|number>> = data
let types: Record<string, ReturnType<typeof predictType>> = {}
const types: Record<string, ReturnType<typeof predictType>> = {}
header.forEach(key => {
const type = predictType(key, data)
console.log('TYPE:', key, type)
if (type === 'REAL' || type === 'INTEGER') {
// converting all data here to text
d = d.map(record => ({
@ -65,9 +64,74 @@ export class SqlSealDatabase {
this.db = new Database(defaultDbPath, { verbose: verbose ? console.log : undefined })
}
async createTableWithData(name: string, data: Array<Record<string, unknown>>) {
const schema = await this.getSchema(data)
await this.createTable(name, schema)
await this.insertData(name, data)
return schema
}
updateData(name: string, data: Array<Record<string, unknown>>) {
const fields = Object.keys(data.reduce((acc, obj) => ({ ...acc, ...obj }), {}));
const update = this.db.prepare(`UPDATE ${name} SET ${fields.map((key: string) => `${key} = @${key}`).join(', ')} WHERE id = @id`);
const updateMany = this.db.transaction((pData: Array<Record<string, any>>) => {
pData.forEach(data => {
try {
update.run(data)
} catch (e) {
console.log(e)
}
})
})
return updateMany(data)
}
insertData(name: string, data: Array<Record<string, unknown>>) {
const fields = Object.keys(data.reduce((acc, obj) => ({ ...acc, ...obj }), {}));
const insert = this.db.prepare(`INSERT INTO ${name} (${fields.join(', ')}) VALUES (${fields.map((key: string) => '@' + key).join(', ')})`);
const insertMany = this.db.transaction((pData: Array<Record<string, any>>) => {
pData.forEach(data => {
try {
// update data so all missing fields are set to null
fields.forEach(field => {
if (!data[field]) {
data[field] = null
}
})
insert.run(data)
} catch (e) {
console.log(e)
}
})
})
return insertMany(data)
}
async createTable(name: string, fields: Record<string, 'TEXT'|'INTEGER'|'REAL'>) {
const sqlFields = Object.entries(fields).map(([key, type]) => `${key} ${type}`)
// FIXME: probably use schema generator, for now create with hardcoded fields
await this.db.prepare(`DROP TABLE IF EXISTS ${name}`).run()
const createSQL = `CREATE TABLE IF NOT EXISTS ${name} (
${sqlFields.join(', ')}
);`
await this.db.prepare(createSQL).run()
this.savedDatabases[name] = true
// Dropping data.
await this.db.prepare(`DELETE FROM ${name}`).run()
}
async getSchema(data: Array<Record<string, unknown>>) {
const fields = Object.keys(data.reduce((acc, obj) => ({ ...acc, ...obj }), {}));
const { types } = toTypeStatements(fields, data as Array<Record<string, string>>); // Call the toTypeStatements function with the correct arguments
return types;
}
async defineDatabaseFromUrl(unprefixedName: string, url: string, prefix: string) {
const name = prefixedIfNotGlobal(unprefixedName, [], prefix)
console.log('define db from url', name, url)
if (this.savedDatabases[name]) {
console.log('Database Exists', name)
return
@ -86,17 +150,9 @@ export class SqlSealDatabase {
})
const fields = parsed.meta.fields
const { data: parsedData, types } = toTypeStatements(fields, parsed.data)
console.log('TABLE CREATE', parsedData)
const sqlFields = Object.entries(types).map(([key, type]) => `${key} ${type}`).join(',\n')
// FIXME: probably use schema generator, for now create with hardcoded fields
await this.db.prepare(`DROP TABLE IF EXISTS ${name}`).run()
const createSQL = `CREATE TABLE IF NOT EXISTS ${name} (
${sqlFields}
);`
await this.db.prepare(createSQL).run()
this.savedDatabases[name] = url
await this.createTable(name, types)
// this.savedDatabases[name] = url
// Purge the database
await this.db.prepare(`DELETE FROM ${name}`).run()

View file

@ -28,7 +28,6 @@ const updateSelect = (selectAst: Select, globalTables: string[], prefix: string)
from: selectAst.from.map(from => {
// FIXME: better typing here.
if(isBaseFrom(from)) {
console.log('Found table', from.table, isGlobal(from.table, globalTables))
return {
...from,
table: prefixedIfNotGlobal(from.table, globalTables, prefix)

View file

@ -2,29 +2,12 @@ import * as Database from "better-sqlite3";
import GrayMatter from "gray-matter";
import { SqlSealDatabase, defineDatabaseFromUrl, instantiateDatabase } from "./database";
import { displayData, displayError, displayInfo } from "./ui";
import { App, Editor, MarkdownPostProcessorContext, MarkdownSectionInformation, WorkspaceLeaf } from "obsidian";
import { Parser } from "node-sql-parser";
import { App, MarkdownPostProcessorContext, MarkdownSectionInformation, WorkspaceLeaf } from "obsidian";
import { updateTables } from "./sqlReparseTables";
import { hashString } from "./hash";
const EditorLine = (line: number): EditorPosition => ({ line, ch: 0 })
function codeBlockHandler(markdown: string, containerEl: HTMLElement, ctx: MarkdownPostProcessorContext) {
const leaf: WorkspaceLeaf = app.workspace.rootSplit.children[0].children[0]
const view: MarkdownView = leaf.view
const editor: Editor = view.editor
const sectionInfo: MarkdownSectionInformation = ctx.getSectionInfo()
const lineStart = EditorLine(sectionInfo.lineStart + 1)
const lineEnd = EditorLine(sectionInfo.lineEnd )
const text = editor.getRange(lineStart, lineEnd)
const gm = GrayMatter(text)
const frontmatter = gm.data
return frontmatter
}
export class SqlSeal {
private db: SqlSealDatabase
public db: SqlSealDatabase
constructor(private readonly app: App, verbose = false) {
this.db = new SqlSealDatabase(app, verbose)
}
@ -33,14 +16,11 @@ export class SqlSeal {
if (ctx.frontmatter && Object.keys(ctx.frontmatter).length > 0) {
return ctx.frontmatter as Record<string, any>
}
console.log('ISSUE WITH FRONTMATTER')
const file = this.app.vault.getFileByPath(ctx.sourcePath)
if (!file) {
console.log('File not found')
return null
}
const data = await this.app.vault.cachedRead(file)
console.log('DATA', data)
const gm = GrayMatter(data)
const frontmatter = gm.data
@ -59,15 +39,12 @@ export class SqlSeal {
this.db.defineDatabaseFromUrl(name, url, prefix)
}
console.log(ctx)
const selectRegexp = /SELECT\s+(.*)/g;
const selectMatch = selectRegexp.exec(source)
if (selectMatch) {
try {
const selectStatement = selectMatch[0]
const selectUpdated = updateTables(selectStatement, [], prefix)
console.log('UPDATED SELECT:::', selectUpdated)
const selectUpdated = updateTables(selectStatement, ['files'], prefix)
const stmt = await this.db.db.prepare(selectUpdated)
const columns = await stmt.columns().map(column => column.name);
const data = await stmt.all(frontmatter)
@ -84,30 +61,3 @@ export class SqlSeal {
}
}
}
// export const sqlSealHandler = async (source, el, ctx) => {
// console.log('FRONTMATTER!!!!', ctx)
// const regex = /TABLE\s+(.+)\s+=\s+file\(([^)]+)\)/g;
// let match
// while ((match = regex.exec(source)) !== null) {
// const name = match[1];
// const url = match[2];
// defineDatabaseFromUrl(name, url)
// }
// const selectRegexp = /SELECT\s+(.*)/g;
// const selectMatch = selectRegexp.exec(source)
// if (selectMatch) {
// try {
// const stmt = await db.prepare(selectMatch[0])
// const columns = await stmt.columns().map(column => column.name);
// const data = await stmt.all(ctx.frontmatter)
// displayData
// (el, columns, data)
// } catch (e) {
// displayError(el, e)
// }
// }
// }

View file

@ -1,3 +1,3 @@
{
"1.0.0": "0.15.0"
"0.1.0": "0.15.0"
}

1214
yarn.lock

File diff suppressed because it is too large Load diff