diff --git a/README.md b/README.md
index 59f0251..4688d98 100644
--- a/README.md
+++ b/README.md
@@ -20,13 +20,6 @@ Transform your Obsidian vault into a reactive computational environment. Reactiv
- Obsidian v1.4.0 or higher
- React v18.2.0
-- Libraries:
- - Recharts v2.10.0
- - Lightweight Charts v4.1.0
- - d3-force v3.0.0
- - lucide-react v0.263.1
- - shadcn/ui (latest components)
-
## ๐ Quick Start
@@ -109,46 +102,31 @@ export default MyComponent;
-## ๐ Built-in Libraries & Components
+## ๐ Available Libraries & Components
-### React Core & Hooks
-```javascript
-import React, { useState, useEffect, useRef, useMemo } from react';
-```
-### UI Components from shadcn/ui
-```javascript
-import {
- Card, CardHeader, CardTitle, CardContent,
- Tabs, TabsList, TabsTrigger,
- Button, Dialog, Form
-} from '@/components/ui';
-```
-### Visualization Libraries
-```javascript
-//Include Responsive containers
-// Recharts for data visualization
-import {
- LineChart, BarChart, PieChart,
- Line, Bar, Pie,
- Tooltip, Legend
-} from 'recharts';
+ReactiveNotes provides access to popular libraries and components directly in your notes:
+### Core Libraries
+```jsx
+// React and hooks
+import React, { useState, useEffect, useRef, useMemo } from 'react';
-// Lightweight Charts for financial charts
+// Data visualization
+import { LineChart, BarChart, PieChart } from 'recharts';
import { createChart } from 'lightweight-charts';
-// D3-force for network graphs
-import {
- forceSimulation,
- forceLink
-} from 'd3-force';
-```
-### Icons
-```javascript
-import {
- Upload, Activity, AlertCircle,
- TrendingUp, TrendingDown
-} from 'lucide-react';
+// Data processing
+import Papa from 'papaparse'; // CSV parsing
+import * as XLSX from 'xlsx'; // Excel files
+import * as mathjs from 'mathjs'; // Mathematics
+import { format, parseISO } from 'date-fns'; // Date handling
+
+// Interface components
+import { Card, Tabs, Switch } from '@/components/ui';
+import { TrendingUp, Activity, Settings } from 'lucide-react'; // 70+ icons
```
+See the full [component reference](./COMPONENTS.md) for a complete list of available libraries, components and utilities.
+Even if needed libraries are not provided within this list CDN imports are always an option.
+
## ๐ป Core Features
### 1. Component Systems
@@ -161,21 +139,20 @@ import {
- Real-time component updates
- Error reporting
-### 3. Canvas Manipulation
-- Direct canvas access for custom drawing
-- Real-time canvas updates
-- Image processing capabilities
-- Custom overlays and annotations
-- Interactive drawing tools
+### 2. Canvas Manipulation
+- HTML Canvas API support for custom drawing
+- THREE.js integration for 3D graphics
+- Lightweight Charts for financial visualization
+- D3 force layout integration for network graphs
+- Support for both 2D and 3D rendering contexts
-
-### 5. Performance
+### 3. Performance
- Efficient re-rendering
-- Memory leak prevention
+- Proper cleanup of resources in useEffect hooks
- Lazy loading support
-### 2. State Management
+### 4. State Management
```javascript
// Local state with React
const [local, setLocal] = useState(0);
@@ -189,7 +166,7 @@ const [stored, setStored] = useStorage('key', defaultValue);
- Cross-note state management
### Note-Specific Data Persistence
-- Each note maintains independent state through frontmatter
+- Each note maintains independent state through frontmatter storage under a key called react_data
- Component states persist across sessions
- Data automatically travels with notes when shared
- Multiple instances of same component can have different states
@@ -210,19 +187,97 @@ const Counter = () => {
return ;
};
```
+#### Browser LocalStorage
+
+For components that need to share state across notes or maintain state independent of specific notes, browser localStorage is available.
-### 3. File System Integration
```javascript
-// Read files with encoding
-const text = await window.fs.readFile('data.txt', {
- encoding: 'utf8'
-});
+// Create a localStorage hook
+const useLocalStorage = (key, defaultValue) => {
+ const [state, setState] = useState(() => {
+ try {
+ const storedValue = localStorage.getItem(key);
+ return storedValue ? JSON.parse(storedValue) : defaultValue;
+ } catch (error) {
+ console.error(`Error reading localStorage key "${key}":`, error);
+ return defaultValue;
+ }
+ });
-// Read binary files
-const binary = await window.fs.readFile('data.xlsx');
+ useEffect(() => {
+ localStorage.setItem(key, JSON.stringify(state));
+ }, [key, state]);
+
+ return [state, setState];
+};
+
+// Use it in components
+const [value, setValue] = useLocalStorage('shared-key', defaultValue);
+//This key is shared across all notes
```
-### 4. CDN Library Support
+Key features:
+- Data persists across browser sessions
+- Shared state across all notes
+- Independent of note content
+- Approximately 5-10MB storage per domain
+- Persists until browser data is cleared
+
+### 5. File Reading Utility
+
+The plugin provides a flexible file reading utility through the `readFile` function that helps you interactively select and read files from your vault.
+```javascript
+const fileData = await readFile( path,extensions);
+```
+### Parameters
+- `path` (optional): Direct path to a specific file. If provided, skips the file selector. Default: `null`
+- `extensions` (optional): Array of file extensions to filter by. Default: `['txt', 'md', 'json', 'csv']`
+
+### Return Value
+
+Returns a Promise that resolves to an object containing:
+
+- `path`: Full file path
+- `name`: File name without extension
+- `extension`: File extension
+- `content`: String content of the file
+
+Returns `null` if the operation is canceled or fails, allowing you to handle this case gracefully.
+
+```javascript
+// Interactive file selection with file type filtering
+const textFile = await readFile(null,['txt', 'md']);
+if (textFile) {
+ console.log(`Selected: ${textFile.name}`);
+ console.log(`Content: ${textFile.content}`);
+}
+
+// Direct file access by path (no modal)
+const jsonFile = await readFile('path/to/config.json');
+if (jsonFile) {
+ const config = JSON.parse(jsonFile.content);
+}
+
+// Read CSV files
+const csvFile = await readFile(null,['csv']);
+if (csvFile) {
+ // Process CSV content
+ const lines = csvFile.content.split('\n');
+ const headers = lines[0].split(',');
+ // ...
+}
+
+// Read binary files like Excel spreadsheets
+const excelFile = await readFile(null,['xlsx', 'xls']);
+if (excelFile) {
+ // excelFile.content contains the file data
+ // Process with SheetJS or other libraries
+}
+```
+This utility is particularly useful for components that need to process files from your vault, such as data visualizations, document analysis, and custom importers.
+
+
+### 6. CDN Library Support
Import additional libraries from cdnjs:
```javascript
import Chart from 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.min.js';
@@ -233,24 +288,18 @@ Requirements:
- Imports at component top
- Browser-compatible libraries only
-### 5. Theme Integration
+### 7. Theme Integration
```javascript
-const theme = getTheme(); // Returns 'dark' or 'light'
+const theme = getTheme(); // Returns 'dark' or 'light' correspodning to obsidian theme
const styles = {
background: theme === 'dark' ? 'var(--background-primary)' : 'white',
color: theme === 'dark' ? 'var(--text-normal)' : 'black'
};
+// Or use 'dark:' within your components
```
-### File System Access
-```javascript
-// Read files
-const csv = await window.fs.readFile('data.csv', { encoding: 'utf8' });
-const binary = await window.fs.readFile('data.xlsx');
-```
-
## ๐จ Component Examples
### Interactive Data Visualization
@@ -366,6 +415,17 @@ const NetworkGraph = () => {
**Tabbed UI**

+**Progression Dashboard**
+
+Knowledge progression dashboard using persistant storage and ability to make new notes/categories
+
+
+**Integrated use case with MCP**
+
+ MCP fetches data from youtube and uses Reactive Notes to display react component for visualising fetched data.
+
+
+
## ๐ ๏ธDevelopment Guide
### Component Structure
```react
@@ -515,8 +575,8 @@ Best practices for optimal performance:
1. **File System**
```javascript
// File operations
- await window.fs.readFile(path, options);
- const text = await window.fs.readFile(path, { encoding: 'utf8' });
+ let file = await readFile(path, extensions);
+ let content=file.content;
```
2. **Theme Management**
diff --git a/assets/IntegratedUsage.gif b/assets/IntegratedUsage.gif
new file mode 100644
index 0000000..d97f1f2
Binary files /dev/null and b/assets/IntegratedUsage.gif differ
diff --git a/assets/ProgressionDashboard.gif b/assets/ProgressionDashboard.gif
new file mode 100644
index 0000000..da29522
Binary files /dev/null and b/assets/ProgressionDashboard.gif differ
diff --git a/assets/gamingObsidian.gif b/assets/gamingObsidian.gif
new file mode 100644
index 0000000..1671420
Binary files /dev/null and b/assets/gamingObsidian.gif differ
diff --git a/main.tsx b/main.tsx
index aaa0a1a..20fea6b 100644
--- a/main.tsx
+++ b/main.tsx
@@ -1,4 +1,4 @@
-import { Plugin, MarkdownPostProcessorContext, MarkdownRenderChild, MarkdownRenderer, TFile, App, stringifyYaml, Notice } from 'obsidian';
+import { Plugin, MarkdownPostProcessorContext, MarkdownRenderChild, MarkdownRenderer, TFile, App, stringifyYaml, Notice, SuggestModal } from 'obsidian';
import { createRoot } from 'react-dom/client';
import { transform } from '@babel/standalone';
import * as React from 'react';
@@ -312,7 +312,100 @@ private getThreeJs() {
}),
//lazy load THREE if needed
...needsThree ? this.getThreeJs() : undefined,
- };
+readFile: async (path=null, extensions = ['txt', 'md', 'json','csv']) => {
+ return new Promise((resolve) => {
+ let isResolved = false;
+ let cancelTimeoutHandle: number | undefined = undefined; // To store timeout handle for cancellation
+ // Create a SuggestModal subclass that lists vault files
+
+ // If a specific path is provided, read that file directly without showing modal
+ if (path) {
+ const file = this.app.vault.getAbstractFileByPath(path);
+ if (file && file instanceof TFile) {
+ this.app.vault.cachedRead(file).then(content => {
+ isResolved = true;
+ resolve({
+ path: file.path,
+ name: file.name,
+ content: content
+ });
+ }).catch(err => {
+ console.error("Failed to read file:", err);
+ isResolved = true;
+ resolve(null);
+ });
+ } else {
+ console.error("File not found:", path);
+ isResolved = true;
+ resolve(null);
+ }
+ return;
+ }
+ class FileSuggestModal extends SuggestModal {
+ getSuggestions(query:string) {
+ // Get all files with the right extension
+ const files = this.app.vault.getFiles()
+ .filter(file => extensions.includes(file.extension));
+
+ // Filter by query if provided
+ if (query) {
+ return files.filter(file =>
+ file.path.toLowerCase().includes(query.toLowerCase())
+ );
+ }
+ return files;
+ }
+
+ renderSuggestion(file:TFile, el: HTMLElement) {
+ el.createEl("div", { text: file.name });
+ el.createEl("small", { text: file.path });
+ }
+
+ onChooseSuggestion(file:TFile, evt: MouseEvent|KeyboardEvent) {
+ console.log("File content:");
+ if (cancelTimeoutHandle !== undefined) {
+ clearTimeout(cancelTimeoutHandle);
+ cancelTimeoutHandle = undefined;
+ }
+ this.app.vault.cachedRead(file).then(content => {isResolved = true;
+ resolve({
+ path: file.path,
+ name: file.basename,
+ extension: file.extension,
+ content: content
+ });
+ }).catch(err => {
+ console.error("Failed to read file:", err);
+ resolve(null);});}
+ }
+
+ // Show the modal
+ const modal = new FileSuggestModal(this.app);
+ modal.setPlaceholder("Select a file");
+ modal.onClose = () => {
+ // If onClose is called, and a suggestion hasn't ALREADY been chosen and processed,
+ // this implies a cancellation or an edge case where onClose fires very quickly.
+ // We use a setTimeout to yield to the event loop, giving onChooseSuggestion
+ // a chance to run if it was triggered by the same user action that closed the modal.
+ if (cancelTimeoutHandle !== undefined) { // Clear previous timeout if any (e.g. rapid open/close)
+ clearTimeout(cancelTimeoutHandle);
+ }
+ cancelTimeoutHandle = window.setTimeout(() => { // window.setTimeout for NodeJS.Timeout type
+ // After yielding, if the promise hasn't been resolved by onChooseSuggestion
+ // and a suggestion wasn't flagged as chosen, then resolve as null (cancellation).
+ if (!isResolved) {
+ resolve(null);
+ }
+ // If suggestionChosen IS true here, it means onChooseSuggestion ran,
+ // set the flag, and it's responsible for resolving (or has already).
+ // If promiseResolved IS true, it's already handled.
+ }, 0); // Yield to the event loop.
+ };
+ modal.open();
+
+ });
+ }
+ };
// Execute the code and get the component
const Component = await new Function(
@@ -392,6 +485,7 @@ export default class ReactNotesPlugin extends Plugin {
);
// Register a global Markdown postprocessor for HTML
this.registerMarkdownPostProcessor((element, context) => {
+ // Only process elements that are within ReactiveNotes components
if (element.closest('.react-component-container')) {
this.parseMarkdownInHtml(element);
}
diff --git a/manifest.json b/manifest.json
index 1d0cf87..f7032fd 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"id": "reactive-notes",
"name": "Reactive Notes",
- "version": "1.0.1",
+ "version": "1.0.2",
"minAppVersion": "1.4.0",
"description": "Transform your vault into a reactive computational environment. Create dynamic React components directly in your notes.",
"author": "Elias Noesis",
diff --git a/package-lock.json b/package-lock.json
index 5fade6c..24b7c0f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "reactive-notes",
- "version": "1.0.1",
+ "version": "1.0.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
diff --git a/package.json b/package.json
index 7ce8aa9..05fcb7b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "reactive-notes",
- "version": "1.0.1",
+ "version": "1.0.2",
"description": "Transform your vault into a reactive computational environment with React components.",
"main": "main.js",
"scripts": {
diff --git a/src/components/componentRegistry.tsx b/src/components/componentRegistry.tsx
index e753d26..a41d54e 100644
--- a/src/components/componentRegistry.tsx
+++ b/src/components/componentRegistry.tsx
@@ -1,5 +1,4 @@
-// src/config/componentRegistry.ts
-//Can be modified to add more components and libraries
+// src/config/componentRegistry.tsx
import React from 'react';
import {
PieChart, Pie, Cell, ResponsiveContainer, Tooltip, Legend,
@@ -29,8 +28,8 @@ import {
import { format, addDays, differenceInDays, parseISO } from 'date-fns';
import { parse, stringify } from 'csv/sync';
import * as mathjs from 'mathjs';
-
-
+import * as XLSX from 'xlsx';
+import Papa from 'papaparse';
// Core React hooks wrapper
// Type for React utilities (non-component exports)
@@ -106,6 +105,8 @@ RadialBarChart, RadialBar,
dateFns: { format, addDays, differenceInDays, parseISO },
csv: { parse, stringify },
math: mathjs,
+ XLSX,
+ Papa,
} as const;
diff --git a/versions.json b/versions.json
index 39cbfbb..8a45d43 100644
--- a/versions.json
+++ b/versions.json
@@ -1,3 +1,3 @@
{
- "1.0.1": "1.4.0"
+ "1.0.2": "1.4.0"
}