Spaces:
Runtime error
Runtime error
Commit History
-
HF: use Space frontmatter README
github-actions[bot]afca2b4 -
Docs: HF Spaces deploy is live — add demo URL, mark TODO done
Hosted demo live at https://huggingface.co/spaces/harshith99/ig-v1 (auto-syncs from master via sync-hf-space.yml). Add the "Try it now" link to the README and close the HF Spaces deploy TODO. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>2509ce2 -
Docs: SHORTCUT.md build guide (Phase C) — completes incremental capture
Adds the ~8-action iOS Shortcut recipe (fetch /embed/captioned/ on-device → POST caption to /capture with the user's token) plus setup + privacy notes. Shipped as a build-it-yourself guide rather than a binary .shortcut (can't be authored off-iOS and goes stale per-device). Linked from the #capture-setup card. Phase C tests already live in tests/test_capture.py. Completes hybrid step 3 (incremental capture). Phases A/B/C all done. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>221abde -
Feat: capture inbox (Phase B) — phone captures merge into the library
/capture with a token enqueues place(s) into an ephemeral, TTL'd in-memory inbox. New POST /capture-inbox/{token} drains a token's captures and merges them into the caller's library (reuses the living-library _merge_rows) → returns a fresh job like /import. Client _pullCaptureInbox() drains on load and shows "📲 N places captured from your phone" with ✨ New badges; a #capture-setup card in the import card surfaces the token (localStorage). Fixes an empty-multipart-body 400 by always sending merge_into (even ""). tests/test_capture.py extended (enqueue, drain+merge, idempotency, status preserved); visual review of the setup card + full round-trip at mobile/desktop. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>eda6c91 -
Feat: /capture endpoint core (Phase A) — single-post extraction
Refactors _run_url_pipeline's extract core into shared _places_from_caption() (analyze_batch → split_multi_venue → location-tag coords). New POST /capture accepts embed_html (the iOS Shortcut fetches /embed/captioned/ on-device so the server never touches Instagram), or a ready caption, or a url (local fetch), plus optional lat/lng for a free location-tag pin. Returns {places, count}. BYOK api_key. tests/test_capture.py added to CI; e2e still green (refactor safe). Inbox delivery (Phase B) and the Shortcut itself (Phase C) are next. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>8221e29 -
Docs: scope /capture endpoint (iOS Shortcut incremental capture)
Adds the step-3 hybrid item: Shortcut fetches /embed/captioned/ on-device → POSTs to /capture → ephemeral token-keyed inbox → web app pulls + merges. Phased A (core) / B (inbox) / C (Shortcut + tests). See project_hybrid_strategy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>9d3aa90 -
Feat: living-library merge polish (Phase 3)
- /import returns added_urls + kept; merge_rows now returns the added rows. - Client tags freshly-merged places (row._isNew) → a "✨ New" card badge, floats them to the top of Browse (stable, transient — resets on reload). - Merge banner reports "N new · M already in your library". - Import card gains a Merge/Replace radio (#import-mode), shown only when a cached library exists; a zero-overlap merge still offers Replace inline. Completes the living-library feature (Phases 1–3). Visual review at mobile + desktop; tests/test_merge.py extended (added_urls/kept) and green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>f981d71 -
Feat: living-library cost-skip on re-extract (Phase 2)
POST /upload now accepts merge_into (the cached library CSV). _run_pipeline seeds it as the job's places_full.csv and runs extract.run(resume=True), so _warm_start_from_csv skips the LLM on posts already in the library (keyed on instagram_url). new_urls excludes known posts, so the cost estimate and progress message reflect only genuinely new ones ("X already in your library, skipped"). After extraction the result is folded back with _merge_rows (keep-existing-wins) so known posts keep status/coords and any place the new export omitted is re-added. Client sends the cached library on the Extract upload. tests/test_merge.py gains a warm-start section that patches extract.analyze_batch to prove known posts never reach the LLM and keep their visited status. A 600-post re-export where 550 are already mapped now only pays to extract the 50 new ones. Phase 3 (polish) is next. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>1a26356 -
Feat: living-library merge on import (Phase 1)
POST /import now accepts an optional merge_into field (the caller's existing library CSV). The uploaded file is folded into it server-side via _merge_rows, keyed on instagram_url (else normalized name|city), keep-existing-wins — so user-owned status and manual edits are never overwritten and only genuinely new posts are added. Response gains added/merged/no_overlap. Client (importPlacesFile) sends the cached localStorage library as merge_into (unless restoring, which forceReplaces), shows an "N new places added" banner, and offers Replace when nothing in the file matched the library. Also fixes the restore card lingering after a library loads (showSubTabBar now hides it). This is the linchpin of the hybrid plan — it's what lets a capture/Shortcut or a re-import grow one living map instead of replacing it. Pinned by tests/test_merge.py (added to CI guardrails). Phase 2 (server-side warm-start cost-skip for bulk re-export) is next. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.c…d1b556d -
Docs: FAQ entry for local-Docker-only features; TODO single-URL local done
Add an FAQ item listing the features that only work on the local Docker build (single post URL, Ollama, server-side geocoding) and why hosted can't do them. Update the TODO single-URL item to reflect the local path is working/confirmed (urllib embed + curl_cffi backup), leaving only the hosted half — which has no free path (IP wall + no CORS; native-app share-sheet is how competitors do it). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>d302d2e -
Feat: curl_cffi as optional backup for single-URL caption fetch
_http_get tries urllib first (zero-dep, already works from a residential IP) and falls back to curl_cffi (browser TLS impersonation) only when urllib errors or returns a walled/unusable response, detected via an ok(html) predicate (Caption block for embed, OG tags for the page). Insurance for if Instagram starts fingerprinting plain requests on the embed route; the dep isn't touched in the common case. Still residential-IP/local-Docker only. Deliberately avoids the GraphQL doc_id path (rotates every 2-4 weeks, fails silently). curl_cffi added to requirements-web.txt as optional. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>5495341 -
Docs: repoint remix-share loop to hosted URL (HTML export dropped)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>88071c8 -
Docs: reprioritize TODO — Single URL → P1, library-merge → P2
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>f857cb4 -
Docs: prune TODO — drop HTML map export, sharpen library-merge item
Remove the self-contained HTML map export item (its use cases are better served by the hosted URL, GPX-for-offline, and a share link). Rewrite the "persistent library" item to its genuinely-unmissing scope: merging a NEW Instagram export into an existing curated library, keyed on instagram_url, preserving status/edits and extracting only unseen posts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>a910421 -
Feat: flush pending library cache on tab background/close
Adds flushCache() wired to visibilitychange→hidden + pagehide so a status/ field/coord edit made within the ~1.5s debounce window is persisted to localStorage before the user leaves, instead of being lost. Also prunes completed items from TODO.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>90d15a0 -
Feat: client-side library persistence (localStorage restore card)
Cache the canonical CSV in the browser after load and write-through on every edit (status, fields, coords). A returning visitor with no live job sees a #restore-card that replays the cached CSV through /import — a pure reconstruction-cache, so status/coords/edits round-trip losslessly with no new data shape and no server change. Stored only on-device; Download stays the durable backup. Works in both deployment modes (no HOSTED branch). - app.js: importPlacesFile() refactored out of handleImportFile; cacheLibrary/ cacheLibrarySoon/maybeShowRestore + restore/forget handlers; quota note - index.html + app.css: #restore-card above the import card - tests/test_persistence.py: cache/restore round-trip is lossless (CI guardrail) - docs: README, CLAUDE.md, TODO (item marked done; IndexedDB deferred) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>1c43cdb
-
Test: export.build_kml coordinate invariants (regression for coord-less KML)
Five new checks in test_pipeline.py guard against places_map.kml being generated without <Point><coordinates> elements when geocoded rows exist: build_kml writes coords, omits Point for ungeocoded rows, correct pinned count in mixed batches, full export→import round-trip, and export.run() end-to-end via a temp CSV. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>1fec3cb
-
Feat: multi-venue extraction for transcription path + tests
Harshith Belagur +1: ClaudeA single reel can name several places ("my top 3 spots in Rome"). The transcription path previously kept only the first venue because transcribe.run() used analyze_post() (returns results[0]). - extract.py: add analyze_post_all() — returns every venue found (empty list for non-places). analyze_post() keeps its single-result contract for the benchmark suite. - transcribe.py: run() now extracts all venues; the per-URL checkpoint stores a list and the resume path stays back-compatible with the legacy single-dict format. - tests: add the four TODO test cases — multi-venue parse, person-handle exclusion, analyze_post_all, and the full transcription path (test_pipeline.py); plus /extract-url route tests for 2-venue, 5-venue, and person-handle (test_e2e.py). Closes the "Test cases needed" item under the single-URL feature in TODO. Co-Authored-By: Claude <noreply@anthropic.com>291da19 -
Docs: split Instagram export guide into desktop + mobile steps
Harshith Belagur +1: ClaudeThe "How to get your Instagram file" guide, FAQ entry, and README only documented one entry path (phrased for the app). Split each into a 💻 computer (instagram.com) flow and a 📱 Instagram app flow, sharing the common Accounts Center → Download your information steps. Adds an .export-platform heading style for the two labeled blocks in the Extract tab. Co-Authored-By: Claude <noreply@anthropic.com>08d1d42 -
Chore: remove cf-worker (CF IPs also blocked by Instagram)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>374aa7f -
Fix: fast-fail on hosted URL fetch with clear error message
Instagram blocks all cloud server IPs (HF, CF Workers — returns login wall). URL feature is local Docker only. Hosted builds now immediately return a clear error instead of waiting 30s for yt-dlp to time out. CF Worker script kept in deploy/ for future reference but unused. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>54d09f0 -
Feat: Cloudflare Worker relay for hosted Instagram URL fetch
Instagram blocks all cloud server IPs (HF, Render, etc). The Worker runs on Cloudflare's edge IPs which Instagram cannot block. deploy/cf-worker/worker.js fetches /embed/captioned/ and returns {"contents": html}. Server-side: proxy path now skips yt-dlp entirely (no 15s timeout), uses _parse_embed_caption on the returned HTML. hosted.env.example documents CAPTION_PROXY_URL as the Worker URL. Local builds unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>a8b1596 -
Fix: URL fetch works on hosted via Instagram's embed/captioned endpoint
Uses /embed/captioned/ (statically served for website embeds — works from any server IP, no proxy needed) as the primary caption source. The proxy path (CAPTION_PROXY_URL) is kept as an override but is no longer needed or set by default. yt-dlp remains as a local fallback that also captures geotag data. Adds 3 new unit tests for _parse_embed_caption (basic, empty page, creator fallback) bringing pipeline checks to 101. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>495ba69 -
Test: add pipeline tests for multi-venue extraction and URL fetch helpers
17 new checks in test_pipeline.py covering: - _parse_batch_response: None guard, empty string, single place, not-place, multi-venue array for single post (must not trim), batch trim, padding, markdown fences - split_multi_venue: single row passthrough, two separate venue rows - _parse_og_tags: description/title extraction, content-first ordering, empty page resilience - _fetch_via_proxy: empty contents raises ValueError cleanly TODO.md: added test cases for multi-venue URL posts (multiple @handles, multi-place video transcription, person vs venue handle disambiguation). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>82a7965 -
Fix: Sonnet default, thinking-block skip, multi-venue URL extraction
- DEFAULT_MODEL: claude-fable-5 → claude-sonnet-4-6 - extract.py: skip thinking blocks when reading LLM response (claude-fable-5 and extended-thinking models prepend a thinking block; content[0].text is None — now finds first text-type block) - extract.py: max_tokens floor of 1024 so single-post calls aren't truncated by the 250-token default - extract.py: system prompt now treats venue @handles as strong signal that a post IS about a place; multi-venue handles prompt the model to return an array - extract.py: _parse_batch_response no longer trims multi-venue arrays for single-post calls (expected=1) - web/app.py: _run_url_pipeline uses analyze_batch directly so all venues from a multi-handle post are captured Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>256d2ea -
Feat: single Instagram URL extraction via CAPTION_PROXY_URL
Re-adds the URL input on the Extract tab. Caption fetch now routes through a configurable proxy (CAPTION_PROXY_URL) on hosted builds — default allorigins.win requires no account. Local Docker skips the proxy and uses yt-dlp + direct OG scraping instead. pipeline/transcribe.py: fetch_post_metadata gains proxy_url param; _fetch_via_proxy handles allorigins.win JSON response format. web/app.py: _run_url_pipeline + /extract-url + /url-chatbot-prepare restored; reads CAPTION_PROXY_URL from env. deploy/hosted.env.example: CAPTION_PROXY_URL=https://api.allorigins.win/get TODO.md: P0 item for server-side video transcription; URL item updated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>56b50ce -
Revert: remove single Instagram post URL feature
Removed the URL input from the Extract tab, all backend routes (/extract-url, /url-chatbot-prepare), JS logic, CSS, and the fetch_post_metadata/OG scraping helpers from transcribe.py. Root cause: yt-dlp needs --cookies-from-browser for Instagram auth (works locally, not on a hosted server). Tracked in TODO.md as a potential local-only CLI flag (--url) instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>425566e -
Fix: API key gate for URL mode; faster timeouts; better network error message
Three issues addressed: 1. "Extract now →" was enabled without an API key on hosted deploys. Added data-has-server-key body attr; _syncUrlModeButtons() now disables the button with "Enter API key above to continue" when Anthropic is selected and neither a server key nor a typed key is present. Re-checks on every keystroke in the API key field. 2. Both yt-dlp and OG scraping were timing out on HF Spaces (Instagram is likely blocked from HF's network). Reduced timeouts: yt-dlp 15s, OG HTTP 8s, so failures surface quickly rather than hanging for 30s+. OG scrape tries two SSL contexts before giving up. 3. When both fetches fail, the error now says "try the free chatbot mode instead" since that path only needs the caption pasted by the user. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>0c8fe05 -
Fix: fall back to OG scraping on any yt-dlp failure, not just photo posts
Previously only fell back on "no video in this post" errors. On HF Spaces yt-dlp can also timeout (Instagram throttling) or fail for other reasons — all of which now fall through to OG scraping. Timeout reduced to 20s. Only raises if both yt-dlp and OG scraping fail, with a combined error message. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>011a581 -
Feature: chatbot (free) mode works for single URL extraction
Fetches the post caption server-side (yt-dlp / OG scraping, no API key), wraps it as a single-post chatbot export, and hands off to the existing chatbot-step2 UI — same download JSON + copy prompt + paste response flow as the full-export chatbot path. - POST /url-chatbot-prepare: fetch caption → chatbot_mod.prepare_export([post]) - app.js: chatbot+URL shows "Prepare extraction file →"; clicking it calls /url-chatbot-prepare and populates chatbot-step2 without touching the existing file-based chatbot flow - _syncUrlModeButtons() centralises run-btn vs chatbot-prepare-btn visibility for URL mode so setProvider() stays clean Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>42e5a24 -
Fix: add yt-dlp to web requirements; block chatbot+URL mode
Two HF Spaces bugs: 1. yt-dlp was commented-out/optional — not installed in the Docker image. Added to requirements-web.txt so it's always present on web deploys. 2. Entering a URL while chatbot provider was active hid the prepare button, showed run-btn, then silently submitted as anthropic without a key and failed. Now URL mode blocks run-btn with an explanatory message when chatbot is selected, and setProvider() respects URL mode state when the provider is changed after a URL has been typed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>47dfeed -
Fix: fall back to OG HTML scraping for photo posts
yt-dlp only handles video/reels — photo posts return "no video in this post". _fetch_og_tags() scrapes og:description and og:title from the page (public posts only, no auth), extracting the caption and creator. fetch_post_metadata() now tries yt-dlp first, then falls back on the "no video" error. SSL context uses certifi (already a core dep). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>4e812fc -
Fix: defer tqdm/anthropic/certifi imports to inside run()
fetch_post_metadata() only needs stdlib (subprocess, json) but the module-level tqdm/anthropic/certifi imports were crashing the web server when /extract-url triggered the import. Moved all heavy deps inside run() where they're actually used. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>4db6cc7 -
Feature: extract a single place from an Instagram post URL
Users can now paste any https://www.instagram.com/p/… URL directly in the upload card as an alternative to the full export ZIP. yt-dlp fetches the post caption and optional geotag server-side, the same LLM extraction + geocode + KML export pipeline runs on the result, and progress is streamed via the existing SSE flow — no new UI states needed. - pipeline/transcribe.py: fetch_post_metadata() uses yt-dlp --dump-json - web/app.py: _run_url_pipeline() + POST /extract-url route - index.html: URL input field + "or paste a single post URL" divider - app.js: URL mode — typing a valid IG URL enables the run button, clears any pending file; selecting a file exits URL mode - app.css: .url-or-divider and .url-input-row styles Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>714e84b -
Brainstorm: visited column + client-side persistence design questions
Add two P1 design brainstorming items: (1) a visited toggle column in the browse table that round-trips through KML/CSV so status persists across re-imports, and (2) browser-side storage (localStorage / IndexedDB / OPFS) so users never need to re-upload between sessions — both reinforce the local-first, no-server-state narrative. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>8b6f1ef
-
Chore: add CODEOWNERS requiring harshithbelagur review on all PRs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>9e92878 -
UX: footer copy, mobile hero stack, visual review workflow
- Footer: "Open source — star it, fork it, break it, fix it, make it yours. Contribute on GitHub ↗" - Hero stacks vertically on mobile (≤480px): full-width Watch demo button below copy - CLAUDE.md: document required visual review (Playwright 390/768/1280px) before every front-end commit Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>bc8eb05 -
UX: update footer copy to dev-friendly OSS callout
"Open source — star it, fork it, break it, fix it, make it yours. Contribute on GitHub ↗" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>bca3ed3 -
Fix: footer centering and visited link color
Add max-width + margin:auto so footer aligns with card content, not the page edge. Add a:visited rule to prevent browser-default purple link color. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>311b263 -
UX: remove docker note from hero, add OSS footer
- Hero card is back to clean copy + button only; Docker info lives in the FAQ where it belongs - Add #site-footer: "Open source — contribute on GitHub ↗", border-top, muted text, full-width Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>fada9fe -
Fix: demo hero layout — nested structure fixes copy collapse and docker note row
Wraps copy+button in .demo-hero-main (flex row) inside #demo-hero (flex column) so the docker note reliably sits below as its own full-width row. Previously all three siblings were in a flat flex row, causing the copy to collapse into a narrow word-wrap column. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>8822e0c -
UX: fix demo hero font sizes and docker note copy
- Remove hardcoded 0.85rem from strong (inherits body default), bump span to 0.88rem and docker note to 0.82rem so the hero reads consistently with the rest of the UI - Reword docker strip to "Want to run it locally? Docker version on GitHub" — avoids implying only Docker is free Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>bdaa9fe -
UX: Docker nudge on landing, FAQ item, Ollama-aware tour step
- Add `.demo-docker-note` strip inside `#demo-hero` linking to GitHub repo for self-hosting with Docker - Add FAQ item "Can I run this fully offline and free with Docker?" (after AI models FAQ) - Tour model-picker step (`model-card`) is now Ollama-aware: on hosted (`OLLAMA_ENABLED=false`) the Ollama option is omitted; `body` can be string or function - Update README.md and CLAUDE.md to document all three changes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>7d20eac -
Fix: Escape key closes the demo tour
Wire keydown Escape to endTour() so users can exit at any step (previously only the ✕ button and overlay-click worked; Escape only closed the feedback modal). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>44d956c -
UX: floating feature-request button → pre-filled GitHub issue
- Floating 💡 pill button (bottom-right, z-index 9000, safe-area-inset aware for iOS) - Click opens a modal (title + details fields, slide-in animation, Escape / overlay-click to dismiss) - Submit constructs issues/new?labels=enhancement&title=…&body=… and opens in new tab - Collapses to emoji-only below 480 px so it doesn't crowd mobile screens Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>9f1919e -
UX: demo tour with dummy data, hero placement on landing page
- tour.js: inject 8 real-world demo places (Tokyo/London/Paris/LA/Bangkok…) into allRows so users see a fully populated Roulette pick, Browse grid with Leaflet map, and download bar — no upload needed - HTML: move tour trigger from FAQ tab to a #demo-hero card between the header and the nav (▶ Watch demo) - app.css: replace .tour-cta-card with #demo-hero styles; tour state is fully restored on exit Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>3e19b3d -
Hooks: add pre-commit docs guard for git and Claude Code
- scripts/hook-pre-commit-docs.sh: blocks git commit when .py/.js/.html/.css files are staged but README.md or CLAUDE.md are not - scripts/install-hooks.sh: one-shot setup that installs the git hook and registers the Claude Code PreToolUse hook into .claude/settings.json - CLAUDE.md: document the hooks and the install command Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>95d4e02 -
UX: add guided tour, pipeline timing note
- tour.js: 6-step spotlight tour (upload → model → run → reimport → roulette → pipeline diagram); simulates tab navigation and temporarily reveals sub-tab-bar in demo mode; triggered by ▶ Take the tour button in FAQ - Progress card: static timing note ("usually 5–10 min — may appear stuck") so users don't abandon a running job - app.css: tour overlay/spotlight/tooltip styles + .progress-timing-note + .tour-cta-card/.btn-tour Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>5c44de2 -
UX: add FAQ tab with pipeline diagram, About, and accordion FAQ
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>23dabb1 -
Rename: Food Mapper → IG Food Mapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>a9cca6c