From 794d2125a595c9538ed097b726ad1141918ea82c Mon Sep 17 00:00:00 2001 From: amitwh Date: Mon, 1 Sep 2025 19:19:45 +0530 Subject: [PATCH] Initial commit: Pan Converter - Cross-platform Markdown editor --- .gitignore | 18 ++ CLAUDE.md | 189 ++++++++++++++++ PUSH_INSTRUCTIONS.md | 95 ++++++++ README.md | 75 +++++++ assets/icon.png | Bin 0 -> 8819 bytes assets/icon.svg | 8 + assets/icon@2x.png | Bin 0 -> 20742 bytes package.json | 69 ++++++ push-to-github.sh | 83 +++++++ scripts/generate-icons.js | 43 ++++ setup-upstream.sh | 37 +++ src/index.html | 88 ++++++++ src/main.js | 242 ++++++++++++++++++++ src/renderer.js | 218 ++++++++++++++++++ src/styles.css | 462 ++++++++++++++++++++++++++++++++++++++ 15 files changed, 1627 insertions(+) create mode 100644 .gitignore create mode 100644 CLAUDE.md create mode 100644 PUSH_INSTRUCTIONS.md create mode 100644 README.md create mode 100644 assets/icon.png create mode 100644 assets/icon.svg create mode 100644 assets/icon@2x.png create mode 100644 package.json create mode 100755 push-to-github.sh create mode 100644 scripts/generate-icons.js create mode 100755 setup-upstream.sh create mode 100644 src/index.html create mode 100644 src/main.js create mode 100644 src/renderer.js create mode 100644 src/styles.css diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2bff97e --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +node_modules/ +dist/ +*.log +.DS_Store +Thumbs.db +.env +.env.local +*.swp +*.swo +*~ +.vscode/ +.idea/ +*.iml +out/ +.cache/ +.npm/ +.electron/ +package-lock.json \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..6d928cc --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,189 @@ +# Pan Converter + +## Overview +Pan Converter is a cross-platform desktop application for editing and converting Markdown files using Pandoc. It features a rich markdown editor with live preview, multiple themes, and support for exporting to various formats. + +## Features +- **Markdown Editor**: Full-featured editor with syntax highlighting +- **Live Preview**: Real-time preview of your markdown content +- **Multiple Themes**: Light, Dark, Solarized, Monokai, and GitHub themes +- **Format Conversion**: Export to HTML, PDF, DOCX, LaTeX, RTF, ODT, and EPUB using Pandoc +- **Cross-Platform**: Runs on Windows, macOS, and Linux +- **Auto-Save**: Automatic saving every 30 seconds + +## Requirements +- **Pandoc**: Must be installed on the system for export functionality + - Ubuntu/Debian: `sudo apt-get install pandoc` + - macOS: `brew install pandoc` + - Windows: Download from https://pandoc.org/installing.html + +## Development Setup + +### Prerequisites +- Node.js (v14 or higher) +- npm or yarn +- Pandoc (for export features) + +### Installation +```bash +# Clone the repository +git clone +cd pan-converter + +# Install dependencies +npm install + +# Generate icons +npm run generate-icons + +# Start the application +npm start +``` + +## Building + +### Build for Current Platform +```bash +npm run build +``` + +### Build for Specific Platforms +```bash +# Windows +npm run build:win + +# macOS +npm run build:mac + +# Linux (generates .deb, .AppImage, and .snap) +npm run build:linux +``` + +### Build for All Platforms +```bash +npm run dist:all +``` + +## Project Structure +``` +pan-converter/ +├── src/ +│ ├── main.js # Main process +│ ├── renderer.js # Renderer process +│ ├── index.html # Main window HTML +│ └── styles.css # Application styles +├── assets/ +│ ├── icon.svg # Source icon +│ └── icon.png # Generated icons +├── scripts/ +│ └── generate-icons.js # Icon generation script +├── package.json # Project configuration +└── CLAUDE.md # This file +``` + +## Architecture + +### Main Process (`src/main.js`) +- Manages application lifecycle +- Creates and manages windows +- Handles file operations +- Manages menu bar +- Communicates with renderer via IPC + +### Renderer Process (`src/renderer.js`) +- Handles UI interactions +- Manages editor state +- Renders markdown preview +- Handles theme switching +- Communicates with main process via IPC + +### Key Libraries +- **Electron**: Desktop application framework +- **marked**: Markdown parsing +- **highlight.js**: Syntax highlighting +- **DOMPurify**: HTML sanitization +- **electron-store**: Persistent storage +- **electron-builder**: Application packaging + +## Features Implementation + +### Markdown Editor +- Uses native textarea with custom styling +- Supports common markdown shortcuts via toolbar +- Tab key inserts 4 spaces for code indentation +- Auto-save every 30 seconds when content changes + +### Live Preview +- Updates in real-time as you type +- Sanitized HTML output for security +- Syntax highlighting for code blocks +- GitHub-flavored markdown support + +### Theme System +- Themes stored in user preferences +- Applied via CSS classes on body element +- Persists across application restarts +- Five built-in themes + +### Export System +- Uses Pandoc command-line tool +- Supports multiple output formats +- Maintains original file structure +- Shows error messages if Pandoc not installed + +## Platform-Specific Branches + +The repository maintains separate branches for platform-specific customizations: +- `master`: Main development branch +- `linux`: Linux-specific configurations +- `macos`: macOS-specific configurations +- `windows`: Windows-specific configurations + +## Debian Package + +The application can be built as a `.deb` package for Debian-based systems: +```bash +npm run build:linux +``` + +The generated `.deb` file will: +- Install to `/opt/pan-converter` +- Create desktop entry for application menu +- Set up proper file associations +- Declare Pandoc as dependency + +## Testing + +### Manual Testing +1. File Operations: New, Open, Save, Save As +2. Editor Features: Bold, Italic, Headings, Links, Code, Lists, Quotes +3. Preview Toggle: Show/hide preview pane +4. Theme Switching: Test all five themes +5. Export Functions: Test each export format +6. Auto-save: Verify 30-second auto-save works + +### Platform Testing +Test on: +- Windows 10/11 +- macOS 11+ (Big Sur and later) +- Ubuntu 20.04+, Debian 11+ + +## Known Issues +- ICO and ICNS icons need manual conversion from PNG +- Pandoc must be installed separately +- Large files may cause performance issues + +## Future Enhancements +- [ ] Add spell checking +- [ ] Implement find and replace +- [ ] Add custom CSS for preview +- [ ] Support for markdown extensions +- [ ] Plugin system for custom exporters +- [ ] Cloud sync capabilities +- [ ] Collaborative editing + +## License +MIT License + +## Support +For issues or feature requests, please open an issue on the GitHub repository. \ No newline at end of file diff --git a/PUSH_INSTRUCTIONS.md b/PUSH_INSTRUCTIONS.md new file mode 100644 index 0000000..88ef9f0 --- /dev/null +++ b/PUSH_INSTRUCTIONS.md @@ -0,0 +1,95 @@ +# Push Instructions for Pan Converter + +## Prerequisites +1. Create a new repository on GitHub: https://github.com/new + - Repository name: `pan-converter` + - Keep it empty (no README, .gitignore, or license) + - Make it public or private as desired + +2. Set up authentication: + - **Option A - SSH Key** (Recommended): + ```bash + # Check if you have SSH key + ls -la ~/.ssh/id_rsa.pub + + # If not, generate one: + ssh-keygen -t rsa -b 4096 -C "your_email@example.com" + + # Add to GitHub: Settings > SSH and GPG keys > New SSH key + cat ~/.ssh/id_rsa.pub + ``` + + - **Option B - Personal Access Token**: + - Go to GitHub Settings > Developer settings > Personal access tokens + - Generate new token with 'repo' scope + - Save the token securely + +## Push Commands + +### Using SSH (Recommended) +```bash +# Set SSH remote +git remote set-url origin git@github.com:amitwh/pan-converter.git + +# Push all branches +git push -u origin master +git push origin linux +git push origin macos +git push origin windows +``` + +### Using HTTPS with Token +```bash +# Set HTTPS remote +git remote set-url origin https://github.com/amitwh/pan-converter.git + +# Push all branches (you'll be prompted for username and token) +git push -u origin master +git push origin linux +git push origin macos +git push origin windows + +# When prompted: +# Username: amitwh +# Password: [paste your Personal Access Token] +``` + +### Using GitHub CLI (if installed) +```bash +# Login to GitHub CLI +gh auth login + +# Create repo and push +gh repo create pan-converter --public --source=. --remote=origin --push +``` + +## Automated Script +```bash +# Run the provided script +./push-to-github.sh +``` + +## Verify Success +After pushing, verify at: https://github.com/amitwh/pan-converter + +You should see: +- 4 branches (master, linux, macos, windows) +- All project files +- Complete commit history + +## Troubleshooting + +### Authentication Failed +- For SSH: Ensure your SSH key is added to GitHub +- For HTTPS: Use Personal Access Token, not password +- Check token has 'repo' scope + +### Repository Not Found +- Ensure repository exists on GitHub +- Check spelling: `pan-converter` +- Verify you're logged into the correct GitHub account + +### Permission Denied +- Check repository ownership +- Ensure you have write access +- Verify authentication method is correct \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b7273e3 --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +# Pan Converter + +A cross-platform Markdown editor and converter powered by Pandoc. + +![Pan Converter](assets/icon.png) + +## Features + +- 📝 **Rich Markdown Editor** - Full-featured editor with syntax highlighting +- 👁️ **Live Preview** - See your markdown rendered in real-time +- 🎨 **Multiple Themes** - Choose from Light, Dark, Solarized, Monokai, or GitHub themes +- 📤 **Export to Multiple Formats** - Convert to HTML, PDF, DOCX, LaTeX, RTF, ODT, and EPUB +- 💾 **Auto-Save** - Never lose your work with automatic saving +- 🖥️ **Cross-Platform** - Works on Windows, macOS, and Linux + +## Installation + +### Prerequisites +- [Pandoc](https://pandoc.org/installing.html) must be installed for export functionality + +### Download +Download the latest release for your platform from the [Releases](https://github.com/yourusername/pan-converter/releases) page. + +### Install from Source +```bash +git clone https://github.com/yourusername/pan-converter.git +cd pan-converter +npm install +npm start +``` + +## Usage + +1. **Write** - Use the editor to write your Markdown content +2. **Preview** - Toggle the preview pane to see rendered output +3. **Theme** - Choose your preferred theme from the View menu +4. **Export** - Export your document to various formats via File > Export + +## Keyboard Shortcuts + +- `Ctrl/Cmd + N` - New file +- `Ctrl/Cmd + O` - Open file +- `Ctrl/Cmd + S` - Save file +- `Ctrl/Cmd + Shift + S` - Save as +- `Ctrl/Cmd + P` - Toggle preview +- `Ctrl/Cmd + Enter` - Toggle preview (alternative) + +## Building + +```bash +# Build for current platform +npm run build + +# Build for specific platform +npm run build:win +npm run build:mac +npm run build:linux + +# Build for all platforms +npm run dist:all +``` + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +## License + +MIT License - see LICENSE file for details + +## Acknowledgments + +- Built with [Electron](https://www.electronjs.org/) +- Markdown parsing by [marked](https://marked.js.org/) +- Export functionality powered by [Pandoc](https://pandoc.org/) \ No newline at end of file diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..68324cd943d7ebf34564f4233e372d54c03ebccf GIT binary patch literal 8819 zcmeHNc~n#9w*LYMB14>Nq6on{pj8Rh0RbUcrCJrR+7>N@fM^8;0wM%~3~H596tq|; zhM-(fizHALBr$^`pkj>(7={cW#XyiG2q7Wi$uIk69BN8G5$h< z%XwG;oWWNc*MF0oHz19=8Kc;Z)8x;b?mP3_12!|a9LIk1?KcDI*X3K5ubM=FOYRmHBTi?>XwO(Q>slY0r(+@N!Y*HT_Y z>xO`WcBheZ;p7aN>{R{ffvx>jRbEi`s$$iuVmNH4f%^(G6I@zVEGX?$TD7yz54<1^ zOBxL1Od0%x`?FN7mLyg+mxb4mx59nBekfsxmU&>yKqWWD^zzxAz-$?AXI5rF$k6Ss z+s?V%G zZNs|T<=|AkJ`fHYUI2U@u9Tv06X}C~7hbYSPs5{`Z zwS)c*t(ZNGt|zpC?bnL>MQez>J;~{5XQQHjZG!)@TzgsLf(47p;Ek7!R=0`J3c@{e zp!L1{Le3=`aHMGE-QGVM1S=%-Jq#HZjML593wdiR(v*9;RZ@A2`w7AkP=5b1i{2l1 zGPc6H`(6Wiw63yLfPwyk^0o9A3sMg%+R5K{dt1$cfK@+!(i0cChdVpcBP_k)=q06lS)Pj2AWwW|anKE(qDv>yh5<_!lqhNJTb z;ErOxv*8trk+QeL#i%A62IF+=wXp)q#zd{op5Dal9p9hgKxs2QNdrS(qiE1D*C1cl z3Y!}G)QJrwa6Kb-nuM<7qY4KMnKk%XU>D0jh<>E!H9S~ z-b@RiGz>JCd7Jw-Y1wuHUl3Lu>wkkyd+`T+FNg#u#smql)T6+c$SrC1a`lY= zqp2WSPrerGN16+^Z(1o)C^E?K1Kt4-iWb4h@mk_|9~H6^z7_l z!7nUaCxgXB+=Pk{pM8UQTl;m}I`;s^p>3|PWTgbgfhYG?N&9|iOu9x}WcUIwQlcw4 zCcn(e=B-6|3vj)@WC^r29mIV*d;$P%>nb9(N%M)2kj0-03Y~IG`sLpD%;Wqg4(bm> z^@wGJ9s_1K@iJhXUZq*kpk0brS%L&k6s4soREs!04aw_s(?o^nX`; ze{<@mXZ{WQ{TqK1D2AQ<*YNxA{@!I}<%0ihKK^Ha6Is4E59M5qmq1f9c9WWBc@v(q zoWTN&6g=kL-zX*|HV&B&OfmC4)M$4`;;`*7v=j5uXhhEnu@N=ja*k`c2|MmDbPk zS8yieu=za7@hlco(x2)18Nz{H%P&-xjzmJW7?b$TM8q$ze>`RH1mX88uPXL8`H66$xaRpY zCTeQD-{rcdm^6k$r%Mx6a|v0}cDQjUU2LU&G*K5HL;7b>5+IqtMFveBhnMkT;dlxQ ziR;Jnf(m$_ms|wsqe+t{61w(%Ldi&mtlB@BfW7lQ6Fxe8N74Tm7&=$GU;KDc`iQ_s z1U@40pF`lE`LFu-{~PT(&b<4!q^CiiXgmtBDU!;xL9f_glSn=~_Ex~xz9iyP2K*Z^ zA+(e-v9?B#lG2kn)*-(;mv%qe z8eYwcH+rL&%Touu8{m;*Fq^xN1b3X+?(~axzLR^7{eqqM@&IRBQ8Mr5cwrQ zIwHK{&aa{lv&;!2t*EfcE7GfJ$Xuw`0<hlt3>M_APVRtq^{zy`- z`0Yz7R^x`if;UP#MyT7~ZSUfCfE^pD=igRD5Gf6i(FD1VwPAE)MyZ+}K9G(=I{?*P z#T6>y2{MZr4=`gAqf5mo4QOH;xR^K1-l7?jxccKi1xB1Fx3Y#&Y9{3A)YR9VE2|^|V^(dBcL@NA zht0sYWgvw6YJ`XL(Dr!4G8--JG)#9WTMghdD zWAhb{1uf3Vge)I`8B+k86we@I$szG8^MiQmEX0k5^`HoTlOg9M#-yWyCNW@rN$`2K z{-G>=PCDD$R2sBya83QY@TblNCz(wUPLSBIf>k;P0=HiAx~7ZYR7JR}uuG;5i*d$E zlO~5#bL3d!TuTWa*y5Jhy1=?QHK)%w!5=_41h8uaT|+ANUDZqD1$XIarz#49V#kJh z2QyY?Qyt(&n(4P-d+1==2Djby;X=qvsM#ON5#?LcyOw7|eGwlxEH_K;ymG67E_k(u zQ*)9h))!Y1M)8BZ-aD!A_nmQA&7gWt-d2E3yDc|5W_2z7S;T$Ac29cGkz{~<_S`O8 zAQpH_zS+;_TG9IW>9|`JJ~B<#!)hnXb&&weKT$5OLZ6K9vBHgBe&q`oo}MVM2!UZI zWR!YRT66|j?(&^HP9c^h7(H+kYixC&?$WYq;vBlWMAq+0XrsB1NgI*%1cn@G@}S0O z6grQmIy`&rt<=q$5PNf{MZi}5rLFGs)`Mxi3E6qb&GiH;g}_a+cz<#(?QX5wB|u(f z+-+KF(r5JuXf5;M88T5IFIFuYOb<4@H05S|c`!Qz@#U>*E>Qv;Wu zreRNWN_<1XY8Ioy?&|Jqp4Ep&uyL#AoJ1(O9O)y^LAjjLFJ7(0IJNQ`pL?1MJAeNq z9TQ}Sfc*VrP<{o~8IcHkS+Ne@^t4TG2Iah+6nar-`3At?%rXPfVE}7y%`%+LPt5)n zshK%|e`w`Z9!(g@M#h>GvABVI@wM5Oc2y?ml31P=DHuH3B5#!U?>{D52y9L2mQ?vK z`o|yQDF<=)1Ek(d1Y*6KOnpPuqLt~io?(@EIyx1r2{9LzUqO?OBW=lPDYDOHVC+?g zkKk>3`bm<(OBn&nR#R{Wx6Wz=9L>R>5Y|U51j3U@b69TT)dhBy~t`lo>7@U2(&4CI7&kSoJ+Ghttc&LkiG6{qiKHaQLvPuAls zL-r$t#fMP(WrSEnz#%#y8(09w6s04kG+HYQZ7~1BYQ@;}uJveh)=KckN9GVKgXYag z5^??iVw(A{&Y1uHb^BkLT>m|sC-S0Oc1$+PM#RNBU5?0NHa~C--sDc zb`zo}`ZcvUT%DNTU+WJfdA)kCWMs1o3T;I*; znJhtaBcfK?3#NjwBOw!-KuI3Qc|sycOH#hvW84c9yO0wu%Wg<;^%!!@tUWY(uCkPL zijPHzneLU&qpvtsQi{7MoP+}V_7bV`S4)cq4p<8ip~r*668u?;3gZrAPLQU8^0^&J zH}LucAX&8!s`5jA*wXxb*aL7N1(DU>SR?C86`M#{ zq%qw3HUR3JVI~|`G;w?fEHxhC)_+MZQ-PC*)pqtNzAw^~3h6yLu_i96^UoUtdK(MS zzPh8mk9li9SgU^c_F(%C@+m%|ZL$2DT5=1H)V7egyH;P`L7Gmzg1F2rX(oOD#=RgR z3M#1V7kx=u2DXccVx}W)lp+V^sj~|E<j?4+vzC zl|A6jD{Ri*NLmH9XWv``F`W(!$sugmHG1gq2}E~%RQaTD@-`@u;joNSaXGJNO<0u zbY~-GGH7;DD)IQbIl*3HA6}m!`vk@sEED|J5APiyLj+nL{dbs;NZ08Y8?Y?*1)X+K zF)Sy|t#5VJPI`s}dK$hVXVF!!)^6(1UicAX4~9lw)y8Mfa5*37s9Lq^=qprM5+a<| zlwzu>kh08+mNn?H@$8r+8L z1yp6)M(EETt1MM#(srQ2o?Lzbw`s~aekJj;j2M!<1UWAVM5slU8S|K82R&1wV~h zr~BR1ahb(mlrkL@dIu1pe48hBaM%i61yUAaJC&(5E*9X)PaIR=<(z9Y=M;IqB$8{u zOZ=uXPF*ur{hoG*{Imjk+XNrhM;7)O(yzMMf%YQi8~Aux4s@c?sH{7!2s!lHtCDLH ztRl+O0~dgE=G7HTv + + + + + + PAN + \ No newline at end of file diff --git a/assets/icon@2x.png b/assets/icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f4cd1ac8a929fbbe0ec7200dc37844d868857c76 GIT binary patch literal 20742 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" + } + } +} \ No newline at end of file diff --git a/push-to-github.sh b/push-to-github.sh new file mode 100755 index 0000000..db22e4f --- /dev/null +++ b/push-to-github.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +echo "==========================================" +echo "Pan Converter - GitHub Push Helper" +echo "==========================================" +echo "" +echo "Before running this script, please ensure:" +echo "1. You have created a repository named 'pan-converter' on GitHub" +echo "2. The repository is empty (no README, .gitignore, or license)" +echo "3. You have set up authentication (SSH key or Personal Access Token)" +echo "" +read -p "Have you completed the above steps? (y/n): " -n 1 -r +echo "" + +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Please complete the setup first:" + echo "1. Go to https://github.com/new" + echo "2. Create a new repository named 'pan-converter'" + echo "3. DO NOT initialize with README, .gitignore, or license" + echo "4. Set up SSH key or Personal Access Token for authentication" + exit 1 +fi + +echo "" +echo "Choose authentication method:" +echo "1. SSH (Recommended if you have SSH keys set up)" +echo "2. HTTPS (Requires Personal Access Token)" +read -p "Enter your choice (1 or 2): " AUTH_CHOICE + +if [ "$AUTH_CHOICE" = "1" ]; then + echo "Using SSH authentication..." + git remote set-url origin git@github.com:amitwh/pan-converter.git +elif [ "$AUTH_CHOICE" = "2" ]; then + echo "Using HTTPS authentication..." + echo "You'll need to enter your GitHub username and Personal Access Token" + git remote set-url origin https://github.com/amitwh/pan-converter.git +else + echo "Invalid choice. Exiting." + exit 1 +fi + +echo "" +echo "Pushing branches to GitHub..." +echo "==============================" + +# Push master branch with upstream tracking +echo "Pushing master branch..." +if git push -u origin master; then + echo "✓ Master branch pushed successfully" +else + echo "✗ Failed to push master branch" + echo "Please check your authentication and try again" + exit 1 +fi + +# Push other branches +echo "Pushing linux branch..." +if git push origin linux; then + echo "✓ Linux branch pushed successfully" +else + echo "✗ Failed to push linux branch" +fi + +echo "Pushing macos branch..." +if git push origin macos; then + echo "✓ macOS branch pushed successfully" +else + echo "✗ Failed to push macos branch" +fi + +echo "Pushing windows branch..." +if git push origin windows; then + echo "✓ Windows branch pushed successfully" +else + echo "✗ Failed to push windows branch" +fi + +echo "" +echo "==========================================" +echo "Push complete!" +echo "Your repository is available at:" +echo "https://github.com/amitwh/pan-converter" +echo "==========================================" \ No newline at end of file diff --git a/scripts/generate-icons.js b/scripts/generate-icons.js new file mode 100644 index 0000000..46d1f69 --- /dev/null +++ b/scripts/generate-icons.js @@ -0,0 +1,43 @@ +const sharp = require('sharp'); +const fs = require('fs'); +const path = require('path'); + +const sizes = { + 'icon.png': 512, + 'icon@2x.png': 1024, + 'icon.ico': 256, + 'icon.icns': 512 +}; + +const svgPath = path.join(__dirname, '..', 'assets', 'icon.svg'); +const svgBuffer = fs.readFileSync(svgPath); + +async function generateIcons() { + for (const [filename, size] of Object.entries(sizes)) { + const outputPath = path.join(__dirname, '..', 'assets', filename); + + if (filename.endsWith('.png')) { + await sharp(svgBuffer) + .resize(size, size) + .png() + .toFile(outputPath); + console.log(`Generated ${filename}`); + } else if (filename.endsWith('.ico')) { + // For ICO, we'll just use the PNG version + await sharp(svgBuffer) + .resize(size, size) + .png() + .toFile(outputPath.replace('.ico', '.png')); + console.log(`Generated ${filename.replace('.ico', '.png')} (use png2ico to convert)`); + } else if (filename.endsWith('.icns')) { + // For ICNS, we'll just use the PNG version + await sharp(svgBuffer) + .resize(size, size) + .png() + .toFile(outputPath.replace('.icns', '.png')); + console.log(`Generated ${filename.replace('.icns', '.png')} (use png2icns to convert)`); + } + } +} + +generateIcons().catch(console.error); \ No newline at end of file diff --git a/setup-upstream.sh b/setup-upstream.sh new file mode 100755 index 0000000..5fe3e5b --- /dev/null +++ b/setup-upstream.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# Pan Converter - Git Remote Setup Script +# +# Instructions: +# 1. Create a new repository on GitHub named "pan-converter" +# 2. Replace YOUR_GITHUB_USERNAME with your actual GitHub username +# 3. Run this script: bash setup-upstream.sh + +GITHUB_USERNAME="amitwh" +REPO_NAME="pan-converter" + +echo "Setting up remote repository..." + +# Add remote origin +git remote add origin "https://github.com/$GITHUB_USERNAME/$REPO_NAME.git" + +echo "Pushing all branches to remote..." + +# Push master branch +git push -u origin master + +# Push platform-specific branches +git push origin linux +git push origin macos +git push origin windows + +echo "Repository setup complete!" +echo "" +echo "Your repository is now available at:" +echo "https://github.com/$GITHUB_USERNAME/$REPO_NAME" +echo "" +echo "Branch structure:" +echo " - master (main development)" +echo " - linux (Linux-specific)" +echo " - macos (macOS-specific)" +echo " - windows (Windows-specific)" diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..1d6882c --- /dev/null +++ b/src/index.html @@ -0,0 +1,88 @@ + + + + + + Pan Converter - Markdown Editor + + + + + +
+
+ + + + + + + +
+ +
+ +
+
+ +
+
+
+
+
+ +
+ Ready + Words: 0 | Characters: 0 +
+
+ + + + \ No newline at end of file diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..7f3a9a7 --- /dev/null +++ b/src/main.js @@ -0,0 +1,242 @@ +const { app, BrowserWindow, Menu, dialog, ipcMain, shell } = require('electron'); +const path = require('path'); +const fs = require('fs'); +const { exec } = require('child_process'); +const Store = require('electron-store'); + +const store = new Store(); + +let mainWindow; +let currentFile = null; + +function createWindow() { + mainWindow = new BrowserWindow({ + width: 1200, + height: 800, + webPreferences: { + nodeIntegration: true, + contextIsolation: false + }, + icon: path.join(__dirname, '../assets/icon.png') + }); + + mainWindow.loadFile(path.join(__dirname, 'index.html')); + + createMenu(); + + mainWindow.on('closed', () => { + mainWindow = null; + }); +} + +function createMenu() { + const template = [ + { + label: 'File', + submenu: [ + { + label: 'New', + accelerator: 'CmdOrCtrl+N', + click: () => mainWindow.webContents.send('file-new') + }, + { + label: 'Open', + accelerator: 'CmdOrCtrl+O', + click: openFile + }, + { + label: 'Save', + accelerator: 'CmdOrCtrl+S', + click: () => mainWindow.webContents.send('file-save') + }, + { + label: 'Save As', + accelerator: 'CmdOrCtrl+Shift+S', + click: saveAsFile + }, + { type: 'separator' }, + { + label: 'Export', + submenu: [ + { label: 'HTML', click: () => exportFile('html') }, + { label: 'PDF', click: () => exportFile('pdf') }, + { label: 'DOCX', click: () => exportFile('docx') }, + { label: 'LaTeX', click: () => exportFile('latex') }, + { label: 'RTF', click: () => exportFile('rtf') }, + { label: 'ODT', click: () => exportFile('odt') }, + { label: 'EPUB', click: () => exportFile('epub') } + ] + }, + { type: 'separator' }, + { + label: 'Quit', + accelerator: process.platform === 'darwin' ? 'Cmd+Q' : 'Ctrl+Q', + click: () => app.quit() + } + ] + }, + { + label: 'Edit', + submenu: [ + { label: 'Undo', accelerator: 'CmdOrCtrl+Z', role: 'undo' }, + { label: 'Redo', accelerator: 'CmdOrCtrl+Y', role: 'redo' }, + { type: 'separator' }, + { label: 'Cut', accelerator: 'CmdOrCtrl+X', role: 'cut' }, + { label: 'Copy', accelerator: 'CmdOrCtrl+C', role: 'copy' }, + { label: 'Paste', accelerator: 'CmdOrCtrl+V', role: 'paste' }, + { label: 'Select All', accelerator: 'CmdOrCtrl+A', role: 'selectAll' } + ] + }, + { + label: 'View', + submenu: [ + { + label: 'Toggle Preview', + accelerator: 'CmdOrCtrl+P', + click: () => mainWindow.webContents.send('toggle-preview') + }, + { + label: 'Theme', + submenu: [ + { label: 'Light', click: () => setTheme('light') }, + { label: 'Dark', click: () => setTheme('dark') }, + { label: 'Solarized', click: () => setTheme('solarized') }, + { label: 'Monokai', click: () => setTheme('monokai') }, + { label: 'GitHub', click: () => setTheme('github') } + ] + }, + { type: 'separator' }, + { label: 'Reload', accelerator: 'CmdOrCtrl+R', role: 'reload' }, + { label: 'Toggle DevTools', accelerator: 'F12', role: 'toggleDevTools' }, + { type: 'separator' }, + { label: 'Zoom In', accelerator: 'CmdOrCtrl+Plus', role: 'zoomIn' }, + { label: 'Zoom Out', accelerator: 'CmdOrCtrl+-', role: 'zoomOut' }, + { label: 'Reset Zoom', accelerator: 'CmdOrCtrl+0', role: 'resetZoom' } + ] + }, + { + label: 'Help', + submenu: [ + { + label: 'About', + click: () => { + dialog.showMessageBox(mainWindow, { + type: 'info', + title: 'About Pan Converter', + message: 'Pan Converter', + detail: 'A cross-platform Markdown editor and converter using Pandoc.\n\nVersion: 1.0.0\nLicense: MIT', + buttons: ['OK'] + }); + } + }, + { + label: 'Documentation', + click: () => shell.openExternal('https://github.com/yourusername/pan-converter') + } + ] + } + ]; + + const menu = Menu.buildFromTemplate(template); + Menu.setApplicationMenu(menu); +} + +function openFile() { + const files = dialog.showOpenDialogSync(mainWindow, { + properties: ['openFile'], + filters: [ + { name: 'Markdown', extensions: ['md', 'markdown'] }, + { name: 'All Files', extensions: ['*'] } + ] + }); + + if (files && files[0]) { + currentFile = files[0]; + const content = fs.readFileSync(currentFile, 'utf-8'); + mainWindow.webContents.send('file-opened', { path: currentFile, content }); + } +} + +function saveAsFile() { + const file = dialog.showSaveDialogSync(mainWindow, { + defaultExt: '.md', + filters: [ + { name: 'Markdown', extensions: ['md', 'markdown'] }, + { name: 'All Files', extensions: ['*'] } + ] + }); + + if (file) { + currentFile = file; + mainWindow.webContents.send('get-content-for-save', file); + } +} + +function exportFile(format) { + if (!currentFile) { + dialog.showErrorBox('Error', 'Please save the file first'); + return; + } + + const outputFile = dialog.showSaveDialogSync(mainWindow, { + defaultPath: currentFile.replace(/\.[^/.]+$/, `.${format}`), + filters: [ + { name: format.toUpperCase(), extensions: [format] } + ] + }); + + if (outputFile) { + const pandocCmd = `pandoc "${currentFile}" -o "${outputFile}"`; + + exec(pandocCmd, (error, stdout, stderr) => { + if (error) { + dialog.showErrorBox('Export Error', `Failed to export: ${error.message}\n\nMake sure Pandoc is installed.`); + } else { + dialog.showMessageBox(mainWindow, { + type: 'info', + title: 'Export Complete', + message: `File exported successfully to ${outputFile}`, + buttons: ['OK'] + }); + } + }); + } +} + +function setTheme(theme) { + store.set('theme', theme); + mainWindow.webContents.send('theme-changed', theme); +} + +// IPC handlers +ipcMain.on('save-file', (event, { path, content }) => { + fs.writeFileSync(path, content, 'utf-8'); + currentFile = path; +}); + +ipcMain.on('save-current-file', (event, content) => { + if (currentFile) { + fs.writeFileSync(currentFile, content, 'utf-8'); + } else { + saveAsFile(); + } +}); + +ipcMain.on('get-theme', (event) => { + const theme = store.get('theme', 'light'); + event.reply('theme-changed', theme); +}); + +app.whenReady().then(createWindow); + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit(); + } +}); + +app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow(); + } +}); \ No newline at end of file diff --git a/src/renderer.js b/src/renderer.js new file mode 100644 index 0000000..6fb247a --- /dev/null +++ b/src/renderer.js @@ -0,0 +1,218 @@ +const { ipcRenderer } = require('electron'); +const marked = require('marked'); +const DOMPurify = require('dompurify'); +const hljs = require('highlight.js'); + +// Configure marked +marked.setOptions({ + highlight: function(code, lang) { + if (lang && hljs.getLanguage(lang)) { + try { + return hljs.highlight(code, { language: lang }).value; + } catch (err) {} + } + return hljs.highlightAuto(code).value; + }, + breaks: true, + gfm: true +}); + +// Elements +const editor = document.getElementById('editor'); +const preview = document.getElementById('preview'); +const previewPane = document.getElementById('preview-pane'); +const editorPane = document.getElementById('editor-pane'); +const statusText = document.getElementById('status-text'); +const wordCount = document.getElementById('word-count'); + +// State +let isPreviewVisible = true; +let currentContent = ''; +let isDirty = false; + +// Initialize +document.addEventListener('DOMContentLoaded', () => { + // Request current theme + ipcRenderer.send('get-theme'); + + // Set up auto-save interval + setInterval(autoSave, 30000); // Auto-save every 30 seconds + + // Initialize with empty content + updatePreview(); + updateWordCount(); +}); + +// Editor input handler +editor.addEventListener('input', () => { + currentContent = editor.value; + isDirty = true; + updatePreview(); + updateWordCount(); + updateStatus('Modified'); +}); + +// Toolbar button handlers +document.getElementById('btn-bold').addEventListener('click', () => insertMarkdown('**', '**')); +document.getElementById('btn-italic').addEventListener('click', () => insertMarkdown('*', '*')); +document.getElementById('btn-heading').addEventListener('click', () => insertMarkdown('## ', '')); +document.getElementById('btn-link').addEventListener('click', () => insertMarkdown('[', '](url)')); +document.getElementById('btn-code').addEventListener('click', () => insertMarkdown('`', '`')); +document.getElementById('btn-list').addEventListener('click', () => insertMarkdown('- ', '')); +document.getElementById('btn-quote').addEventListener('click', () => insertMarkdown('> ', '')); +document.getElementById('btn-preview-toggle').addEventListener('click', togglePreview); + +// Markdown insertion helper +function insertMarkdown(before, after) { + const start = editor.selectionStart; + const end = editor.selectionEnd; + const selectedText = editor.value.substring(start, end); + const replacement = before + (selectedText || 'text') + after; + + editor.value = editor.value.substring(0, start) + replacement + editor.value.substring(end); + + // Set cursor position + if (selectedText) { + editor.selectionStart = start; + editor.selectionEnd = start + replacement.length; + } else { + editor.selectionStart = start + before.length; + editor.selectionEnd = start + before.length + 4; // Select "text" + } + + editor.focus(); + + // Trigger input event + editor.dispatchEvent(new Event('input')); +} + +// Update preview +function updatePreview() { + const html = marked.parse(editor.value); + const clean = DOMPurify.sanitize(html); + preview.innerHTML = clean; + + // Re-highlight code blocks + preview.querySelectorAll('pre code').forEach((block) => { + hljs.highlightElement(block); + }); +} + +// Toggle preview visibility +function togglePreview() { + isPreviewVisible = !isPreviewVisible; + + if (isPreviewVisible) { + previewPane.classList.remove('hidden'); + editorPane.classList.remove('full-width'); + } else { + previewPane.classList.add('hidden'); + editorPane.classList.add('full-width'); + } +} + +// Update word count +function updateWordCount() { + const text = editor.value; + const words = text.trim() ? text.trim().split(/\s+/).length : 0; + const chars = text.length; + wordCount.textContent = `Words: ${words} | Characters: ${chars}`; +} + +// Update status +function updateStatus(text) { + statusText.textContent = text; +} + +// Auto-save function +function autoSave() { + if (isDirty && currentContent) { + ipcRenderer.send('save-current-file', currentContent); + isDirty = false; + updateStatus('Auto-saved'); + setTimeout(() => updateStatus('Ready'), 2000); + } +} + +// IPC event handlers +ipcRenderer.on('file-new', () => { + if (isDirty) { + if (confirm('You have unsaved changes. Do you want to continue?')) { + editor.value = ''; + currentContent = ''; + isDirty = false; + updatePreview(); + updateWordCount(); + updateStatus('New file'); + } + } else { + editor.value = ''; + currentContent = ''; + updatePreview(); + updateWordCount(); + updateStatus('New file'); + } +}); + +ipcRenderer.on('file-opened', (event, { path, content }) => { + editor.value = content; + currentContent = content; + isDirty = false; + updatePreview(); + updateWordCount(); + updateStatus(`Opened: ${path}`); +}); + +ipcRenderer.on('file-save', () => { + ipcRenderer.send('save-current-file', editor.value); + isDirty = false; + updateStatus('Saved'); +}); + +ipcRenderer.on('get-content-for-save', (event, path) => { + ipcRenderer.send('save-file', { path, content: editor.value }); + isDirty = false; + updateStatus(`Saved: ${path}`); +}); + +ipcRenderer.on('toggle-preview', () => { + togglePreview(); +}); + +ipcRenderer.on('theme-changed', (event, theme) => { + // Remove all theme classes + document.body.classList.remove('theme-light', 'theme-dark', 'theme-solarized', 'theme-monokai', 'theme-github'); + + // Add new theme class + if (theme !== 'light') { + document.body.classList.add(`theme-${theme}`); + } + + updateStatus(`Theme: ${theme}`); +}); + +// Keyboard shortcuts +document.addEventListener('keydown', (e) => { + // Ctrl/Cmd + Enter to toggle preview + if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { + togglePreview(); + } + + // Tab key in editor + if (e.key === 'Tab' && e.target === editor) { + e.preventDefault(); + const start = editor.selectionStart; + const end = editor.selectionEnd; + + editor.value = editor.value.substring(0, start) + ' ' + editor.value.substring(end); + editor.selectionStart = editor.selectionEnd = start + 4; + } +}); + +// Prevent accidental navigation +window.addEventListener('beforeunload', (e) => { + if (isDirty) { + e.preventDefault(); + e.returnValue = ''; + } +}); \ No newline at end of file diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..04004b1 --- /dev/null +++ b/src/styles.css @@ -0,0 +1,462 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + overflow: hidden; + height: 100vh; +} + +.container { + display: flex; + flex-direction: column; + height: 100vh; +} + +/* Toolbar */ +.toolbar { + display: flex; + align-items: center; + padding: 8px 12px; + background: #f5f5f5; + border-bottom: 1px solid #ddd; + gap: 4px; +} + +.toolbar button { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border: 1px solid transparent; + background: transparent; + border-radius: 4px; + cursor: pointer; + transition: all 0.2s; +} + +.toolbar button:hover { + background: #e0e0e0; + border-color: #ccc; +} + +.toolbar button:active { + background: #d0d0d0; +} + +.toolbar-separator { + width: 1px; + height: 24px; + background: #ccc; + margin: 0 8px; +} + +/* Editor Container */ +.editor-container { + display: flex; + flex: 1; + overflow: hidden; +} + +.pane { + flex: 1; + overflow: auto; +} + +#editor-pane { + border-right: 1px solid #ddd; +} + +#editor { + width: 100%; + height: 100%; + padding: 20px; + font-family: 'SF Mono', Monaco, 'Courier New', monospace; + font-size: 14px; + line-height: 1.6; + border: none; + outline: none; + resize: none; +} + +#preview-pane { + padding: 20px; + background: #fff; +} + +#preview { + max-width: 800px; + margin: 0 auto; +} + +/* Status Bar */ +.status-bar { + display: flex; + justify-content: space-between; + padding: 4px 12px; + background: #f5f5f5; + border-top: 1px solid #ddd; + font-size: 12px; + color: #666; +} + +/* Preview Styles */ +#preview h1, #preview h2, #preview h3, #preview h4, #preview h5, #preview h6 { + margin-top: 24px; + margin-bottom: 16px; + font-weight: 600; + line-height: 1.25; +} + +#preview h1 { + font-size: 2em; + border-bottom: 1px solid #eaecef; + padding-bottom: 0.3em; +} + +#preview h2 { + font-size: 1.5em; + border-bottom: 1px solid #eaecef; + padding-bottom: 0.3em; +} + +#preview p { + margin-bottom: 16px; + line-height: 1.6; +} + +#preview code { + padding: 0.2em 0.4em; + margin: 0; + font-size: 85%; + background-color: rgba(27,31,35,0.05); + border-radius: 3px; + font-family: 'SF Mono', Monaco, 'Courier New', monospace; +} + +#preview pre { + padding: 16px; + overflow: auto; + font-size: 85%; + line-height: 1.45; + background-color: #f6f8fa; + border-radius: 3px; + margin-bottom: 16px; +} + +#preview pre code { + display: inline; + max-width: auto; + padding: 0; + margin: 0; + overflow: visible; + line-height: inherit; + word-wrap: normal; + background-color: transparent; + border: 0; +} + +#preview blockquote { + padding: 0 1em; + color: #6a737d; + border-left: 0.25em solid #dfe2e5; + margin-bottom: 16px; +} + +#preview ul, #preview ol { + padding-left: 2em; + margin-bottom: 16px; +} + +#preview li { + margin-bottom: 4px; +} + +#preview a { + color: #0366d6; + text-decoration: none; +} + +#preview a:hover { + text-decoration: underline; +} + +#preview img { + max-width: 100%; + height: auto; +} + +#preview table { + border-collapse: collapse; + margin-bottom: 16px; + width: 100%; +} + +#preview table th, +#preview table td { + padding: 6px 13px; + border: 1px solid #dfe2e5; +} + +#preview table th { + font-weight: 600; + background-color: #f6f8fa; +} + +#preview table tr:nth-child(2n) { + background-color: #f6f8fa; +} + +/* Theme: Dark */ +body.theme-dark { + background: #1e1e1e; + color: #d4d4d4; +} + +body.theme-dark .toolbar { + background: #2d2d30; + border-bottom-color: #3e3e42; +} + +body.theme-dark .toolbar button { + color: #cccccc; +} + +body.theme-dark .toolbar button:hover { + background: #3e3e42; + border-color: #464647; +} + +body.theme-dark .toolbar-separator { + background: #3e3e42; +} + +body.theme-dark #editor-pane { + border-right-color: #3e3e42; +} + +body.theme-dark #editor { + background: #1e1e1e; + color: #d4d4d4; +} + +body.theme-dark #preview-pane { + background: #252526; +} + +body.theme-dark #preview { + color: #d4d4d4; +} + +body.theme-dark #preview h1, +body.theme-dark #preview h2 { + border-bottom-color: #3e3e42; +} + +body.theme-dark #preview code { + background-color: rgba(255,255,255,0.1); +} + +body.theme-dark #preview pre { + background-color: #2d2d30; +} + +body.theme-dark #preview blockquote { + color: #808080; + border-left-color: #3e3e42; +} + +body.theme-dark #preview a { + color: #569cd6; +} + +body.theme-dark #preview table th, +body.theme-dark #preview table td { + border-color: #3e3e42; +} + +body.theme-dark #preview table th { + background-color: #2d2d30; +} + +body.theme-dark #preview table tr:nth-child(2n) { + background-color: #2d2d30; +} + +body.theme-dark .status-bar { + background: #2d2d30; + border-top-color: #3e3e42; + color: #969696; +} + +/* Theme: Solarized */ +body.theme-solarized { + background: #fdf6e3; + color: #657b83; +} + +body.theme-solarized .toolbar { + background: #eee8d5; + border-bottom-color: #93a1a1; +} + +body.theme-solarized .toolbar button { + color: #586e75; +} + +body.theme-solarized .toolbar button:hover { + background: #fdf6e3; + border-color: #93a1a1; +} + +body.theme-solarized #editor { + background: #fdf6e3; + color: #657b83; +} + +body.theme-solarized #preview { + color: #586e75; +} + +body.theme-solarized #preview code { + background-color: #eee8d5; +} + +body.theme-solarized #preview pre { + background-color: #eee8d5; +} + +body.theme-solarized #preview a { + color: #268bd2; +} + +/* Theme: Monokai */ +body.theme-monokai { + background: #272822; + color: #f8f8f2; +} + +body.theme-monokai .toolbar { + background: #3e3d32; + border-bottom-color: #75715e; +} + +body.theme-monokai .toolbar button { + color: #f8f8f2; +} + +body.theme-monokai .toolbar button:hover { + background: #49483e; + border-color: #75715e; +} + +body.theme-monokai #editor { + background: #272822; + color: #f8f8f2; +} + +body.theme-monokai #preview-pane { + background: #272822; +} + +body.theme-monokai #preview { + color: #f8f8f2; +} + +body.theme-monokai #preview h1, +body.theme-monokai #preview h2 { + border-bottom-color: #75715e; +} + +body.theme-monokai #preview code { + background-color: #3e3d32; +} + +body.theme-monokai #preview pre { + background-color: #3e3d32; +} + +body.theme-monokai #preview blockquote { + color: #75715e; + border-left-color: #75715e; +} + +body.theme-monokai #preview a { + color: #66d9ef; +} + +body.theme-monokai .status-bar { + background: #3e3d32; + border-top-color: #75715e; + color: #75715e; +} + +/* Theme: GitHub */ +body.theme-github { + background: #fff; + color: #24292e; +} + +body.theme-github .toolbar { + background: #fafbfc; + border-bottom-color: #e1e4e8; +} + +body.theme-github .toolbar button { + color: #586069; +} + +body.theme-github .toolbar button:hover { + background: #f6f8fa; + border-color: #e1e4e8; +} + +body.theme-github #editor { + background: #fff; + color: #24292e; +} + +body.theme-github #preview { + color: #24292e; +} + +body.theme-github #preview h1, +body.theme-github #preview h2 { + border-bottom-color: #eaecef; +} + +body.theme-github #preview code { + background-color: rgba(27,31,35,0.05); +} + +body.theme-github #preview pre { + background-color: #f6f8fa; +} + +body.theme-github #preview blockquote { + color: #6a737d; + border-left-color: #dfe2e5; +} + +body.theme-github #preview a { + color: #0366d6; +} + +body.theme-github .status-bar { + background: #fafbfc; + border-top-color: #e1e4e8; + color: #586069; +} + +/* Hide preview pane by default */ +#preview-pane.hidden { + display: none; +} + +#editor-pane.full-width { + border-right: none; +} \ No newline at end of file