Skip to content

Commit 54f0954

Browse files
committed
Open file
1 parent 097baea commit 54f0954

File tree

7 files changed

+124
-26
lines changed

7 files changed

+124
-26
lines changed

src/App.global.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
@tailwind base;
22
@tailwind components;
33
@tailwind utilities;
4+
5+
@layer components {
6+
.btn {
7+
@apply rounded-md bg-gray-50 px-2 py-0.5 outline-none text-xs border shadow-sm active:bg-blue-500 active:text-white disabled:opacity-50;
8+
}
9+
}

src/components/Main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const Main = () => {
5353
</nav>
5454

5555
{/* Main content */}
56-
<section className="relative flex flex-col w-full bg-gray-100 text-base">
56+
<section className="relative flex flex-col w-full bg-gray-100">
5757
<div className="h-full overflow-x-hidden overflow-y-auto px-6 my-6">
5858
{routes.map(({ path, Component }) => (
5959
<Route key={path} exact path={path}>

src/components/html-preview/HtmlPreview.tsx

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,49 @@
11
/* eslint-disable react/no-danger */
2+
import { ipcRenderer } from 'electron';
23
import React, { useState } from 'react';
34

45
const HtmlPreview = () => {
56
const [html, setHtml] = useState(
6-
'<h1>Hello</h1>\n<quote>This is a quote</quote>'
7+
'<h1>Hello</h1>\n<blockquote>This is a quote</blockquote>'
78
);
9+
const [opening, setOpening] = useState(false);
810

911
const handleChange = (evt: { target: { value: string } }) =>
1012
setHtml(evt.target.value);
1113

14+
const handleOpen = async () => {
15+
setOpening(true);
16+
const filters = [{ name: 'HTML Files', extensions: ['htm', 'html'] }];
17+
const content = await ipcRenderer.invoke('open-file', filters);
18+
setHtml(content);
19+
setOpening(false);
20+
};
21+
1222
return (
13-
<div className="flex min-h-full">
14-
<textarea
15-
onChange={handleChange}
16-
className="flex-1 min-h-full bg-white p-4"
17-
value={html}
18-
/>
19-
<div className="mx-1" />
20-
<section
21-
className="flex-1 min-h-full bg-blue-50 p-4 prose"
22-
dangerouslySetInnerHTML={{ __html: html }}
23-
/>
23+
<div className="min-h-full flex flex-col">
24+
<div className="flex justify-start mb-1">
25+
<button
26+
type="button"
27+
className="btn"
28+
onClick={handleOpen}
29+
disabled={opening}
30+
>
31+
Open...
32+
</button>
33+
</div>
34+
<div className="flex flex-1 min-h-full">
35+
<textarea
36+
onChange={handleChange}
37+
className="flex-1 min-h-full bg-white p-4 rounded-md"
38+
value={html}
39+
disabled={opening}
40+
/>
41+
<div className="mx-1" />
42+
<section
43+
className="flex-1 min-h-full bg-blue-50 p-4 prose rounded-md"
44+
dangerouslySetInnerHTML={{ __html: html }}
45+
/>
46+
</div>
2447
</div>
2548
);
2649
};

src/components/md-to-html/MarkdownToHtml.tsx

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,40 @@
11
/* eslint-disable react/no-danger */
22
import React, { useState } from 'react';
33
import marked from 'marked';
4+
import { ipcRenderer } from 'electron';
45

56
const Md2Html = () => {
67
const [md, setMd] = useState('# Hello\n> This is a quote');
78
const [preview, setPreview] = useState(false);
9+
const [opening, setOpening] = useState(false);
810

911
const handleChange = (evt: { target: { value: string } }) =>
1012
setMd(evt.target.value);
1113

14+
const handleOpen = async () => {
15+
setOpening(true);
16+
const filters = [
17+
{ name: 'Markdown Files', extensions: ['md', 'markdown', 'txt'] },
18+
];
19+
const content = await ipcRenderer.invoke('open-file', filters);
20+
setMd(content);
21+
setOpening(false);
22+
};
23+
1224
return (
1325
<div className="min-h-full flex flex-col">
14-
<div className="flex justify-end mb-1">
26+
<div className="flex justify-between mb-1">
27+
<button
28+
type="button"
29+
className="btn"
30+
onClick={handleOpen}
31+
disabled={opening}
32+
>
33+
Open...
34+
</button>
1535
<button
1636
type="button"
17-
className="rounded bg-gray-300 px-2 py-1 outline-none text-sm text-gray-600"
37+
className="btn"
1838
onClick={() => setPreview(!preview)}
1939
>
2040
{preview ? 'Raw HTML' : 'Preview'}
@@ -23,18 +43,19 @@ const Md2Html = () => {
2343
<div className="flex min-h-full flex-1">
2444
<textarea
2545
onChange={handleChange}
26-
className="flex-1 min-h-full bg-white p-4"
46+
className="flex-1 min-h-full bg-white p-4 rounded-md"
2747
value={md}
48+
disabled={opening}
2849
/>
2950
<div className="mx-1" />
3051
{preview ? (
3152
<section
32-
className="flex-1 min-h-full bg-blue-50 p-4 prose"
53+
className="flex-1 min-h-full bg-blue-50 p-4 prose rounded-md"
3354
dangerouslySetInnerHTML={{ __html: marked(md) }}
3455
/>
3556
) : (
3657
<textarea
37-
className="flex-1 min-h-full bg-blue-100 p-4"
58+
className="flex-1 min-h-full bg-blue-100 p-4 rounded-md"
3859
value={marked(md)}
3960
disabled
4061
/>

src/components/unix-timestamp/UnixTimestamp.tsx

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import dayjs from 'dayjs';
22
import React, { useState, useEffect } from 'react';
3+
import { clipboard } from 'electron';
34

45
const UnixTimestampConverter = () => {
56
const [date, setDate] = useState(dayjs());
7+
const [copied, setCopied] = useState(false);
68

79
useEffect(() => {
810
const timerID = setInterval(() => setDate(dayjs()), 1000);
@@ -12,12 +14,30 @@ const UnixTimestampConverter = () => {
1214
};
1315
});
1416

17+
const handleCopy = () => {
18+
clipboard.write({ text: `${date.unix()}` });
19+
setCopied(true);
20+
setTimeout(() => setCopied(false), 1_000);
21+
};
22+
1523
return (
16-
<div>
17-
The current Unix epoch time is
18-
<span className="text-lg bg-blue-200 mx-2 p-2 rounded inline-flex items-center content-center">
19-
{date.unix()}
20-
</span>
24+
<div className="min-h-full flex flex-col">
25+
<div className="flex justify-start mb-1">
26+
<button
27+
type="button"
28+
className="btn"
29+
onClick={handleCopy}
30+
disabled={copied}
31+
>
32+
{copied ? 'Copied' : 'Copy'}
33+
</button>
34+
</div>
35+
<div className="flex-1 min-h-full">
36+
The current Unix epoch time is
37+
<span className="text-base bg-blue-200 mx-2 px-2 py-1 rounded inline-flex items-center content-center font-mono">
38+
{date.unix()}
39+
</span>
40+
</div>
2141
</div>
2242
);
2343
};

src/main.dev.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@
1111
import 'core-js/stable';
1212
import 'regenerator-runtime/runtime';
1313
import path from 'path';
14-
import { app, BrowserWindow, shell } from 'electron';
14+
import { app, BrowserWindow, dialog, ipcMain, shell } from 'electron';
1515
import { autoUpdater } from 'electron-updater';
1616
import log from 'electron-log';
17+
import { FileFilter, IpcMainInvokeEvent } from 'electron/main';
18+
import fs from 'fs';
19+
import { promisify } from 'util';
1720
import MenuBuilder from './menu';
1821

1922
export default class AppUpdater {
@@ -112,9 +115,28 @@ const createWindow = async () => {
112115
};
113116

114117
/**
115-
* Add event listeners...
118+
* Handlers events from React
116119
*/
120+
ipcMain.handle(
121+
'open-file',
122+
async (_event: IpcMainInvokeEvent, filters: FileFilter[]) => {
123+
const files = await dialog.showOpenDialog({
124+
properties: ['openFile'],
125+
filters,
126+
});
127+
128+
let content = '';
129+
if (files) {
130+
const buffer = await promisify(fs.readFile)(files.filePaths[0]);
131+
content = buffer.toString();
132+
}
133+
return content;
134+
}
135+
);
117136

137+
/**
138+
* Add event listeners...
139+
*/
118140
app.on('window-all-closed', () => {
119141
// Respect the OSX convention of having the application in memory even
120142
// after all windows have been closed

tailwind.config.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ module.exports = {
55
purge: {
66
content: ['./src/**/*.{ts,tsx,html}'],
77
},
8-
variants: {},
8+
variants: {
9+
extend: {
10+
backgroundColor: ['active'],
11+
textColor: ['active'],
12+
opacity: ['disabled'],
13+
},
14+
},
915
plugins: [typography],
1016
};

0 commit comments

Comments
 (0)