sdocs-dev 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/bin/sdocs-dev.js +81 -14
- package/package.json +1 -1
- package/public/css/layout.css +4 -1
- package/public/css/mobile.css +1 -1
- package/public/css/rendered.css +12 -1
- package/public/css/tokens.css +5 -2
- package/public/default.md +125 -96
- package/public/fonts/inter-400.woff2 +0 -0
- package/public/fonts/inter-500.woff2 +0 -0
- package/public/fonts/inter-600.woff2 +0 -0
- package/public/images/examples.png +0 -0
- package/public/index.html +20 -7
- package/public/sdocs-app.js +62 -12
- package/public/sdocs-controls.js +26 -39
- package/public/sdocs-export.js +2 -1
- package/public/sdocs-styles.js +85 -1
- package/public/sdocs-theme.js +7 -1
- package/public/sw.js +106 -0
- package/public/vendor/marked.min.js +6 -0
- package/server.js +65 -3
package/public/default.md
CHANGED
|
@@ -1,36 +1,85 @@
|
|
|
1
1
|
---
|
|
2
2
|
styles:
|
|
3
|
-
fontFamily:
|
|
3
|
+
fontFamily: Inter
|
|
4
4
|
baseFontSize: 16
|
|
5
5
|
lineHeight: 1.75
|
|
6
|
-
headers: { fontFamily: "inherit", scale: 1, marginBottom: 0.4 }
|
|
7
6
|
h1: { fontSize: 2.1, fontWeight: 700 }
|
|
8
7
|
h2: { fontSize: 1.55, fontWeight: 600 }
|
|
9
8
|
h3: { fontSize: 1.2, fontWeight: 600 }
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
p: { lineHeight: 1.75, marginBottom: 1 }
|
|
10
|
+
light:
|
|
11
|
+
background: "#ffffff"
|
|
12
|
+
color: "#1c1917"
|
|
13
|
+
dark:
|
|
14
|
+
background: "#2c2a26"
|
|
15
|
+
color: "#e7e5e2"
|
|
16
16
|
---
|
|
17
|
+
|
|
17
18
|
# Say hello to SmallDocs: A markdown-first replacement for Word & GDocs
|
|
18
19
|
|
|
19
20
|
If you're working with agents, a document written in markdown is <ins>officially</ins>* 407 times more useful than a document locked inside a `.docx` or `.gdoc` file format. Because of this, I believe Word and GDocs' days are numbered. (*I am the official.)
|
|
20
21
|
|
|
21
22
|
But while markdown is great for agents, it's a bit annoying for humans. Quickly and elegantly reading a `.md` file requires you to open your code editor and enter "preview" mode. Sharing a markdown file requires you to actually send the file to someone. They then have to download it and find the least annoying way to read it.
|
|
22
23
|
|
|
23
|
-
SmallDocs is an [open source](https://github.com/JoshInLisbon/SDocs) attempt at something different. It lets you (or your agent) easily, elegantly and privately **read**, **
|
|
24
|
+
SmallDocs is an [open source](https://github.com/JoshInLisbon/SDocs) attempt at something different. It lets you (or your agent) easily, elegantly and <ins>100% privately</ins> **read**, **format**, **share** and **export** `.md` files.
|
|
25
|
+
|
|
26
|
+
Reading a `.md` file in SmallDocs feels just like this (you're reading markdown right now). And by playing with the styles, it can feel like [this](https://sdocs.dev/#md=fVbNbttGEL7zKQb2IQAhEaJky4oOKdIEboq2QFoHNXLTcHcobkXuMrtDyWpRoA_RJ-yTFLMkHcm1e5N2vpn55psfaTqdJoGPNYV1AlA6y7fYmPq4hh-dxwSgwEC3zvKd-Z3WkK8SgNpY-kBmW_Ea8mx1nQBUhJp8jHEe5WONxxKNh_cmtDUeI6BBvzX2W8fsmjXMshghX8Mf0bXPNM9Wk_j1fsh0M5vBnwKcnwPzF4GLp8DFOXA5AFvBPS1q8oRlni0iuDZ2J3hNynlk4-waOqvJS4CIKGqndl86x_Q0fz6BwnlN_t5ortYwRpSkUZgC1W7rXWf1Gi4uSyyX5ewiWpSrnZfHxWK-zLF_7DV7NK2KHHO8iEFHnR6NOLuez_WjcXFmXBZX8_lyNLZntoWeU16OtrH8F5Iqp2PV54VQSSutLyZfvW7UFeY0ep0r1kv0boSqq9fXS7yYPBVnQZquTmMui-vV1aqPqdHvnpM0x3yZP5WUZvpaXT8raTnD2c1LkuornC3xBUkH2s9KqpSaFbMXJT1P-rykc5wv8tVp-eds_lfSG1xeX8_-I-l8kVO-PI2Jq9erVSQ6nU6T5BI-VQQ_d4YYfqG9qzuZf3ClrLmx8IkeOElunZftQE1hAlwRaKe6hizDAQMg7E3osAb0bEpUDKXxgQGtBgTlLKOx5CWo0YQBAilndQb3BKpygeJGBSiodJ7gML4enNcholD_1gUmPSzwKfTRhH4bKYUslsTO1RAqbElHxlwZuzN2myXJT-h32h0sGLsnz0FsIYPvGTDswhrSQ4UMpox-kQQobKiv6ps0SS4v4d4bNnYL94Yr1zHceqNEuSSR5AUFhsMAESYBtAnYtoQ-g7dgHVPh3A60o2BfxcRwdB2wk9qleASmpq2RSRwOlREP9PrRxdOXzngB1kaRDZQNLWN6YKiwLwuCMP_SYW34CP_89TcYhi1xAGHthhrxmCXJG7gQ7kGhN8K_cbHDJgDWBzwGEKFH5YVsYPScwduSyQNXyJOo8lbksuBsfZRMUBAz-ewiZr9jaiuy8IOx2yS5l49H13konW8wJmsfi5gA7ckf-1YWpFxDMmynumbwWbyZfGMsjl_byll6FaLMAbBtRcLN3jQbCBRCHPA9ebi7-wDIMMemn5m-1yii9hWjUoIvapqMT63zjKcPQj9kMhSXcC-D89l18B0amyRTSNOPEW9E_jSNEnwtUBLuQl_loSJP8mPm5Wt0_ZV85Cob5F09uG-2hkGbstxAqNwhEgB6QMX1EeLkqgrtlnSM8c7VNRbDz9oQoSG_JYla1kZxX7An1H1Z1jEoZAzsXVsZFcPcOtWFwd26KH2BHhpCKzLLdLPHYQPOlPiBqE0S-aSdjG3d77t0W7oYOwZbUSQ1lslKCKzTM8jb-GdEei7DCJvLzQSoaSsMJsjTJsXA5E3YhXQzGe5ObQIP-Omm729g3ynuPEFt9hRA2iCnQVphOFBdZkmy2WziVexD45g6-frSosetx7aCg-EK0rRwtU7TmDc1jLVRaQyaJdOTQD0hpiZ5c_IaD3pMKmdFWL4fbysGeI-MSfJW7l5_skpTkziO_YLiCFXXSBskfYs-0PjeoKqMpXgQY7p4aaAx1nlp_56sIatoOAuvhE7ZWY2SHeV6mpKHlRoPfhgWETQyxqUH3Mr7BILyppUPQqQ_enIHZMZ7obTxJFOa9Udyk2mnHja9DEWNageFe-g7tcka3VssuJYsyLXMkn8B&theme=light), [this](https://sdocs.dev/#md=jVbdbuPKDb7XUxDKRQDDEWzH3nh90WB_uqc5OH9oUgS9W1pDWVOPZnSGVBy1KNCH6BP2SQqOrDjJZoFzY1iajxzyI_lRFxcXGUvviDcZQBW8fMHGun4D-S-dtxLyDGCLTF-Cl1v7T9rA_CoDcNbTX8juatnAvFhnADWhocgb-NdLN7-FtrWe8ylwiU7tp9Bg3Fn_MYiEZgOzYgX_Vg_z0Xi4aFGspunx_njR1Ww2ABcvgfPi3feQl6-Ri5fAdyNw-Qr4NqxV1KvcX6czL-ZDPs76veINlSGi2OA3kHfeUFQPecKUwdB48wby4eXWhXL_exckHW1DNBTvrZF6A8vpi2xm400sCuUWS-t3SulyCtYb8inGxRGlIWcAWtFyv4uh82YD-VmF1aqq8nRSBheivlyY-XZJw8uhMk9HV-UlkhliHavxdLil-Xp1Orx8cTgz75fr9Xg4EvQdxyM3r4Kd0VWF-fRk9f7y8pJwtHqLvE8jtJytl1WZT1_7vKQyvX7yudoul1fHQA3G_Vu0Ld8tF6vFK9qMMbNq9iZt5XK7qr5HW_Ue12b5HdpWRHg6_Ia2l47fpm0xm68uV89TNOvtsqI_RNt6uyqrd9_QNi_ni8vZc5-4fr8uZ-rz4uIiy87gM7HdefgtWl_a1hFDFSLcheAY7moU-AlZsuyuJtgSC0g6wUj-XEBqguCJ4WClTk9NYIGKULpIXMBdTf15pBNQ1GVF5GBiPT1Ywa2jCfzvP_8FZ_cJ2EMZOmfUf40Pei15MClMMoC-hyA1RThgX2TZ2RncCkaB-zGEL0TO-l2WfaQqRII-dHCIVggQ2PqdoyQQEKpUiSmg5wNFkNryBupwAK41AODQUPAEE413Aoea_BBfx8lrTGRcZ9mfID_SaBl8EPhHxwIHTdUKuBD2PGSH3qTkh8cCTlZ6rRU4hLjnIk983Ao9EPwYtpxlvwSB_DBwN3A7BmkHlgaTxG4ZGmJwKBRTAdKN1u_0GuWn0u5IalfAnx8o9lLrKTkmhdimddSQlwGiDJ-NnDLchyj1MW61-hJill3AZPIp-Moa8iVNhnLmN7D34QD0iKW4fqBDOYaDdQ5MyJPdbUtkRpPUcAdkqJAllQQ93AA9tlQKmcHiMyWdHG1-racp73MGBG9LAgldWQ_Yu9jxE_LnHgwKapKMFUFNkfLUQX-lJjwQ_M2LdXAj8DES7jnLEj0j5WqHUAaWAm6G61If2dQVYELJU0BoMfVhDN1u6Mdjk8HvHbFSOhp0TPGcobHeDHU6AVibPz9W-ECAxiTqrocyW709NyHNk2UgjB6ssJa-dfRopb_Oh8Kp37s6EsEPKMRPQ4FPWXFtW55qHzXatS0yAzqnKRBtsmxewGRywwogZvJi0V1PJnCfgkNoCLUTqs6B75otRR0szU1T42RnK_01Ns00PVqW62xxcmu9dFbsA6nbT-ifBk_nTIfCSh06gUhotOeU6evs8uRA0w6sSqIePisvx1kahGnQi3FyphAimCOojIRCwC2VFh2UyMTXWXZTPWOoQquS53vYoVCi6oBWuIAfQlCZqOSAQ3-0KJa8DLr0gVhqElsyfIgEOsO_tlpgdFn2KZhkoUlBE5JGoh9Kq2ol5Au48UKxwpKS4gKrEp6wchTX0tlyT8cmerDcoQOHPcVjI50-b07dU4am6bwtxyH_-vVr9gEO5NxFFWKDImSU6E6VABj79PmZ3x4rU2JULd5qWbQFizxTc-0brb5PX6wGhB6fGd9pr6YhMhErKeBn7LdU5Ony7K5vwy5iW_fT8TNpmgQz7a5x47jusYv2-WpR5VSJF0pNS4lRihuYpNlIqUQeXHEYCj-It21octwgtW2n6tHDT5aF_LDw0jIz6HcUQ8fQ1shpbWw768wzRVVGDqomQyC19Xv9p9GZ4KmAvw8PPkiR7kpW3KBzulIfKLIWJ6k3Ndtg7Bhj-7SWVeTIwwGlrFPOLYXWEWApHTrXH4elyLL7QWipB7vzqVnIOR7WYDoKEJPeFXCCssRup6tx2OTfWLCuBVv1z22Q9-lj4Vt0Igg8PWo8yqQuymM3VtZbrsk87dNI52lrpi3kqEoe0Jijyv4xvKCu1_RJ8H8&theme=dark) or even [this](https://sdocs.dev/#md=pVbdbtvMEb3nUwysiwCqKFiSJdkC2iBxkiZA2y-wnaa903B3KG603GF2llKUtkAfok_YJyl2KSqy41xVd-L87NkzZw6Z53km4WBJVhlAyS68w9rYwwruufWK4B6dwCwDKFDoHbtwb77TCibzDMAaR-_JbKqwgsl4mQFUhJp86vW42x0XHBjuLRYpVqPfGPeaQ-B6BZfjq1g8WcE_UlV3xnQ8HaW_n49nLC8v4V8xcfo4cTKe_yJx9jRx8iRzccxsYuKT-4x-QnmTchVr6vuuoGbH0qCiFLPGbWNMk2KPwbBbgWPXBQvLavu15ZDKC_aa_GejQ7WCq2O1hBiK_YzbxCNn82MkokrUFai2G8-t0yu4GJTzclniRYootuzjwwlOZ7Np97Bj9RS6VFe4oIvUtGfyR918Tsv5KTh7HKRFOVd9sL_oj8azxQ1O-nDP0WOwdE1Uzi9GP6oWenqtb_qq5wi6PR1AOKebi9HTnrq8KvG85xXO54vrrqdGv32Otks1mU4vn9CmrvWC5s_SttR6Vqpf0LZY0nV58wvaFkTLYvlL2h43fp62yWJ6OZueXxGX10WJ_w9tk8kEp49ou76-ucHUM8_zLBvA69ZYbdwGEG7_9AEeKgxwR9KQCgIfgsAnIS9Z9go2zBoU1zU6ncctgsBsQSpurYaSyII1WwIExW5HXtJmwN6EChC2jveW9IawsBTxWMJNS_Dff_8n5isjNIKKbFO2dgToNDjakY8xTaLIRZBjeE-eXghUvIfAUETwwI7GWTYYwEdvnDKNJZis4A3Db47goYqX-0zWZtnbHfkDSFscb9Fjr3AXYYtxG0sj4GJnuBVoWt-w0Bg-lHDgFhS6FwEiGm8Kgn2kCntGQDMJGBfRgJAL5BSNwIQXApojhsAMdauqcZat1-sCpcoG8EdmvQJlCX1_XiaaFUiFnuDu7as3f347rjWcfgNQ3ByO8ai0SISypikYvT4Wq4pqhJ9_A2i8cQHS2wAazw35YEiSElCvAOvCbFpupWvUeFYkcoYjz7kNTRt-n47O89PJkOeO88LzXsjHG2bZQ0XwyZlv0FTGsnBTHcBIJPFLKwEcS0C7MZhEkJhKGvv0t6fjnK7gnpyYKJ03VGJrg4zgXWst3LILnm132HfynCt2pdm0nTEDfWvImziMftoFAX1TZC25MIbXbYB9RQ7aqHNwRFHkqecINmZHYEJkOFRUJy1zG0CRp5rd4WyUiS5PDfvwaFw97-nGe_ZbSbf1hBpq1jQC3V2om4k8bZTn3axOjRLCCGhPuH0J3JCL4PqRoiP7cw-P-3Mwe3ShK0rv_5dQkU8XNdJN7g_wqtvuEEXu6WtrPAk8pjYuIEXeytaCEcCzkgoFSjSWNGAAEyQKr0Z_gC9cpPEOIE7sOE24D6i2WfZ3bn1PiAC5aJVx9zxwY5xhJ2O4rZiFuoFosqYgj4HsYZVlkzEMh78lfULJvsYwHCa6411rlpC2ldOwQaEQ7I2TbBrL_kq-YDHhcCz52hoKUBx6OCNY5_kuJdG6k0wcBelsFsvvsaRTrSYJvlUhygdVJEt6EjsOIzTDLruKpR_JJ7BO0bG-RIl6QB_aZgQWvx_AMiabLtlDNMZDSMZGVujJssxWvYGna380DUW3jhYeHT7NyJpd51bogBTLQQLVY_gQZ63oGEwseq6BQ0U-FUpy5gZFSKAzgiiDlCBjeM-OPYTKSLdDqMLZiiiMOx9Ioib_CUmkz3qUBG1ctyzPFnXe92zR787sXVKLLMonxw25cDIveAbAANAdouHpVpE_np6WIWr1rrUkifyeT7g1wXwnJ5VpsiyH4VCCjuZgujyNAYfDUcRF3vdPaxLBDUkqePvNhPQ1IFBjCOSP419frlOytCq67yh-XObR29LTuFatp9ThLwzGBYpER601nusmyHDY6bOjJG1mY5qu4j4Js_Wkj_Prk9d5_kXYrWN-GrBOuopL-oASCB5IQpbdta5byKSjMo2boDReAgRT06h74TsG9Ju2JhdkDJ87S2gacvKy-8aoOcZOy5mcWwIGOtrjjtDKudKxiOTGF398iaYXsj_KPOFJ_v0iYRhn_wM&theme=light):
|
|
27
|
+
|
|
28
|
+

|
|
24
29
|
|
|
25
|
-
|
|
30
|
+
Use SmallDoc's CLI for speed. Creating a SmallDoc for a `.md` file (+ automatically opening your browser to read it) is as simple as:
|
|
26
31
|
|
|
27
32
|
```
|
|
28
|
-
# npm i sdocs-dev
|
|
33
|
+
# npm i -g sdocs-dev
|
|
29
34
|
sdoc README.md
|
|
30
35
|
```
|
|
31
36
|
|
|
32
37
|
## How SmallDocs work
|
|
33
38
|
|
|
39
|
+
### URLs
|
|
40
|
+
|
|
41
|
+
The URL format for SmallDocs is:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
https://sdocs.dev/#md={compressed & encoded .md}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Your entire document (content and styles) lives in the URL hash.
|
|
48
|
+
|
|
49
|
+
To keep URLs as short as possible, SmallDocs compresses your markdown using [deflate](https://en.wikipedia.org/wiki/Deflate) (a standard compression algorithm built into every browser) and then encodes the result with [base64url](https://en.wikipedia.org/wiki/Base64#URL_applications) (a URL-safe variant of base64 that avoids characters like `+`, `/`, and `=` which would otherwise need percent-encoding).
|
|
50
|
+
|
|
51
|
+
The `mode` parameter controls which view opens. Valid values are `read` (clean reading view, style panel hidden), `style` (style panel visible), and `raw` (raw markdown editor). When sharing a link for someone to read, use `mode=read`:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
https://sdocs.dev/#md=...&mode=read
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
You can also link directly to a section using the `sec` parameter. Click any heading's link icon to copy its section URL:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
https://sdocs.dev/#md=...&sec=url-formatting
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The `sec` value is the heading text slugified (lowercased, spaces become hyphens, special characters stripped). The page will scroll to that section on load.
|
|
64
|
+
|
|
65
|
+
The `theme` parameter forces a specific theme: `theme=light` or `theme=dark`. This overrides the reader's system preference, which is useful when sharing a link where the document looks best in a particular theme.
|
|
66
|
+
|
|
67
|
+
### Privacy
|
|
68
|
+
|
|
69
|
+
Because the SmallDocs url format is:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
https://sdocs.dev/#md={compressed & encoded .md}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Your document never hits the SDocs server.
|
|
76
|
+
|
|
77
|
+
This layer of privacy is built into how HTTP works. The hash fragment (everything after the `#` in a URL) is never sent to the server by the browser. It always stays entirely client-side:
|
|
78
|
+
|
|
79
|
+
> "The fragment is not sent to the server when the URI is requested; it is processed by the client" - [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Fragment)
|
|
80
|
+
|
|
81
|
+
The [sdocs.dev](https://sdocs.dev) site is purely a rendering space. JavaScript reads `window.location.hash`, decompresses and decodes the content, and renders your `.md` locally. The server is about 60 lines of Node.js that serves static files — no database, no logging, no analytics.
|
|
82
|
+
|
|
34
83
|
### Formatting
|
|
35
84
|
|
|
36
85
|
SDocs adds basic styling to markdown files. You write your content in regular markdown and the styles live in a metadata block at the top of the file.
|
|
@@ -53,78 +102,70 @@ styles:
|
|
|
53
102
|
baseFontSize: 17
|
|
54
103
|
h1: { fontSize: 2.3, fontWeight: 700 }
|
|
55
104
|
p: { lineHeight: 1.9, marginBottom: 1.2 }
|
|
56
|
-
|
|
57
|
-
background: "#fffaf5"
|
|
58
|
-
color: "#1a1a2e"
|
|
59
|
-
h1: { color: "#c0392b" }
|
|
60
|
-
dark:
|
|
61
|
-
background: "#1a1520"
|
|
62
|
-
color: "#e7e5e2"
|
|
63
|
-
h1: { color: "#ef6f5e" }
|
|
105
|
+
...
|
|
64
106
|
---
|
|
65
107
|
```
|
|
66
108
|
|
|
67
|
-
Non-color properties (fonts, sizes, spacing) are shared across themes and live at the top level. Colors live inside `light:` and `dark:` blocks so both themes render correctly.
|
|
68
|
-
|
|
69
|
-
All color controls are in the **Colors** section of the style panel. The light/dark toggle at the top of that section lets you customize each theme independently. Colors cascade from general to specific — set `color` once and it flows to headings, paragraphs, and lists unless you override them individually.
|
|
70
|
-
|
|
71
109
|
(Click "**Raw**" — top left — to see the front matter for this file. See all available properties [here](https://sdocs.dev) or by running `npm i sdocs-dev; sdoc schema`.)
|
|
72
110
|
|
|
73
111
|
When a `Styled .md` file is rendered in the SmallDocs interface the specified styles are applied. If a plain `.md` file is rendered the default styles are applied.
|
|
74
112
|
|
|
75
|
-
|
|
113
|
+
#### Light & dark modes
|
|
76
114
|
|
|
77
|
-
|
|
115
|
+
You can nest styles in `light` and `dark` keys:
|
|
78
116
|
|
|
79
117
|
```
|
|
80
|
-
|
|
118
|
+
light:
|
|
119
|
+
background: "#fffaf5"
|
|
120
|
+
color: "#1a1a2e"
|
|
121
|
+
h1: { color: "#c0392b" }
|
|
122
|
+
dark:
|
|
123
|
+
background: "#1a1520"
|
|
124
|
+
color: "#e7e5e2"
|
|
125
|
+
h1: { color: "#ef6f5e" }
|
|
81
126
|
```
|
|
82
127
|
|
|
83
|
-
|
|
128
|
+
These will be used when you view the site in each mode.
|
|
84
129
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
The `mode` parameter controls which view opens. Valid values are `read` (clean reading view, style panel hidden), `style` (style panel visible), and `raw` (raw markdown editor). When sharing a link for someone to read, use `mode=read`:
|
|
130
|
+
Non-color properties (fonts, sizes, spacing) are shared across themes and live at the top level.
|
|
88
131
|
|
|
89
|
-
|
|
90
|
-
https://sdocs.dev/#md=...&mode=read
|
|
91
|
-
```
|
|
132
|
+
All color controls are in the **Colors** section of the style panel. The light/dark toggle at the top of that section lets you customize each theme independently. Colors cascade from general to specific — set `color` once and it flows to headings, paragraphs, and lists unless you override them individually.
|
|
92
133
|
|
|
93
|
-
|
|
134
|
+
### Drag & drop
|
|
94
135
|
|
|
95
|
-
|
|
96
|
-
https://sdocs.dev/#md=...&sec=url-formatting
|
|
97
|
-
```
|
|
136
|
+
Drag any `.md` file onto the editor to SmallDoc it instantly.
|
|
98
137
|
|
|
99
|
-
|
|
138
|
+
### Exports
|
|
100
139
|
|
|
101
|
-
|
|
140
|
+
SmallDocs can export your document in four formats:
|
|
102
141
|
|
|
103
|
-
|
|
142
|
+
- **Raw .md** — your markdown content with all front matter stripped. Plain markdown, compatible with anything.
|
|
143
|
+
- **PDF** — a styled PDF generated from the rendered view via the browser's print engine.
|
|
144
|
+
- **Word (.docx)** — a styled Word document generated from the rendered HTML.
|
|
145
|
+
- **Styled .md** — your markdown with the `styles:` front matter block included. This is the format SmallDocs reads back in, so your formatting is preserved.
|
|
104
146
|
|
|
105
|
-
|
|
147
|
+
### Collapsed headers
|
|
106
148
|
|
|
107
|
-
|
|
149
|
+
SmallDocs loads with all headers collapsed. This is done because it makes it easy to get an overview of the whole document.
|
|
108
150
|
|
|
109
|
-
|
|
151
|
+
If you expand a parent, all of its children expand too.
|
|
110
152
|
|
|
111
|
-
|
|
153
|
+
### Copy & paste
|
|
112
154
|
|
|
113
|
-
|
|
155
|
+
Every header has its own copy and paste button. This copies its content and all of its children's content. At the moment this is the fastest way to get SmallDoc content into your agent's context, but we're looking for novel ideas to make this better.
|
|
114
156
|
|
|
115
|
-
|
|
157
|
+
### Works offline
|
|
116
158
|
|
|
117
|
-
|
|
159
|
+
`https://sdocs.dev` uses extensive client side caching. If you've loaded the site once, you can visit it even when you're offline. If something has changed server side, we invalidate the cache and the next time you visit the site you'll get the latest version.
|
|
118
160
|
|
|
119
|
-
|
|
161
|
+
### Auto-save
|
|
120
162
|
|
|
121
|
-
|
|
122
|
-
- **PDF** — a styled PDF generated from the rendered view via the browser's print engine.
|
|
123
|
-
- **Word (.docx)** — a styled Word document generated from the rendered HTML.
|
|
124
|
-
- **Styled .md** — your markdown with the `styles:` front matter block included. This is the format SmallDocs reads back in, so your formatting is preserved.
|
|
163
|
+
Because the URL includes your full document and dynamically updates via JavaScript, every change you make is instantly preserved in the URL. This works when you're offline.
|
|
125
164
|
|
|
126
165
|
## The CLI
|
|
127
166
|
|
|
167
|
+
### Installation
|
|
168
|
+
|
|
128
169
|
SmallDocs has a command-line tool that lets you open, share, and style markdown files from the terminal. Install it once:
|
|
129
170
|
|
|
130
171
|
```
|
|
@@ -141,86 +182,74 @@ sdoc README.md
|
|
|
141
182
|
|
|
142
183
|
Your browser opens with the document styled and readable. That's it — one command to go from `.md` file to formatted document.
|
|
143
184
|
|
|
144
|
-
### Modes
|
|
145
|
-
|
|
146
|
-
By default, files open in read mode. You can open in any mode:
|
|
147
|
-
|
|
148
|
-
```
|
|
149
|
-
sdoc README.md # read mode (default)
|
|
150
|
-
sdoc README.md --write # write mode (contentEditable editor)
|
|
151
|
-
sdoc README.md --style # style mode (styling panel visible)
|
|
152
|
-
sdoc README.md --raw # raw mode (plain markdown source)
|
|
153
|
-
```
|
|
154
|
-
|
|
155
185
|
### Share a link
|
|
156
186
|
|
|
157
187
|
```
|
|
158
188
|
sdoc share README.md
|
|
159
189
|
```
|
|
160
190
|
|
|
161
|
-
This
|
|
191
|
+
This copies a shareable link to your clipboard.
|
|
192
|
+
|
|
193
|
+
You can also combine it with options:
|
|
162
194
|
|
|
163
195
|
```
|
|
164
196
|
sdoc share report.md --section "Results" # deep-link to a heading
|
|
165
197
|
sdoc share notes.md --write # link opens in write mode
|
|
198
|
+
sdoc share notes.md --dark # link opens in dark theme
|
|
166
199
|
```
|
|
167
200
|
|
|
168
|
-
###
|
|
169
|
-
|
|
170
|
-
Any command that outputs markdown can be piped directly into SmallDocs:
|
|
201
|
+
### Start a new document
|
|
171
202
|
|
|
172
203
|
```
|
|
173
|
-
|
|
174
|
-
cat notes.md | sdoc share # get a shareable URL
|
|
175
|
-
your-agent --output md | sdoc # pipe agent output to browser
|
|
204
|
+
sdoc new
|
|
176
205
|
```
|
|
177
206
|
|
|
178
|
-
|
|
207
|
+
Opens a blank document in write mode, ready to type a `h1`.
|
|
208
|
+
|
|
209
|
+
### Style schema
|
|
179
210
|
|
|
180
211
|
```
|
|
181
|
-
sdoc
|
|
212
|
+
sdoc schema
|
|
182
213
|
```
|
|
183
214
|
|
|
184
|
-
|
|
215
|
+
Prints every available style property with its type, default value, and description. This is designed to be readable by both humans and LLMs — so your agent can write YAML front matter for you.
|
|
185
216
|
|
|
186
|
-
###
|
|
217
|
+
### Modes
|
|
187
218
|
|
|
188
|
-
|
|
219
|
+
By default, files open in read mode. You can open in any mode:
|
|
189
220
|
|
|
190
221
|
```
|
|
191
|
-
sdoc
|
|
192
|
-
sdoc
|
|
222
|
+
sdoc README.md # read mode (default)
|
|
223
|
+
sdoc README.md --write # write mode (contentEditable editor)
|
|
224
|
+
sdoc README.md --style # style mode (styling panel visible)
|
|
225
|
+
sdoc README.md --raw # raw mode (plain markdown source)
|
|
193
226
|
```
|
|
194
227
|
|
|
195
|
-
###
|
|
228
|
+
### Pipe from stdin
|
|
229
|
+
|
|
230
|
+
Any command that outputs markdown can be piped directly into SmallDocs:
|
|
196
231
|
|
|
197
232
|
```
|
|
198
|
-
sdoc
|
|
233
|
+
cat notes.md | sdoc # open in browser
|
|
234
|
+
cat notes.md | sdoc share # pipe to clipboard link
|
|
235
|
+
your-agent --output md | sdoc # pipe agent output to browser
|
|
199
236
|
```
|
|
200
237
|
|
|
201
|
-
|
|
238
|
+
### Default styles
|
|
239
|
+
|
|
240
|
+
If you find a style you like, use the "Save as Default" panel in the Style view to generate a command that saves your preferences to `~/.sdocs/styles.yaml`. The CLI automatically applies these defaults to every file you open — unless the file has its own styles, which always take priority.
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
sdoc defaults # view your current defaults
|
|
244
|
+
sdoc defaults --reset # remove them
|
|
245
|
+
```
|
|
202
246
|
|
|
203
247
|
### For agents
|
|
204
248
|
|
|
205
249
|
The CLI is designed to work well in automated workflows. A few patterns:
|
|
206
250
|
|
|
207
|
-
- **Generate a styled doc**: have your agent write a `.md` file with YAML front matter, then `sdoc share file.md` to
|
|
251
|
+
- **Generate a styled doc**: have your agent write a `.md` file with YAML front matter, then `sdoc share file.md` to copy a shareable link
|
|
208
252
|
- **Learn the format**: `sdoc schema` gives your agent everything it needs to know about available style properties
|
|
209
253
|
- **Deep-link to context**: `sdoc share file.md --section "Heading"` creates a URL that scrolls straight to the relevant section
|
|
210
254
|
- **No auth, no API keys**: everything is client-side — the URL *is* the document
|
|
211
255
|
|
|
212
|
-
### Small opinionated things
|
|
213
|
-
|
|
214
|
-
SmallDocs has opinions. We do some things which might not work for everyone but hopefully make the general `.md` experience better for most.
|
|
215
|
-
|
|
216
|
-
We welcome your opinions. Raise an issue on GitHub or make a pull request if you want something to change.
|
|
217
|
-
|
|
218
|
-
#### Collapsed headers
|
|
219
|
-
|
|
220
|
-
SmallDocs loads with all headers collapsed. This is done because it makes it easy to get an overview of the whole document.
|
|
221
|
-
|
|
222
|
-
If you expand a parent, all of its children expand too.
|
|
223
|
-
|
|
224
|
-
#### Copy & paste
|
|
225
|
-
|
|
226
|
-
Every header has its own copy and paste button. This copies its content and all of its children's content. At the moment this is the fastest way to get SmallDoc content into your agent's context, but we're looking for novel ideas to make this better.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/public/index.html
CHANGED
|
@@ -4,9 +4,6 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>SDocs</title>
|
|
7
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
|
|
10
7
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32' fill='none'%3E%3Crect x='2' y='2' width='28' height='28' rx='7' fill='%232563EB' fill-opacity='.15'/%3E%3Crect x='2' y='2' width='28' height='28' rx='7' stroke='%232563EB' stroke-width='2'/%3E%3Cpath d='M9 11h14M9 16h11M9 21h8' stroke='%232563EB' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E">
|
|
11
8
|
<link rel="stylesheet" href="/public/css/tokens.css">
|
|
12
9
|
<link rel="stylesheet" href="/public/css/layout.css">
|
|
@@ -37,12 +34,12 @@
|
|
|
37
34
|
<button class="btn active" id="btn-read" title="Read">
|
|
38
35
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 7v14"/><path d="M16 12h2"/><path d="M16 8h2"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/><path d="M6 12h2"/><path d="M6 8h2"/></svg>
|
|
39
36
|
</button>
|
|
40
|
-
<button class="btn" id="btn-write" title="Write">
|
|
41
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"/></svg>
|
|
42
|
-
</button>
|
|
43
37
|
<button class="btn" id="btn-style" title="Style">
|
|
44
38
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m14.622 17.897-10.68-2.913"/><path d="M18.376 2.622a1 1 0 1 1 3.002 3.002L17.36 9.643a.5.5 0 0 0 0 .707l.944.944a2.41 2.41 0 0 1 0 3.408l-.944.944a.5.5 0 0 1-.707 0L8.354 7.348a.5.5 0 0 1 0-.707l.944-.944a2.41 2.41 0 0 1 3.408 0l.944.944a.5.5 0 0 0 .707 0z"/><path d="M9 8c-1.804 2.71-3.97 3.46-6.583 3.948a.507.507 0 0 0-.302.819l7.32 8.883a1 1 0 0 0 1.185.204C12.735 20.405 16 16.792 16 15"/></svg>
|
|
45
39
|
</button>
|
|
40
|
+
<button class="btn" id="btn-write" title="Write">
|
|
41
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"/></svg>
|
|
42
|
+
</button>
|
|
46
43
|
<button class="btn" id="btn-raw" title="Raw">
|
|
47
44
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
|
|
48
45
|
</button>
|
|
@@ -245,6 +242,10 @@
|
|
|
245
242
|
<label>Border</label>
|
|
246
243
|
<div class="color-pair"><input type="color" id="ctrl-bq-border-color" value="#2563eb"><button class="reset-btn" id="reset-bq-border-color" title="Reset to default">↺</button></div>
|
|
247
244
|
</div>
|
|
245
|
+
<div class="control-row">
|
|
246
|
+
<label>Background</label>
|
|
247
|
+
<div class="color-pair"><input type="color" id="ctrl-bq-bg" value="#f7f5f2"><button class="reset-btn" id="reset-bq-bg" title="Reset to default">↺</button></div>
|
|
248
|
+
</div>
|
|
248
249
|
<div class="control-row">
|
|
249
250
|
<label>Text</label>
|
|
250
251
|
<div class="color-pair"><input type="color" id="ctrl-bq-color" value="#6b6560"><button class="reset-btn" id="reset-bq-color" title="Reset to default">↺</button></div>
|
|
@@ -573,7 +574,7 @@
|
|
|
573
574
|
</span>
|
|
574
575
|
</div>
|
|
575
576
|
|
|
576
|
-
<script src="
|
|
577
|
+
<script src="/public/vendor/marked.min.js"></script>
|
|
577
578
|
<script src="/public/sdocs-yaml.js"></script>
|
|
578
579
|
<script src="/public/sdocs-styles.js"></script>
|
|
579
580
|
<script src="/public/sdocs-state.js"></script>
|
|
@@ -582,5 +583,17 @@
|
|
|
582
583
|
<script src="/public/sdocs-export.js"></script>
|
|
583
584
|
<script src="/public/sdocs-write.js"></script>
|
|
584
585
|
<script src="/public/sdocs-app.js"></script>
|
|
586
|
+
<script>
|
|
587
|
+
var APP_VERSION = '__APP_VERSION__';
|
|
588
|
+
if ('serviceWorker' in navigator) {
|
|
589
|
+
navigator.serviceWorker.register('/sw.js').then(function (reg) {
|
|
590
|
+
var msg = { type: 'check-update', version: APP_VERSION };
|
|
591
|
+
if (reg.active) reg.active.postMessage(msg);
|
|
592
|
+
navigator.serviceWorker.addEventListener('controllerchange', function () {
|
|
593
|
+
if (navigator.serviceWorker.controller) navigator.serviceWorker.controller.postMessage(msg);
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
</script>
|
|
585
598
|
</body>
|
|
586
599
|
</html>
|
package/public/sdocs-app.js
CHANGED
|
@@ -263,7 +263,10 @@ function updateHash() {
|
|
|
263
263
|
}
|
|
264
264
|
var params = new URLSearchParams();
|
|
265
265
|
if (!S._isDefaultState) {
|
|
266
|
-
var
|
|
266
|
+
var styles = SDocStyles.stripStyleDefaults(S.collectStyles());
|
|
267
|
+
var meta = Object.assign({}, S.currentMeta);
|
|
268
|
+
if (Object.keys(styles).length > 0) meta.styles = styles;
|
|
269
|
+
else delete meta.styles;
|
|
267
270
|
var full = SDocYaml.serializeFrontMatter(meta) + '\n' + S.currentBody;
|
|
268
271
|
var compressed = await compressText(full);
|
|
269
272
|
params.set('md', compressed);
|
|
@@ -415,6 +418,10 @@ document.getElementById('factory-reset-styles').addEventListener('click', functi
|
|
|
415
418
|
|
|
416
419
|
document.getElementById('toolbar-brand').addEventListener('click', function(e) {
|
|
417
420
|
e.preventDefault();
|
|
421
|
+
if (window.location.hash && window.location.hash.indexOf('md=') !== -1) {
|
|
422
|
+
window.open(window.location.origin + window.location.pathname, '_blank');
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
418
425
|
S.resetAllStyles();
|
|
419
426
|
loadText(DEFAULT_MD);
|
|
420
427
|
S._isDefaultState = true;
|
|
@@ -476,19 +483,26 @@ S.loadText = loadText;
|
|
|
476
483
|
// Sync theme tabs to initial theme
|
|
477
484
|
S.updateThemeTabs(S.activeTheme);
|
|
478
485
|
|
|
479
|
-
// ──
|
|
486
|
+
// ── Load document from URL hash ──────────────────────────────────
|
|
480
487
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
startNewDocument();
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
488
|
+
var _lastLoadedHash = null;
|
|
489
|
+
|
|
490
|
+
async function loadFromHash() {
|
|
487
491
|
var hash = window.location.hash.slice(1);
|
|
492
|
+
if (hash === _lastLoadedHash) return;
|
|
493
|
+
_lastLoadedHash = hash;
|
|
494
|
+
|
|
495
|
+
clearTimeout(S._hashTimer);
|
|
496
|
+
|
|
488
497
|
var params = hash ? new URLSearchParams(hash) : new URLSearchParams();
|
|
489
498
|
var mdParam = params.get('md');
|
|
490
499
|
var modeParam = params.get('mode');
|
|
491
500
|
var stylesParam = params.get('styles');
|
|
501
|
+
var themeParam = params.get('theme');
|
|
502
|
+
var secParam = params.get('sec');
|
|
503
|
+
|
|
504
|
+
S.resetAllStyles();
|
|
505
|
+
|
|
492
506
|
if (mdParam) {
|
|
493
507
|
try {
|
|
494
508
|
S._isDefaultState = false;
|
|
@@ -498,12 +512,29 @@ S.updateThemeTabs(S.activeTheme);
|
|
|
498
512
|
console.warn('sdocs-dev: could not decode hash', e);
|
|
499
513
|
}
|
|
500
514
|
}
|
|
515
|
+
|
|
516
|
+
if (themeParam === 'light' || themeParam === 'dark') {
|
|
517
|
+
var savedPref = localStorage.getItem('sdocs-theme');
|
|
518
|
+
S.switchThemeAndUpdate(themeParam);
|
|
519
|
+
// Restore localStorage — URL theme is view-only, not persistent
|
|
520
|
+
if (savedPref) localStorage.setItem('sdocs-theme', savedPref);
|
|
521
|
+
else localStorage.removeItem('sdocs-theme');
|
|
522
|
+
} else {
|
|
523
|
+
// Restore user's actual preferred theme (previous hash may have overridden it)
|
|
524
|
+
var preferred = S.getPreferredTheme();
|
|
525
|
+
if (preferred !== S.activeTheme) {
|
|
526
|
+
S.switchThemeAndUpdate(preferred);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
501
530
|
if (modeParam && ['read', 'style', 'write', 'raw', 'export'].includes(modeParam)) {
|
|
502
531
|
setMode(modeParam, true);
|
|
503
532
|
} else {
|
|
504
533
|
setMode('read', true);
|
|
505
534
|
}
|
|
535
|
+
|
|
506
536
|
if (!mdParam) {
|
|
537
|
+
S._isDefaultState = true;
|
|
507
538
|
loadText(DEFAULT_MD);
|
|
508
539
|
if (stylesParam) {
|
|
509
540
|
try {
|
|
@@ -515,13 +546,11 @@ S.updateThemeTabs(S.activeTheme);
|
|
|
515
546
|
}
|
|
516
547
|
}
|
|
517
548
|
|
|
518
|
-
var secParam = params.get('sec');
|
|
519
549
|
if (secParam) {
|
|
520
550
|
setTimeout(function() {
|
|
521
551
|
var target = document.getElementById(secParam);
|
|
522
552
|
if (!target) return;
|
|
523
553
|
|
|
524
|
-
// Expand the target's own section body (its content)
|
|
525
554
|
var ownSection = target.closest('.md-section');
|
|
526
555
|
if (ownSection) {
|
|
527
556
|
var ownBody = ownSection.querySelector(':scope > .md-section-body');
|
|
@@ -530,7 +559,6 @@ S.updateThemeTabs(S.activeTheme);
|
|
|
530
559
|
if (ownToggle) { ownToggle.classList.add('open'); }
|
|
531
560
|
}
|
|
532
561
|
|
|
533
|
-
// Expand all ancestor section bodies
|
|
534
562
|
var el = target.closest('.md-section-body');
|
|
535
563
|
while (el) {
|
|
536
564
|
el.classList.add('open');
|
|
@@ -542,7 +570,6 @@ S.updateThemeTabs(S.activeTheme);
|
|
|
542
570
|
el = el.parentElement ? el.parentElement.closest('.md-section-body') : null;
|
|
543
571
|
}
|
|
544
572
|
|
|
545
|
-
// Add scroll spacer so the target can reach the top
|
|
546
573
|
var spacerNeeded = contentArea.clientHeight - (contentArea.scrollHeight - target.offsetTop);
|
|
547
574
|
if (spacerNeeded > 0) {
|
|
548
575
|
var spacer = document.createElement('div');
|
|
@@ -554,8 +581,31 @@ S.updateThemeTabs(S.activeTheme);
|
|
|
554
581
|
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
555
582
|
}, 200);
|
|
556
583
|
}
|
|
584
|
+
|
|
585
|
+
if (!secParam) {
|
|
586
|
+
contentArea.scrollTop = 0;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// ── Init ──────────────────────────────────
|
|
591
|
+
|
|
592
|
+
(async function () {
|
|
593
|
+
await _defaultReady;
|
|
594
|
+
if (window.location.pathname === '/new') {
|
|
595
|
+
startNewDocument();
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
await loadFromHash();
|
|
557
599
|
}());
|
|
558
600
|
|
|
601
|
+
window.addEventListener('hashchange', function () {
|
|
602
|
+
loadFromHash();
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
window.addEventListener('popstate', function () {
|
|
606
|
+
loadFromHash();
|
|
607
|
+
});
|
|
608
|
+
|
|
559
609
|
// ── Toolbar scroll hints (fade + bounce-peek) ──────
|
|
560
610
|
|
|
561
611
|
function initScrollHint(el) {
|