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