czon 0.9.6 → 0.9.8

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.
@@ -87,6 +87,163 @@ const ContentPage = props => {
87
87
  react_1.default.createElement("footer", { className: "footer" },
88
88
  react_1.default.createElement(LanguageSwitcher_1.LanguageSwitcher, { ctx: props.ctx, lang: props.lang, file: props.file }),
89
89
  react_1.default.createElement(CZONFooter_1.CZONFooter, null))) }),
90
+ react_1.default.createElement("button", { className: "share-float-btn", id: "share-float-btn" }, "Share"),
91
+ react_1.default.createElement("div", { className: "share-modal-overlay", id: "share-modal-overlay" },
92
+ react_1.default.createElement("div", { className: "share-modal" },
93
+ react_1.default.createElement("img", { className: "share-preview", id: "share-preview", alt: "Share preview", src: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" }),
94
+ react_1.default.createElement("div", { className: "share-modal-actions" },
95
+ react_1.default.createElement("button", { className: "share-download-btn", id: "share-download-btn" }, "Save Image"),
96
+ react_1.default.createElement("button", { className: "share-close-btn", id: "share-close-btn" }, "Close")))),
97
+ react_1.default.createElement("div", { className: "share-card", id: "share-card" },
98
+ react_1.default.createElement("div", { className: "share-card-header" },
99
+ react_1.default.createElement("div", { className: "share-card-header-left" },
100
+ react_1.default.createElement("div", { className: "share-card-site", id: "share-card-site" }),
101
+ react_1.default.createElement("div", { className: "share-card-title", id: "share-card-title" })),
102
+ react_1.default.createElement("div", { className: "share-card-qr" },
103
+ react_1.default.createElement("canvas", { id: "share-qr-canvas", width: "64", height: "64" }),
104
+ react_1.default.createElement("span", { className: "share-card-qr-hint" }, "Scan to read"))),
105
+ react_1.default.createElement("div", { className: "share-card-divider" }),
106
+ react_1.default.createElement("div", { className: "share-card-body", id: "share-card-body" })),
107
+ react_1.default.createElement("script", { id: "qrcode-lib", src: "https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js", defer: true }),
108
+ react_1.default.createElement("script", { id: "html2canvas-lib", src: "https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js", defer: true }),
109
+ react_1.default.createElement("script", { dangerouslySetInnerHTML: {
110
+ __html: `
111
+ (function() {
112
+ var floatBtn = document.getElementById('share-float-btn');
113
+ var overlay = document.getElementById('share-modal-overlay');
114
+ var preview = document.getElementById('share-preview');
115
+ var downloadBtn = document.getElementById('share-download-btn');
116
+ var closeBtn = document.getElementById('share-close-btn');
117
+ var shareCard = document.getElementById('share-card');
118
+ var cardSite = document.getElementById('share-card-site');
119
+ var cardTitle = document.getElementById('share-card-title');
120
+ var cardBody = document.getElementById('share-card-body');
121
+ var qrCanvas = document.getElementById('share-qr-canvas');
122
+ var savedRange = null;
123
+ var articleTitle = ${JSON.stringify(title)};
124
+ var siteName = ${JSON.stringify(props.ctx.site.options?.site?.title || 'CZON')};
125
+
126
+ // Detect text selection within .content-body
127
+ document.addEventListener('selectionchange', function() {
128
+ var sel = window.getSelection();
129
+ if (!sel || sel.isCollapsed || !sel.rangeCount) {
130
+ floatBtn.style.display = 'none';
131
+ return;
132
+ }
133
+ var range = sel.getRangeAt(0);
134
+ var container = document.querySelector('.content-body');
135
+ if (!container || !container.contains(range.commonAncestorContainer)) {
136
+ floatBtn.style.display = 'none';
137
+ return;
138
+ }
139
+ var text = sel.toString().trim();
140
+ if (!text) {
141
+ floatBtn.style.display = 'none';
142
+ return;
143
+ }
144
+ savedRange = range.cloneRange();
145
+ var rect = range.getBoundingClientRect();
146
+ floatBtn.style.display = 'block';
147
+ floatBtn.style.top = (window.scrollY + rect.bottom + 6) + 'px';
148
+ floatBtn.style.left = (window.scrollX + rect.left + rect.width / 2 - 30) + 'px';
149
+ });
150
+
151
+ floatBtn.addEventListener('click', function(e) {
152
+ e.preventDefault();
153
+ e.stopPropagation();
154
+ if (!savedRange) return;
155
+ renderShareCard(savedRange);
156
+ floatBtn.style.display = 'none';
157
+ });
158
+
159
+ closeBtn.addEventListener('click', function() {
160
+ overlay.classList.remove('active');
161
+ });
162
+ overlay.addEventListener('click', function(e) {
163
+ if (e.target === overlay) overlay.classList.remove('active');
164
+ });
165
+
166
+ downloadBtn.addEventListener('click', function() {
167
+ var a = document.createElement('a');
168
+ a.href = preview.src;
169
+ a.download = 'share.png';
170
+ a.click();
171
+ });
172
+
173
+ function renderQR() {
174
+ if (typeof qrcode === 'undefined') return;
175
+ var size = 64;
176
+ var qr = qrcode(0, 'M');
177
+ qr.addData(window.location.href);
178
+ qr.make();
179
+ var moduleCount = qr.getModuleCount();
180
+ var cellSize = size / moduleCount;
181
+ var ctx = qrCanvas.getContext('2d');
182
+ ctx.clearRect(0, 0, size, size);
183
+ ctx.fillStyle = '#1a1a1a';
184
+ for (var r = 0; r < moduleCount; r++) {
185
+ for (var c = 0; c < moduleCount; c++) {
186
+ if (qr.isDark(r, c)) {
187
+ ctx.fillRect(c * cellSize, r * cellSize, cellSize + 0.5, cellSize + 0.5);
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ function renderShareCard(range) {
194
+ // Populate card content
195
+ cardSite.textContent = siteName;
196
+ cardTitle.textContent = articleTitle;
197
+
198
+ // Clone selected DOM fragment with rich formatting
199
+ var fragment = range.cloneContents();
200
+ cardBody.innerHTML = '';
201
+ cardBody.appendChild(fragment);
202
+
203
+ // Copy computed styles for elements that need them (e.g. KaTeX)
204
+ var allStyles = document.querySelectorAll('style, link[rel="stylesheet"]');
205
+ var styleClones = [];
206
+ allStyles.forEach(function(s) {
207
+ styleClones.push(s.cloneNode(true));
208
+ });
209
+
210
+ // Render QR code
211
+ renderQR();
212
+
213
+ // Show card for html2canvas to capture
214
+ shareCard.classList.add('rendering');
215
+
216
+ // Wait a frame for layout
217
+ requestAnimationFrame(function() {
218
+ // Enforce minimum 3:4 aspect ratio
219
+ var cardW = shareCard.offsetWidth;
220
+ var cardH = shareCard.offsetHeight;
221
+ var minH = Math.round(cardW * 4 / 3);
222
+ if (cardH < minH) {
223
+ shareCard.style.minHeight = minH + 'px';
224
+ }
225
+
226
+ html2canvas(shareCard, {
227
+ scale: 2,
228
+ useCORS: true,
229
+ backgroundColor: '#ffffff',
230
+ logging: false,
231
+ }).then(function(canvas) {
232
+ shareCard.classList.remove('rendering');
233
+ shareCard.style.minHeight = '';
234
+
235
+ preview.src = canvas.toDataURL('image/png');
236
+ overlay.classList.add('active');
237
+ }).catch(function(err) {
238
+ console.error('Share card render error:', err);
239
+ shareCard.classList.remove('rendering');
240
+ shareCard.style.minHeight = '';
241
+ });
242
+ });
243
+ }
244
+ })();
245
+ `,
246
+ } }),
90
247
  react_1.default.createElement("script", { id: "hljs-lib", src: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js", defer: true }),
91
248
  react_1.default.createElement("script", null, `
92
249
  document.getElementById('hljs-lib').addEventListener('load', () => {
@@ -185,7 +342,7 @@ const ContentPage = props => {
185
342
 
186
343
  function renderEmblaCarousels() {
187
344
  // Detect image groups, make them carousels automatically
188
- Map.groupBy(document.querySelectorAll('img'), x => x.parentNode).entries().forEach(([container, images]) => {
345
+ Map.groupBy(document.querySelectorAll('.content-body img'), x => x.parentNode).entries().forEach(([container, images]) => {
189
346
  const outer = document.createElement('div');
190
347
  outer.classList.add('embla');
191
348
  container.appendChild(outer);
package/dist/ssg/style.js CHANGED
@@ -378,5 +378,161 @@ html:not(.dark) body {
378
378
  flex: 0 0 100%;
379
379
  min-width: 0;
380
380
  }
381
+
382
+ /* Share feature */
383
+ .share-float-btn {
384
+ display: none;
385
+ position: absolute;
386
+ z-index: 1000;
387
+ background: var(--primary-color);
388
+ color: #fff;
389
+ border: none;
390
+ border-radius: 6px;
391
+ padding: 4px 12px;
392
+ font-size: 14px;
393
+ cursor: pointer;
394
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
395
+ white-space: nowrap;
396
+ line-height: 1.6;
397
+ }
398
+ .share-float-btn:hover {
399
+ opacity: 0.9;
400
+ }
401
+
402
+ .share-modal-overlay {
403
+ display: none;
404
+ position: fixed;
405
+ inset: 0;
406
+ z-index: 2000;
407
+ background: rgba(0,0,0,0.5);
408
+ justify-content: center;
409
+ align-items: center;
410
+ }
411
+ .share-modal-overlay.active {
412
+ display: flex;
413
+ }
414
+ .share-modal {
415
+ background: var(--bg-primary);
416
+ border-radius: 12px;
417
+ padding: 20px;
418
+ max-width: 90vw;
419
+ max-height: 90vh;
420
+ overflow: auto;
421
+ box-shadow: 0 8px 32px rgba(0,0,0,0.3);
422
+ display: flex;
423
+ flex-direction: column;
424
+ align-items: center;
425
+ gap: 16px;
426
+ }
427
+ .share-modal img.share-preview {
428
+ max-width: 100%;
429
+ height: auto;
430
+ border-radius: 8px;
431
+ box-shadow: 0 2px 12px rgba(0,0,0,0.1);
432
+ }
433
+ .share-modal-actions {
434
+ display: flex;
435
+ gap: 12px;
436
+ }
437
+ .share-modal-actions button {
438
+ padding: 8px 24px;
439
+ border-radius: 6px;
440
+ border: none;
441
+ font-size: 14px;
442
+ cursor: pointer;
443
+ }
444
+ .share-modal-actions .share-download-btn {
445
+ background: var(--primary-color);
446
+ color: #fff;
447
+ }
448
+ .share-modal-actions .share-close-btn {
449
+ background: var(--tag-bg);
450
+ color: var(--tag-text);
451
+ }
452
+
453
+ /* Share card (off-screen DOM for html2canvas capture) */
454
+ .share-card {
455
+ display: none;
456
+ position: fixed;
457
+ left: -9999px;
458
+ top: 0;
459
+ width: 540px;
460
+ min-height: 720px;
461
+ background: #ffffff;
462
+ border-radius: 12px;
463
+ padding: 36px;
464
+ box-sizing: border-box;
465
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
466
+ color: #1a1a1a;
467
+ flex-direction: column;
468
+ pointer-events: none;
469
+ }
470
+ .share-card.rendering {
471
+ display: flex;
472
+ }
473
+ .share-card-header {
474
+ display: flex;
475
+ justify-content: space-between;
476
+ align-items: flex-start;
477
+ gap: 12px;
478
+ margin-bottom: 20px;
479
+ }
480
+ .share-card-header-left {
481
+ flex: 1;
482
+ min-width: 0;
483
+ }
484
+ .share-card-site {
485
+ font-size: 14px;
486
+ color: #999999;
487
+ margin-bottom: 8px;
488
+ }
489
+ .share-card-title {
490
+ font-size: 24px;
491
+ font-weight: 700;
492
+ color: #1a1a1a;
493
+ line-height: 1.3;
494
+ word-wrap: break-word;
495
+ }
496
+ .share-card-qr {
497
+ flex-shrink: 0;
498
+ display: flex;
499
+ flex-direction: column;
500
+ align-items: center;
501
+ }
502
+ .share-card-qr canvas {
503
+ display: block;
504
+ }
505
+ .share-card-qr-hint {
506
+ font-size: 10px;
507
+ color: #bbbbbb;
508
+ margin-top: 4px;
509
+ white-space: nowrap;
510
+ }
511
+ .share-card-divider {
512
+ height: 1px;
513
+ background: #e5e5e5;
514
+ margin-bottom: 20px;
515
+ }
516
+ .share-card-body {
517
+ flex: 1;
518
+ background: #f8f9fa;
519
+ border-radius: 8px;
520
+ padding: 20px;
521
+ font-size: 18px;
522
+ line-height: 1.8;
523
+ color: #333333;
524
+ overflow: hidden;
525
+ }
526
+ .share-card-body img {
527
+ max-width: 100%;
528
+ height: auto;
529
+ }
530
+ .share-card-body pre {
531
+ white-space: pre-wrap;
532
+ word-wrap: break-word;
533
+ }
534
+ .share-card-body strong {
535
+ color: #ff5722;
536
+ }
381
537
  `;
382
538
  //# sourceMappingURL=style.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "czon",
3
- "version": "0.9.6",
3
+ "version": "0.9.8",
4
4
  "description": "CZON - AI enhanced Markdown content engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",