@dmthepm/commune 0.1.0

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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +468 -0
  3. package/bin/commune.mjs +29 -0
  4. package/lib/cli/check.d.ts +13 -0
  5. package/lib/cli/check.js +58 -0
  6. package/lib/cli/errors.d.ts +29 -0
  7. package/lib/cli/errors.js +41 -0
  8. package/lib/cli/gate.d.ts +34 -0
  9. package/lib/cli/gate.js +165 -0
  10. package/lib/cli/main.d.ts +20 -0
  11. package/lib/cli/main.js +175 -0
  12. package/lib/cli/query.d.ts +30 -0
  13. package/lib/cli/query.js +103 -0
  14. package/lib/cli/related.d.ts +17 -0
  15. package/lib/cli/related.js +177 -0
  16. package/lib/cli/render.d.ts +20 -0
  17. package/lib/cli/render.js +32 -0
  18. package/lib/cli/root.d.ts +11 -0
  19. package/lib/cli/root.js +28 -0
  20. package/lib/cli/usage.d.ts +3 -0
  21. package/lib/cli/usage.js +46 -0
  22. package/lib/cli/version.d.ts +24 -0
  23. package/lib/cli/version.js +29 -0
  24. package/lib/integration.d.ts +24 -0
  25. package/lib/integration.js +111 -0
  26. package/lib/lib/graph.d.ts +354 -0
  27. package/lib/lib/graph.js +774 -0
  28. package/lib/markdown.d.ts +30 -0
  29. package/lib/markdown.js +24 -0
  30. package/lib/rehype-external-links.d.ts +15 -0
  31. package/lib/rehype-external-links.js +46 -0
  32. package/lib/remark-wikilinks.d.ts +25 -0
  33. package/lib/remark-wikilinks.js +108 -0
  34. package/package.json +101 -0
  35. package/src/components/Backlinks.astro +17 -0
  36. package/src/components/BacklinksScript.astro +117 -0
  37. package/src/components/Footer.astro +35 -0
  38. package/src/components/Header.astro +250 -0
  39. package/src/components/HeaderStarScript.astro +380 -0
  40. package/src/components/HomeFooterCards.astro +155 -0
  41. package/src/components/MarkdownLink.astro +36 -0
  42. package/src/components/PlausibleScript.astro +13 -0
  43. package/src/components/RelatedNotes.astro +77 -0
  44. package/src/components/SearchModal.astro +213 -0
  45. package/src/components/StarredLinksScript.astro +92 -0
  46. package/src/components/panes.ts +61 -0
  47. package/src/styles/design-system.css +157 -0
  48. package/src/styles/notes.css +85 -0
@@ -0,0 +1,250 @@
1
+ ---
2
+ ---
3
+ <header class="commune-header">
4
+ <div class="wrap">
5
+ <a href="/" class="brand-section">
6
+ <img src="/avatar.jpg" alt="Devon" class="avatar" />
7
+ <span class="brand brand-full">Devon Meadows</span>
8
+ <span class="brand brand-short">Devon</span>
9
+ </a>
10
+ <div class="controls">
11
+ <button id="theme-toggle" class="theme-btn" aria-label="Toggle theme">
12
+ <span class="sun">☀️</span>
13
+ <span class="moon">🌙</span>
14
+ </button>
15
+ <button class="search-btn" aria-label="Search (⌘K)" onclick="window.dispatchEvent(new CustomEvent('commune:openSearch'))">
16
+ <span class="search-icon">🔍</span>
17
+ <span class="search-text">Search</span><kbd>⌘K</kbd>
18
+ </button>
19
+ </div>
20
+ </div>
21
+ </header>
22
+
23
+ <style>
24
+ .commune-header{
25
+ position:sticky;
26
+ top:0;
27
+ background:var(--c-bg);
28
+ backdrop-filter:saturate(120%) blur(12px);
29
+ border-bottom:1px solid var(--c-border);
30
+ z-index:var(--z-header);
31
+ box-shadow:var(--c-shadow-sm);
32
+ }
33
+ .wrap{
34
+ display:flex;
35
+ align-items:center;
36
+ gap:1rem;
37
+ justify-content:space-between;
38
+ padding:1rem 1.25rem;
39
+ max-width:clamp(480px, 45vw, 700px);
40
+ margin:0 auto;
41
+ }
42
+ .brand-section{
43
+ display:flex;
44
+ align-items:center;
45
+ gap:.75rem;
46
+ text-decoration:none;
47
+ color:inherit;
48
+ }
49
+ .avatar{
50
+ width:2.5rem;
51
+ height:2.5rem;
52
+ border-radius:50%;
53
+ object-fit:cover;
54
+ border:2px solid var(--c-border);
55
+ transition:border-color 0.15s ease;
56
+ }
57
+ .avatar:hover{
58
+ border-color:var(--c-accent);
59
+ }
60
+ .controls{
61
+ display:flex;
62
+ align-items:center;
63
+ gap:.75rem;
64
+ }
65
+ .brand{
66
+ font-weight:700;
67
+ font-size:1.15rem;
68
+ letter-spacing:.3px;
69
+ color:var(--c-text);
70
+ cursor:pointer;
71
+ transition:color 0.15s ease;
72
+ }
73
+
74
+ /* Show full name on desktop, hide short name */
75
+ .brand-short {
76
+ display: none;
77
+ }
78
+
79
+ /* Tablet and below: remove max-width to match body full-width layout */
80
+ @media (max-width: 1023px) {
81
+ .wrap {
82
+ max-width: none;
83
+ padding:1rem 1.5rem;
84
+ }
85
+ }
86
+
87
+ /* Mobile: show short name, hide full name */
88
+ @media (max-width: 768px) {
89
+ .brand-full {
90
+ display: none;
91
+ }
92
+ .brand-short {
93
+ display: block;
94
+ font-size:1.1rem;
95
+ font-weight:600;
96
+ }
97
+ }
98
+ .brand:hover{
99
+ color:var(--c-accent);
100
+ }
101
+ nav{
102
+ display:flex;
103
+ gap:1.25rem;
104
+ }
105
+ nav a{
106
+ color:var(--c-text);
107
+ font-weight:500;
108
+ cursor:pointer;
109
+ transition:color 0.15s ease;
110
+ }
111
+ nav a:hover{
112
+ color:var(--c-accent);
113
+ }
114
+ .theme-btn{
115
+ display:flex;
116
+ align-items:center;
117
+ justify-content:center;
118
+ width:2.5rem;
119
+ height:2.5rem;
120
+ border:1.5px solid var(--c-border);
121
+ background:var(--c-bg-soft);
122
+ border-radius:var(--c-radius-md);
123
+ cursor:pointer;
124
+ transition:all 0.15s ease;
125
+ font-size:1.2rem;
126
+ position:relative;
127
+ }
128
+ .theme-btn:hover{
129
+ border-color:var(--c-accent);
130
+ transform:rotate(15deg);
131
+ }
132
+
133
+ /* Mobile: prevent rotate transform issues on touch */
134
+ @media (max-width: 768px) {
135
+ .theme-btn:hover {
136
+ transform:none;
137
+ }
138
+ .theme-btn:active {
139
+ transform:scale(0.95);
140
+ transition:transform 0.1s ease;
141
+ }
142
+ }
143
+ .theme-btn .sun,
144
+ .theme-btn .moon{
145
+ position:absolute;
146
+ transition:opacity 0.2s ease, transform 0.2s ease;
147
+ }
148
+ :root[data-theme="light"] .theme-btn .sun{
149
+ opacity:1;
150
+ transform:rotate(0deg);
151
+ }
152
+ :root[data-theme="light"] .theme-btn .moon{
153
+ opacity:0;
154
+ transform:rotate(180deg);
155
+ }
156
+ :root[data-theme="dark"] .theme-btn .sun{
157
+ opacity:0;
158
+ transform:rotate(180deg);
159
+ }
160
+ :root[data-theme="dark"] .theme-btn .moon{
161
+ opacity:1;
162
+ transform:rotate(0deg);
163
+ }
164
+ .search-btn{
165
+ display:flex;
166
+ align-items:center;
167
+ gap:.6rem;
168
+ border:1.5px solid var(--c-border);
169
+ background:var(--c-bg-soft);
170
+ padding:.5rem .8rem;
171
+ border-radius:var(--c-radius-md);
172
+ cursor:pointer;
173
+ transition:all 0.15s ease;
174
+ }
175
+ .search-btn:hover{
176
+ border-color:var(--c-accent);
177
+ background:var(--c-bg);
178
+ }
179
+ .search-btn span{
180
+ color:var(--c-text-muted);
181
+ font-size:.95rem;
182
+ }
183
+ /* Hide search emoji on desktop */
184
+ .search-btn .search-icon {
185
+ display:none;
186
+ }
187
+ kbd{
188
+ font-size:.75rem;
189
+ color:var(--c-text-muted);
190
+ border:1px solid var(--c-border);
191
+ background:var(--c-bg);
192
+ padding:.2rem .4rem;
193
+ border-radius:var(--c-radius-sm);
194
+ }
195
+
196
+ /* Mobile: hide Command-K text and search text, show only icon */
197
+ @media (max-width: 768px) {
198
+ .search-btn kbd,
199
+ .search-btn .search-text {
200
+ display:none;
201
+ }
202
+ .search-btn .search-icon {
203
+ display:block;
204
+ font-size:1.1rem;
205
+ }
206
+ .search-btn {
207
+ padding:.6rem;
208
+ min-width:2.5rem;
209
+ justify-content:center;
210
+ }
211
+ .avatar {
212
+ width:2.2rem;
213
+ height:2.2rem;
214
+ }
215
+ .controls {
216
+ gap:.5rem;
217
+ }
218
+ }
219
+ </style>
220
+
221
+ <script is:inline>
222
+ // Theme toggle
223
+ (function initTheme(){
224
+ const toggle = document.getElementById('theme-toggle');
225
+ const root = document.documentElement;
226
+
227
+ // Get saved theme or default to system preference
228
+ const savedTheme = localStorage.getItem('commune-theme');
229
+ const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
230
+ const initialTheme = savedTheme || (systemPrefersDark ? 'dark' : 'light');
231
+
232
+ root.setAttribute('data-theme', initialTheme);
233
+
234
+ toggle?.addEventListener('click', () => {
235
+ const currentTheme = root.getAttribute('data-theme');
236
+ const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
237
+ root.setAttribute('data-theme', newTheme);
238
+ localStorage.setItem('commune-theme', newTheme);
239
+ });
240
+ })();
241
+
242
+ // cmd/ctrl-k opens modal
243
+ addEventListener('keydown', (e) => {
244
+ const mac = navigator.platform.toUpperCase().includes('MAC');
245
+ if ((mac ? e.metaKey : e.ctrlKey) && e.key.toLowerCase() === 'k') {
246
+ e.preventDefault();
247
+ dispatchEvent(new CustomEvent('commune:openSearch'));
248
+ }
249
+ });
250
+ </script>
@@ -0,0 +1,380 @@
1
+ <script is:inline>
2
+ // === HEADER STAR INITIALIZATION ===
3
+ // Adds a ⭐ star to the h1 title of starred notes with a clickable modal
4
+
5
+ (function() {
6
+ let backlinksData = null;
7
+
8
+ // Show the star modal
9
+ function showStarModal() {
10
+ const modal = document.getElementById('star-modal');
11
+ if (modal) {
12
+ modal.style.display = 'flex';
13
+ // Prevent body scroll when modal is open
14
+ document.body.style.overflow = 'hidden';
15
+ }
16
+ }
17
+
18
+ // Hide the star modal
19
+ function hideStarModal() {
20
+ const modal = document.getElementById('star-modal');
21
+ if (modal) {
22
+ modal.style.display = 'none';
23
+ document.body.style.overflow = '';
24
+ }
25
+ }
26
+
27
+ // Create the modal HTML (only once)
28
+ function createStarModal() {
29
+ if (document.getElementById('star-modal')) return; // Already exists
30
+
31
+ const modal = document.createElement('div');
32
+ modal.id = 'star-modal';
33
+ modal.className = 'star-modal';
34
+ modal.innerHTML = `
35
+ <div class="star-modal-backdrop"></div>
36
+ <div class="star-modal-content">
37
+ <button class="star-modal-close" aria-label="Close">&times;</button>
38
+ <div class="star-modal-body">
39
+ <div class="star-modal-icon">⭐</div>
40
+ <h2>Top 5%</h2>
41
+ <p>Other notes link here often. Shows this idea anchors your wiki.</p>
42
+ <p><a href="/notes/the-star/" class="star-link">More about <span class="star-emoji">⭐</span></a></p>
43
+ </div>
44
+ </div>
45
+ `;
46
+
47
+ document.body.appendChild(modal);
48
+
49
+ // Close on modal/backdrop click (but not on content)
50
+ modal.addEventListener('click', hideStarModal);
51
+
52
+ // Prevent clicks on modal content from closing modal
53
+ modal.querySelector('.star-modal-content').addEventListener('click', (e) => {
54
+ e.stopPropagation();
55
+ });
56
+
57
+ // Close on X button click
58
+ modal.querySelector('.star-modal-close').addEventListener('click', (e) => {
59
+ e.stopPropagation(); // Prevent double-close
60
+ hideStarModal();
61
+ });
62
+
63
+ // Close on Escape key
64
+ document.addEventListener('keydown', (e) => {
65
+ if (e.key === 'Escape' && modal.style.display === 'flex') {
66
+ hideStarModal();
67
+ }
68
+ });
69
+ }
70
+
71
+ async function initializeHeaderStar(scope = document) {
72
+ try {
73
+ // Fetch backlinks data if not already loaded
74
+ if (!backlinksData) {
75
+ const res = await fetch('/backlinks.json');
76
+ if (res.ok) {
77
+ backlinksData = await res.json();
78
+ } else {
79
+ console.warn('Failed to load backlinks.json for header star');
80
+ return;
81
+ }
82
+ }
83
+
84
+ // Create modal (only once globally)
85
+ createStarModal();
86
+
87
+ // Find the current note's slug from the URL
88
+ const currentPath = window.location.pathname;
89
+ const normalizedPath = currentPath.endsWith('/') ? currentPath : currentPath + '/';
90
+
91
+ // Check if current note is starred
92
+ const note = backlinksData[normalizedPath];
93
+
94
+ if (note?.isStarred) {
95
+ // Find all h1 elements within the scope
96
+ const h1s = scope.querySelectorAll('.prose h1');
97
+
98
+ h1s.forEach(h1 => {
99
+ // Check if star isn't already there
100
+ if (!h1.querySelector('.star-indicator')) {
101
+ // Create wrapper to prevent line break between last word and star
102
+ const wrapper = document.createElement('span');
103
+ wrapper.style.whiteSpace = 'nowrap';
104
+
105
+ // Create star element (inline, clickable)
106
+ const star = document.createElement('span');
107
+ star.className = 'star-indicator';
108
+ star.innerHTML = ' ⭐';
109
+ star.setAttribute('aria-label', 'Top 5% most linked - click to learn more');
110
+ star.setAttribute('role', 'button');
111
+ star.setAttribute('tabindex', '0');
112
+
113
+ // Click handler to open modal
114
+ star.addEventListener('click', (e) => {
115
+ e.preventDefault();
116
+ showStarModal();
117
+ });
118
+
119
+ // Keyboard support
120
+ star.addEventListener('keydown', (e) => {
121
+ if (e.key === 'Enter' || e.key === ' ') {
122
+ e.preventDefault();
123
+ showStarModal();
124
+ }
125
+ });
126
+
127
+ // Get the h1's text content
128
+ const textContent = h1.textContent || '';
129
+ const words = textContent.trim().split(/\s+/);
130
+
131
+ // If there's more than one word, wrap the last word + star
132
+ if (words.length > 1) {
133
+ const lastWord = words.pop();
134
+ const restOfText = words.join(' ');
135
+
136
+ h1.textContent = restOfText + ' ';
137
+ wrapper.textContent = lastWord;
138
+ wrapper.appendChild(star);
139
+ h1.appendChild(wrapper);
140
+ } else {
141
+ // Just one word, append star directly with nowrap
142
+ wrapper.textContent = textContent;
143
+ wrapper.appendChild(star);
144
+ h1.textContent = '';
145
+ h1.appendChild(wrapper);
146
+ }
147
+ }
148
+ });
149
+ }
150
+ } catch (error) {
151
+ console.error('Error initializing header star:', error);
152
+ }
153
+ }
154
+
155
+ // Make functions globally available
156
+ window.initializeHeaderStar = initializeHeaderStar;
157
+ window.showStarModal = showStarModal;
158
+
159
+ // Auto-initialize on DOM ready
160
+ if (document.readyState === 'loading') {
161
+ document.addEventListener('DOMContentLoaded', () => initializeHeaderStar());
162
+ } else {
163
+ initializeHeaderStar();
164
+ }
165
+ })();
166
+ </script>
167
+
168
+ <style is:global>
169
+ /* Star indicator - inline with title (Skool-style) */
170
+ .star-indicator {
171
+ display: inline-block;
172
+ cursor: pointer;
173
+ font-size: 1em;
174
+ margin-left: 0.35em;
175
+ opacity: 0.9;
176
+ transition: opacity 0.2s ease, transform 0.2s ease;
177
+ user-select: none;
178
+ white-space: nowrap;
179
+ }
180
+
181
+ .star-indicator:hover {
182
+ opacity: 1;
183
+ transform: scale(1.1);
184
+ }
185
+
186
+ .star-indicator:focus {
187
+ outline: 2px solid var(--c-accent);
188
+ outline-offset: 4px;
189
+ border-radius: 4px;
190
+ }
191
+
192
+ /* Inline stars (in WikiLinks and backlinks) */
193
+ .star-indicator-inline {
194
+ display: inline-block;
195
+ cursor: pointer;
196
+ margin-left: 0.15em;
197
+ opacity: 0.9;
198
+ transition: opacity 0.2s ease, transform 0.2s ease;
199
+ user-select: none;
200
+ text-decoration: none !important;
201
+ border-bottom: none !important;
202
+ }
203
+
204
+ .star-indicator-inline:hover {
205
+ opacity: 1;
206
+ transform: scale(1.1);
207
+ }
208
+
209
+ .star-indicator-inline:focus {
210
+ outline: 2px solid var(--c-accent);
211
+ outline-offset: 4px;
212
+ border-radius: 4px;
213
+ }
214
+
215
+ /* Modal styles */
216
+ .star-modal {
217
+ display: none; /* Hidden by default, shown via JS */
218
+ position: fixed;
219
+ top: 0;
220
+ left: 0;
221
+ right: 0;
222
+ bottom: 0;
223
+ z-index: 9999;
224
+ align-items: center;
225
+ justify-content: center;
226
+ animation: fadeIn 0.2s ease;
227
+ }
228
+
229
+ @keyframes fadeIn {
230
+ from { opacity: 0; }
231
+ to { opacity: 1; }
232
+ }
233
+
234
+ .star-modal-backdrop {
235
+ position: absolute;
236
+ top: 0;
237
+ left: 0;
238
+ right: 0;
239
+ bottom: 0;
240
+ background: rgba(0, 0, 0, 0.75);
241
+ backdrop-filter: blur(4px);
242
+ }
243
+
244
+ .star-modal-content {
245
+ position: relative;
246
+ background: var(--c-bg);
247
+ border-radius: var(--c-radius-lg);
248
+ max-width: 540px;
249
+ width: 90%;
250
+ max-height: 85vh;
251
+ overflow-y: auto;
252
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
253
+ animation: slideUp 0.3s cubic-bezier(0.22, 1, 0.36, 1);
254
+ border: 1px solid var(--c-border);
255
+ }
256
+
257
+ @keyframes slideUp {
258
+ from {
259
+ transform: translateY(20px);
260
+ opacity: 0;
261
+ }
262
+ to {
263
+ transform: translateY(0);
264
+ opacity: 1;
265
+ }
266
+ }
267
+
268
+ .star-modal-close {
269
+ position: absolute;
270
+ top: 1rem;
271
+ right: 1rem;
272
+ width: 32px;
273
+ height: 32px;
274
+ background: var(--c-bg-soft);
275
+ border: 1px solid var(--c-border);
276
+ border-radius: 50%;
277
+ display: flex;
278
+ align-items: center;
279
+ justify-content: center;
280
+ cursor: pointer;
281
+ font-size: 1.5rem;
282
+ line-height: 1;
283
+ color: var(--c-text-muted);
284
+ transition: all 0.2s ease;
285
+ padding: 0;
286
+ }
287
+
288
+ .star-modal-close:hover {
289
+ background: var(--c-bg-muted);
290
+ color: var(--c-text);
291
+ transform: rotate(90deg);
292
+ }
293
+
294
+ .star-modal-body {
295
+ padding: 2.5rem 2rem 2rem;
296
+ text-align: center;
297
+ }
298
+
299
+ .star-modal-icon {
300
+ font-size: 3.5rem;
301
+ margin-bottom: 0.75rem;
302
+ animation: starPulse 2s ease-in-out infinite;
303
+ }
304
+
305
+ @keyframes starPulse {
306
+ 0%, 100% { transform: scale(1); }
307
+ 50% { transform: scale(1.1); }
308
+ }
309
+
310
+ .star-modal-body h2 {
311
+ font-size: 1.5rem;
312
+ font-weight: 700;
313
+ margin-bottom: 0.75rem;
314
+ color: var(--c-text);
315
+ }
316
+
317
+ .star-modal-body p {
318
+ font-size: 1rem;
319
+ line-height: 1.5;
320
+ color: var(--c-text-accent);
321
+ margin-bottom: 0.75rem;
322
+ }
323
+
324
+ .star-modal-body p:last-child {
325
+ margin-bottom: 0;
326
+ }
327
+
328
+ .star-modal-body strong {
329
+ color: var(--c-accent);
330
+ font-weight: 600;
331
+ }
332
+
333
+ .star-modal-body em {
334
+ font-style: italic;
335
+ color: var(--c-text);
336
+ }
337
+
338
+ .star-link {
339
+ color: var(--c-accent);
340
+ text-decoration: underline;
341
+ text-decoration-thickness: 1px;
342
+ text-underline-offset: 2px;
343
+ transition: opacity 0.2s ease;
344
+ }
345
+
346
+ .star-link:hover {
347
+ opacity: 0.8;
348
+ }
349
+
350
+ .star-link .star-emoji {
351
+ text-decoration: none;
352
+ }
353
+
354
+ /* Mobile adjustments */
355
+ @media (max-width: 1023px) {
356
+ .star-modal-content {
357
+ width: 95%;
358
+ max-height: 90vh;
359
+ }
360
+
361
+ .star-modal-body {
362
+ padding: 2rem 1.5rem 1.5rem;
363
+ }
364
+
365
+ .star-modal-body h2 {
366
+ font-size: 1.35rem;
367
+ margin-bottom: 0.5rem;
368
+ }
369
+
370
+ .star-modal-icon {
371
+ font-size: 2.5rem;
372
+ margin-bottom: 0.5rem;
373
+ }
374
+
375
+ .star-modal-body p {
376
+ font-size: 0.95rem;
377
+ margin-bottom: 0.5rem;
378
+ }
379
+ }
380
+ </style>