| … | … | @@ -498,3 +498,224 @@ if (msg.imageData) { |
| 498 | 498 | ```bash |
| 499 | 499 | tcpdump -i any -A 'tcp port 9000' |
| 500 | 500 | ``` |
| 501 | |
| 502 | ## Common Mistakes & Solutions |
| 503 | |
| 504 | **CRITICAL: Review this section before implementing image uploads in any client!** |
| 505 | |
| 506 | ### Mistake 1: Backend Doesn't Add Markdown Reference |
| 507 | |
| 508 | **Problem (2026-01-23):** |
| 509 | ```javascript |
| 510 | // ❌ WRONG - Backend receives imageData and creates hash, but doesn't add to message |
| 511 | const yaml = YAMLHelper.create(metadata, message) |
| 512 | ``` |
| 513 | |
| 514 | **Result:** |
| 515 | - YAML has `image: <hash>` in metadata (profile picture field) |
| 516 | - Message content is just the plain text |
| 517 | - No `` in content, so frontend can't render inline image |
| 518 | - Image displays as separate attachment OR not at all |
| 519 | |
| 520 | **Solution:** |
| 521 | ```javascript |
| 522 | // ✅ CORRECT - Add markdown reference to message before creating YAML |
| 523 | let finalMessage = message |
| 524 | if (imageHash) { |
| 525 | finalMessage = message + `\n` |
| 526 | } |
| 527 | const yaml = YAMLHelper.create(metadata, finalMessage) |
| 528 | ``` |
| 529 | |
| 530 | **Why:** The YAML `image:` field is for profile pictures (stored in db.get('image')). Inline images must be referenced in the message content using markdown syntax. |
| 531 | |
| 532 | **Files affected:** |
| 533 | - `pear-app/index.js` line 1177-1183 (POST /api/post handler) |
| 534 | |
| 535 | --- |
| 536 | |
| 537 | ### Mistake 2: Frontend Shows Raw Markdown Instead of Rendering |
| 538 | |
| 539 | **Problem (2026-01-23):** |
| 540 | ```javascript |
| 541 | // ❌ WRONG - Escape HTML and show markdown literally |
| 542 | <div class="message-text">${escapeHtml(msg.text)}</div> |
| 543 | |
| 544 | // Plus separate image rendering |
| 545 | if (msg.imageData) { |
| 546 | imageHtml = `<img src="${dataUrl}" />` |
| 547 | } |
| 548 | ``` |
| 549 | |
| 550 | **Result:** |
| 551 | ``` |
| 552 | User message text here |
| 553 |  ← Shows literally! |
| 554 | [IMAGE DISPLAYS HERE] ← Shows separately! |
| 555 | ``` |
| 556 | |
| 557 | **Solution:** |
| 558 | ```javascript |
| 559 | // ✅ CORRECT - Parse markdown syntax before rendering |
| 560 | function parseMarkdown(text, imageData) { |
| 561 | let html = escapeHtml(text) |
| 562 | |
| 563 | if (imageData) { |
| 564 | const dataUrl = imageData.startsWith('data:') |
| 565 | ? imageData |
| 566 | : `data:image/jpeg;base64,${imageData}` |
| 567 | |
| 568 | // Replace  with <img> tags |
| 569 | html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (match, alt, hash) => { |
| 570 | return `<img src="${dataUrl}" alt="${alt || 'Image'}" class="inline-image" />` |
| 571 | }) |
| 572 | } |
| 573 | |
| 574 | html = html.replace(/\n/g, '<br>') |
| 575 | return html |
| 576 | } |
| 577 | |
| 578 | // Use in timeline |
| 579 | <div class="message-text">${parseMarkdown(msg.text, msg.imageData)}</div> |
| 580 | ``` |
| 581 | |
| 582 | **Result:** |
| 583 | ``` |
| 584 | User message text here |
| 585 | [IMAGE DISPLAYS INLINE] ← Clean rendering! |
| 586 | ``` |
| 587 | |
| 588 | **Why:** Markdown is a formatting syntax that must be parsed into HTML. Showing `` literally is confusing and ugly. |
| 589 | |
| 590 | **Files affected:** |
| 591 | - `pear-app/public/app-simple.js` lines 1044-1072 (parseMarkdown function) |
| 592 | - `pear-app/public/app-simple.js` lines 100-115 (renderTimeline) |
| 593 | - `pear-app/public/style.css` lines 247-258 (inline-image styling) |
| 594 | |
| 595 | --- |
| 596 | |
| 597 | ### Mistake 3: Wiredove Canvas Not Drawn To |
| 598 | |
| 599 | **Problem (2026-01-23):** |
| 600 | ```javascript |
| 601 | // ❌ WRONG - Create canvas but never draw to it |
| 602 | const canvas = document.createElement("canvas") |
| 603 | const ctx = canvas.getContext("2d") |
| 604 | const img = new Image() |
| 605 | |
| 606 | img.onload = async () => { |
| 607 | const croppedImage = canvas.toDataURL() // Empty canvas! |
| 608 | const hash = await apds.make(img.src) // Hash original |
| 609 | // ... |
| 610 | } |
| 611 | ``` |
| 612 | |
| 613 | **Result:** |
| 614 | - `croppedImage` is an empty canvas (never drawn) |
| 615 | - `img.src` is hashed directly (might work, but inconsistent) |
| 616 | - Resize/compression code commented out but canvas still created (dead code) |
| 617 | |
| 618 | **Solution:** |
| 619 | ```javascript |
| 620 | // ✅ CORRECT - Draw to canvas before converting to data URL |
| 621 | img.onload = async () => { |
| 622 | // Set canvas to image size |
| 623 | canvas.width = img.width |
| 624 | canvas.height = img.height |
| 625 | |
| 626 | // Draw image to canvas |
| 627 | ctx.drawImage(img, 0, 0) |
| 628 | |
| 629 | // Get data URL from canvas (JPEG, 90% quality) |
| 630 | const dataUrl = canvas.toDataURL('image/jpeg', 0.9) |
| 631 | |
| 632 | // Hash the canvas output |
| 633 | const hash = await apds.make(dataUrl) |
| 634 | const mdimg = `` |
| 635 | } |
| 636 | ``` |
| 637 | |
| 638 | **Why:** |
| 639 | - Consistency: All systems should hash the same data URL format |
| 640 | - Compression: `canvas.toDataURL('image/jpeg', 0.9)` compresses images |
| 641 | - Future-proof: Easy to add resizing by changing canvas dimensions |
| 642 | |
| 643 | **Files affected:** |
| 644 | - `evbogue/wiredove/upload.js` lines 21-55 |
| 645 | |
| 646 | --- |
| 647 | |
| 648 | ### Checklist for Image Upload Implementation |
| 649 | |
| 650 | When implementing image uploads in ANY wirecore/wiredove client: |
| 651 | |
| 652 | **Backend (index.js, wiredove-cli):** |
| 653 | - [ ] Receive image as base64 from frontend |
| 654 | - [ ] Reconstruct data URL: `data:image/jpeg;base64,${base64}` |
| 655 | - [ ] Hash the data URL (not raw bytes): `ANProto.hash(dataUrl)` |
| 656 | - [ ] **ADD markdown to message:** `message + '\n'` |
| 657 | - [ ] Store data URL in feed entry: `entry.imageData = dataUrl` |
| 658 | - [ ] Create YAML with updated message (not original) |
| 659 | - [ ] Send to relay as separate WebSocket message |
| 660 | |
| 661 | **Frontend (app-simple.js, wiredove/upload.js):** |
| 662 | - [ ] Upload via file input: `<input type="file" accept="image/*">` |
| 663 | - [ ] Read as data URL: `FileReader.readAsDataURL(file)` |
| 664 | - [ ] Draw to canvas: `canvas.toDataURL('image/jpeg', 0.9)` |
| 665 | - [ ] Hash data URL: `apds.make(dataUrl)` or `ANProto.hash(dataUrl)` |
| 666 | - [ ] Send base64 part to backend (strip `data:image/jpeg;base64,` prefix) |
| 667 | - [ ] **Parse markdown when displaying:** Convert `` to `<img>` |
| 668 | - [ ] Style inline images: `.inline-image { max-width: 100%; }` |
| 669 | |
| 670 | **Testing:** |
| 671 | - [ ] Post with image from web UI |
| 672 | - [ ] Verify markdown in YAML: Should contain `` |
| 673 | - [ ] Check pub.wiredove.net: Image should display |
| 674 | - [ ] Check wirecore timeline: Image should display inline (not with markdown shown) |
| 675 | - [ ] Verify hash: `curl -I https://pub.wiredove.net/<hash>` → HTTP 200 |
| 676 | |
| 677 | --- |
| 678 | |
| 679 | ### Version History with Mistake Fixes |
| 680 | |
| 681 | - **v4257** (2026-01-22): Phase 3 complete - Original working version |
| 682 | - Handle both old (base64) and new (data URL) formats |
| 683 | |
| 684 | - **v4261** (2026-01-23): Fixed backend markdown reference |
| 685 | - Backend now adds `\n` to message |
| 686 | - YAML contains full content with inline image reference |
| 687 | - **BUG:** Frontend still showing raw markdown + image separately |
| 688 | |
| 689 | - **v4265** (2026-01-23): Fixed frontend markdown parsing ✅ |
| 690 | - Added `parseMarkdown()` function to convert `` to `<img>` |
| 691 | - Images now render inline without showing markdown syntax |
| 692 | - Clean display: text + image, no ugly `` visible |
| 693 | - **wiredove fixed too:** Canvas drawing implemented properly |
| 694 | |
| 695 | --- |
| 696 | |
| 697 | ## Implementation Reference |
| 698 | |
| 699 | When you (Claude) are asked to work on wirecore image uploads or debug image display issues, **ALWAYS review this "Common Mistakes" section first** to avoid repeating these errors. |
| 700 | |
| 701 | ### Quick Diagnostic Questions: |
| 702 | |
| 703 | 1. **Image not appearing?** |
| 704 | - Check: Did backend add `` to message? |
| 705 | - Check: Was image sent as separate WebSocket message? |
| 706 | - Check: Does hash match between YAML and blob? |
| 707 | |
| 708 | 2. **Markdown showing literally?** |
| 709 | - Check: Is frontend calling `parseMarkdown()`? |
| 710 | - Check: Is `escapeHtml()` being used without markdown parsing? |
| 711 | - Fix: Add markdown parser before rendering |
| 712 | |
| 713 | 3. **Hash mismatch / 404 errors?** |
| 714 | - Check: Are you hashing data URLs (not raw bytes)? |
| 715 | - Check: Is canvas being drawn to before `toDataURL()`? |
| 716 | - Fix: Hash what you send (data URL format) |
| 717 | |
| 718 | 4. **Images showing twice?** |
| 719 | - Check: Are you rendering both parsed markdown AND separate imageHtml? |
| 720 | - Fix: Only render parsed markdown (includes inline images) |
| 721 | |