commit
49602e5a53
22 changed files with 14685 additions and 0 deletions
@ -0,0 +1,5 @@ |
|||||||
|
.idea |
||||||
|
.vscode |
||||||
|
.DS_Store |
||||||
|
|
||||||
|
node_modules |
@ -0,0 +1,18 @@ |
|||||||
|
Copyright 2022, MikO <miko@massivedynamic.eu> |
||||||
|
|
||||||
|
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. |
@ -0,0 +1,2 @@ |
|||||||
|
# ation |
||||||
|
– a simple keynote software for Markdown files – written in `electron` |
@ -0,0 +1,7 @@ |
|||||||
|
"use strict"; |
||||||
|
|
||||||
|
const {contextBridge, ipcRenderer} = require("electron"); |
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld("api", { |
||||||
|
openFile : () => ipcRenderer.invoke("WindowManager::openFile").then(response => alert(JSON.stringify(response))) |
||||||
|
}); |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@ |
|||||||
|
{ |
||||||
|
"name": "ation", |
||||||
|
"version": "0.0.1", |
||||||
|
"description": "a simple presentation software", |
||||||
|
"main": "main.js", |
||||||
|
"scripts": { |
||||||
|
"start": "electron .", |
||||||
|
"dev": "concurrently \"cd src/ui && cross-env BROWSER=none npm start\" \"wait-on tcp:3000 && electron .\"" |
||||||
|
}, |
||||||
|
"author": "MikO <miko@massivedynamic.eu>", |
||||||
|
"license": "MIT", |
||||||
|
"dependencies": { |
||||||
|
"electron": "^21.0.0", |
||||||
|
"jsonmark": "0.0.1", |
||||||
|
"markdown-json": "^1.6.1" |
||||||
|
}, |
||||||
|
"devDependencies": { |
||||||
|
"concurrently": "^7.4.0", |
||||||
|
"cross-env": "^7.0.3", |
||||||
|
"wait-on": "^6.0.1" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
"use strict"; |
||||||
|
|
||||||
|
const WindowManager = require("./WindowManager"); |
||||||
|
|
||||||
|
class Ation { |
||||||
|
windowManager; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
if (Ation.Instances > 0) |
||||||
|
throw new Error("Only one Instance of Ation possible"); |
||||||
|
this.windowManager = new WindowManager();
|
||||||
|
} |
||||||
|
} |
||||||
|
Ation.Instances = 0; |
||||||
|
|
||||||
|
module.exports = Ation; |
@ -0,0 +1,8 @@ |
|||||||
|
const isDevelopment = () => { |
||||||
|
const mode = process.env.NODE_ENV || "development"; |
||||||
|
return mode === "development"; |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
isDevelopment |
||||||
|
}; |
@ -0,0 +1,79 @@ |
|||||||
|
"use strict"; |
||||||
|
|
||||||
|
const {app, BrowserWindow, ipcMain, dialog} = require("electron"); |
||||||
|
const path = require("path"); |
||||||
|
const fs = require("fs/promises"); |
||||||
|
|
||||||
|
const JSONMark = require("jsonmark"); |
||||||
|
|
||||||
|
const {isDevelopment} = require("./Util"); |
||||||
|
|
||||||
|
class WindowManager { |
||||||
|
mainWindow; |
||||||
|
windows; |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.mainWindow = null; |
||||||
|
this.windows = []; |
||||||
|
|
||||||
|
app.whenReady().then(() => this.init()); |
||||||
|
|
||||||
|
ipcMain.handle("WindowManager::openFile", async () => { |
||||||
|
if (!this.mainWindow) |
||||||
|
return null; |
||||||
|
|
||||||
|
const result = await dialog.showOpenDialog(this.mainWindow, { |
||||||
|
title : "open file", |
||||||
|
filters : [ |
||||||
|
{ |
||||||
|
name : "Markdown files", |
||||||
|
extensions : [".md"] |
||||||
|
} |
||||||
|
] |
||||||
|
}); |
||||||
|
|
||||||
|
if (result.canceled || result.filePaths.length < 1) |
||||||
|
return null; |
||||||
|
|
||||||
|
const fileContents = await fs.readFile(result.filePaths[0], {encoding : "utf-8"}); |
||||||
|
const data = JSONMark.parse(fileContents); |
||||||
|
|
||||||
|
console.log(data); |
||||||
|
|
||||||
|
return data; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
init() { |
||||||
|
this.mainWindow = WindowManager._CreateWindow(); |
||||||
|
|
||||||
|
this.mainWindow. |
||||||
|
|
||||||
|
if (isDevelopment()) |
||||||
|
this.mainWindow.loadURL("http://localhost:3000"); |
||||||
|
else |
||||||
|
this.mainWindow.loadFile(`file://${path.join(__dirname, "ui", "build", "index.html")}`); |
||||||
|
} |
||||||
|
|
||||||
|
static _CreateWindow(options = null, parent = null) { |
||||||
|
const windowOptions = { |
||||||
|
width : 800, |
||||||
|
height : 600, |
||||||
|
show : false, |
||||||
|
webPreferences : { |
||||||
|
contextIsolation : true, |
||||||
|
preload : path.join(__dirname, "..", "contextAPI.js") |
||||||
|
}, |
||||||
|
parent, |
||||||
|
...options |
||||||
|
}; |
||||||
|
if (!parent) |
||||||
|
delete windowOptions.parent; |
||||||
|
const window = new BrowserWindow(windowOptions); |
||||||
|
window.once("ready-to-show", () => window.show());
|
||||||
|
|
||||||
|
return window; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = WindowManager; |
@ -0,0 +1,23 @@ |
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. |
||||||
|
|
||||||
|
# dependencies |
||||||
|
/node_modules |
||||||
|
/.pnp |
||||||
|
.pnp.js |
||||||
|
|
||||||
|
# testing |
||||||
|
/coverage |
||||||
|
|
||||||
|
# production |
||||||
|
/build |
||||||
|
|
||||||
|
# misc |
||||||
|
.DS_Store |
||||||
|
.env.local |
||||||
|
.env.development.local |
||||||
|
.env.test.local |
||||||
|
.env.production.local |
||||||
|
|
||||||
|
npm-debug.log* |
||||||
|
yarn-debug.log* |
||||||
|
yarn-error.log* |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,39 @@ |
|||||||
|
{ |
||||||
|
"name": "ui", |
||||||
|
"version": "0.1.0", |
||||||
|
"private": true, |
||||||
|
"dependencies": { |
||||||
|
"@testing-library/jest-dom": "^5.16.5", |
||||||
|
"@testing-library/react": "^13.4.0", |
||||||
|
"@testing-library/user-event": "^13.5.0", |
||||||
|
"react": "^18.2.0", |
||||||
|
"react-dom": "^18.2.0", |
||||||
|
"react-scripts": "5.0.1", |
||||||
|
"sass": "^1.55.0", |
||||||
|
"web-vitals": "^2.1.4" |
||||||
|
}, |
||||||
|
"scripts": { |
||||||
|
"start": "react-scripts start", |
||||||
|
"build": "react-scripts build", |
||||||
|
"test": "react-scripts test", |
||||||
|
"eject": "react-scripts eject" |
||||||
|
}, |
||||||
|
"eslintConfig": { |
||||||
|
"extends": [ |
||||||
|
"react-app", |
||||||
|
"react-app/jest" |
||||||
|
] |
||||||
|
}, |
||||||
|
"browserslist": { |
||||||
|
"production": [ |
||||||
|
">0.2%", |
||||||
|
"not dead", |
||||||
|
"not op_mini all" |
||||||
|
], |
||||||
|
"development": [ |
||||||
|
"last 1 chrome version", |
||||||
|
"last 1 firefox version", |
||||||
|
"last 1 safari version" |
||||||
|
] |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html lang="en"> |
||||||
|
<head> |
||||||
|
<meta charset="utf-8" /> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" /> |
||||||
|
<title>ation</title> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div id="root"></div> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,3 @@ |
|||||||
|
.slides-list { |
||||||
|
background: #222; |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
.window { |
||||||
|
display: grid; |
||||||
|
grid-template-columns: 200px auto; |
||||||
|
height: 100%; |
||||||
|
width: 100%; |
||||||
|
position: absolute; |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
@import "window"; |
||||||
|
@import "slidesList"; |
||||||
|
|
||||||
|
* { |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
html, body { |
||||||
|
font-size: 16px; |
||||||
|
} |
||||||
|
|
||||||
|
body { |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
import React, {useEffect, useState} from "react"; |
||||||
|
|
||||||
|
import SlidesList from "./SlidesList"; |
||||||
|
import Modes from "../models/Modes"; |
||||||
|
|
||||||
|
const Ation = () => { |
||||||
|
const [mode, setMode] = useState(Modes.NORMAL); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
const keyHandler = event => { |
||||||
|
switch (event.key) { |
||||||
|
case "F5": |
||||||
|
document.documentElement.requestFullscreen(); |
||||||
|
setMode(Modes.PRESENT); |
||||||
|
break; |
||||||
|
default: |
||||||
|
return; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const fullscreenHandler = () => { |
||||||
|
if (!document.fullscreenElement) |
||||||
|
setMode(Modes.NORMAL); |
||||||
|
}; |
||||||
|
|
||||||
|
window.addEventListener("keydown", keyHandler); |
||||||
|
window.addEventListener("fullscreenchange", fullscreenHandler); |
||||||
|
|
||||||
|
return () => { |
||||||
|
window.removeEventListener("keydown", keyHandler); |
||||||
|
window.removeEventListener("fullscreenchange", fullscreenHandler); |
||||||
|
} |
||||||
|
|
||||||
|
}, []); |
||||||
|
|
||||||
|
return ( |
||||||
|
<section className={`window${mode === Modes.PRESENT ? " fullscreen" : ""}`}> |
||||||
|
<SlidesList /> |
||||||
|
<main className="main"> |
||||||
|
MAIN |
||||||
|
<button onClick={() => window.api.openFile()}>open</button> |
||||||
|
</main> |
||||||
|
</section> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default Ation; |
@ -0,0 +1,25 @@ |
|||||||
|
import React, {useState} from "react"; |
||||||
|
|
||||||
|
import Slide from "../models/Slide"; |
||||||
|
|
||||||
|
const Slides = [ |
||||||
|
new Slide("", "ABC"), |
||||||
|
new Slide("", "DEF"), |
||||||
|
new Slide("", "GHI"), |
||||||
|
new Slide("", "JKL"), |
||||||
|
new Slide("", "MNO") |
||||||
|
]; |
||||||
|
|
||||||
|
const SlidesList = () => { |
||||||
|
return ( |
||||||
|
<aside className="slides-list"> |
||||||
|
{Slides.map((slide, index) => ( |
||||||
|
<div key={index}> |
||||||
|
{slide.content} |
||||||
|
</div> |
||||||
|
))} |
||||||
|
</aside> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default SlidesList; |
@ -0,0 +1,13 @@ |
|||||||
|
import React from "react"; |
||||||
|
import ReactDOM from "react-dom/client"; |
||||||
|
|
||||||
|
import Ation from "./components/Ation"; |
||||||
|
|
||||||
|
import "./assets/css/ation.scss"; |
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(document.getElementById("root")); |
||||||
|
root.render( |
||||||
|
<React.StrictMode> |
||||||
|
<Ation /> |
||||||
|
</React.StrictMode> |
||||||
|
); |
@ -0,0 +1,6 @@ |
|||||||
|
const Modes = Object.freeze({ |
||||||
|
NORMAL : 1, |
||||||
|
PRESENT : 2 |
||||||
|
}); |
||||||
|
|
||||||
|
export default Modes; |
@ -0,0 +1,11 @@ |
|||||||
|
class Slide { |
||||||
|
title; |
||||||
|
content; |
||||||
|
|
||||||
|
constructor(title, content) { |
||||||
|
this.title = title; |
||||||
|
this.content = content; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export default Slide; |
Loading…
Reference in new issue