@imsovikde/source-grab 1.0.1
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/CHANGELOG.md +27 -0
- package/LICENSE +21 -0
- package/README.md +281 -0
- package/background/capture-registry.js +306 -0
- package/background/debugger-capture.js +171 -0
- package/background/download-engine.js +316 -0
- package/background/service-worker.js +426 -0
- package/content-scripts/auto-scroll.js +51 -0
- package/content-scripts/isolated-bridge.js +36 -0
- package/content-scripts/main-world-hook.js +278 -0
- package/design-tokens.css +67 -0
- package/icons/2-double-tap.svg +12 -0
- package/icons/2-scroll-left.svg +11 -0
- package/icons/2-scroll-right.svg +11 -0
- package/icons/2-scroll-up-1.svg +11 -0
- package/icons/2-scroll-up.svg +11 -0
- package/icons/2-tap-1.svg +15 -0
- package/icons/2-tap.svg +11 -0
- package/icons/2-touch-hold.svg +13 -0
- package/icons/3-scroll-down.svg +11 -0
- package/icons/3-scroll-up.svg +11 -0
- package/icons/3-tap-scroll.svg +13 -0
- package/icons/3-tap.svg +11 -0
- package/icons/analytics.svg +10 -0
- package/icons/anchor.svg +10 -0
- package/icons/apple.svg +11 -0
- package/icons/arrow-circle-down.svg +4 -0
- package/icons/arrow-circle-left.svg +4 -0
- package/icons/arrow-circle-right.svg +4 -0
- package/icons/arrow-circle-up.svg +4 -0
- package/icons/arrow-down-2.svg +3 -0
- package/icons/arrow-down.svg +11 -0
- package/icons/arrow-left.svg +3 -0
- package/icons/arrow-ne.svg +3 -0
- package/icons/arrow-nw.svg +3 -0
- package/icons/arrow-right.svg +3 -0
- package/icons/arrow-se.svg +3 -0
- package/icons/arrow-single-down.svg +3 -0
- package/icons/arrow-single-left.svg +3 -0
- package/icons/arrow-single-right.svg +3 -0
- package/icons/arrow-single-up.svg +3 -0
- package/icons/arrow-square-down.svg +4 -0
- package/icons/arrow-square-left.svg +4 -0
- package/icons/arrow-square-right.svg +4 -0
- package/icons/arrow-square-up.svg +4 -0
- package/icons/arrow-sw.svg +3 -0
- package/icons/arrow-up-2.svg +3 -0
- package/icons/arrow-up.svg +11 -0
- package/icons/arrow.svg +10 -0
- package/icons/at-the-rate.svg +10 -0
- package/icons/automation.svg +4 -0
- package/icons/backward.svg +11 -0
- package/icons/bag-2.svg +3 -0
- package/icons/bag.svg +3 -0
- package/icons/balloon-2.svg +10 -0
- package/icons/balloon.svg +10 -0
- package/icons/bank.svg +10 -0
- package/icons/basket.svg +6 -0
- package/icons/behance.svg +12 -0
- package/icons/bell-2.svg +10 -0
- package/icons/bell.svg +10 -0
- package/icons/bill.svg +19 -0
- package/icons/bisexual.svg +10 -0
- package/icons/blood-bag.svg +10 -0
- package/icons/blood.svg +12 -0
- package/icons/bluetooth.svg +10 -0
- package/icons/bookmark.svg +10 -0
- package/icons/bot.svg +6 -0
- package/icons/bottle.svg +15 -0
- package/icons/box-2.svg +6 -0
- package/icons/box.svg +3 -0
- package/icons/bug.svg +3 -0
- package/icons/bulb.svg +10 -0
- package/icons/burger.svg +16 -0
- package/icons/bus.svg +8 -0
- package/icons/cake.svg +10 -0
- package/icons/calculator.svg +15 -0
- package/icons/calendar.svg +15 -0
- package/icons/call.svg +12 -0
- package/icons/camera-2.svg +10 -0
- package/icons/camera.svg +12 -0
- package/icons/candy.svg +10 -0
- package/icons/car.svg +8 -0
- package/icons/card-2.svg +9 -0
- package/icons/card-3.svg +6 -0
- package/icons/card.svg +4 -0
- package/icons/cart-add.svg +4 -0
- package/icons/cart-delete.svg +4 -0
- package/icons/cart-remove.svg +4 -0
- package/icons/cash.svg +11 -0
- package/icons/caution.svg +12 -0
- package/icons/center-align-2.svg +10 -0
- package/icons/center-align.svg +13 -0
- package/icons/checklist.svg +4 -0
- package/icons/chevrons-down.svg +4 -0
- package/icons/chevrons-left.svg +4 -0
- package/icons/chevrons-right.svg +4 -0
- package/icons/chevrons-up.svg +4 -0
- package/icons/chip.svg +4 -0
- package/icons/clap.svg +14 -0
- package/icons/clock.svg +11 -0
- package/icons/cloud-down.svg +10 -0
- package/icons/cloud-up.svg +10 -0
- package/icons/cloud.svg +10 -0
- package/icons/cloudy-day.svg +15 -0
- package/icons/cloudy-night.svg +10 -0
- package/icons/codepen.svg +10 -0
- package/icons/coffee-cup-1.svg +7 -0
- package/icons/coffee-cup-2.svg +6 -0
- package/icons/coin-2.svg +11 -0
- package/icons/coin.svg +11 -0
- package/icons/confused-emoji.svg +8 -0
- package/icons/conveyor-belt.svg +8 -0
- package/icons/cookie.svg +14 -0
- package/icons/cool-emoji.svg +4 -0
- package/icons/copy.svg +10 -0
- package/icons/crop.svg +10 -0
- package/icons/cross.svg +10 -0
- package/icons/crown.svg +14 -0
- package/icons/crying-emoji.svg +4 -0
- package/icons/cut.svg +10 -0
- package/icons/cutlery.svg +11 -0
- package/icons/dashboard-2.svg +10 -0
- package/icons/dashboard-3.svg +11 -0
- package/icons/dashboard-4.svg +11 -0
- package/icons/dashboard.svg +11 -0
- package/icons/delete.svg +12 -0
- package/icons/diamond.svg +10 -0
- package/icons/dish.svg +10 -0
- package/icons/doc-add.svg +11 -0
- package/icons/doc-remove.svg +11 -0
- package/icons/doc.svg +15 -0
- package/icons/dollar-2.svg +11 -0
- package/icons/dollar.svg +10 -0
- package/icons/double-tap.svg +12 -0
- package/icons/download.svg +11 -0
- package/icons/drawer.svg +10 -0
- package/icons/dribbble.svg +10 -0
- package/icons/drink.svg +10 -0
- package/icons/dropbox.svg +10 -0
- package/icons/dropper.svg +10 -0
- package/icons/egg.svg +11 -0
- package/icons/eject.svg +12 -0
- package/icons/eraser.svg +10 -0
- package/icons/euro.svg +11 -0
- package/icons/facebook-2.svg +10 -0
- package/icons/facebook.svg +3 -0
- package/icons/factory.svg +7 -0
- package/icons/fast-forward.svg +10 -0
- package/icons/fast-rewind.svg +10 -0
- package/icons/fb-messenger.svg +11 -0
- package/icons/femaile.svg +10 -0
- package/icons/file-attachment.svg +10 -0
- package/icons/file-audio.svg +13 -0
- package/icons/file-code.svg +13 -0
- package/icons/file-contract.svg +15 -0
- package/icons/file-csv.svg +13 -0
- package/icons/file-figma.svg +14 -0
- package/icons/file-form.svg +15 -0
- package/icons/file-image-2.svg +12 -0
- package/icons/file-image.svg +12 -0
- package/icons/file-invoice.svg +15 -0
- package/icons/file-jpg.svg +13 -0
- package/icons/file-list.svg +17 -0
- package/icons/file-mov.svg +13 -0
- package/icons/file-mp4.svg +13 -0
- package/icons/file-notes.svg +12 -0
- package/icons/file-pdf.svg +13 -0
- package/icons/file-png.svg +13 -0
- package/icons/file-spreadsheet.svg +11 -0
- package/icons/file-svg.svg +13 -0
- package/icons/file-text.svg +14 -0
- package/icons/file-vector.svg +11 -0
- package/icons/file-zip.svg +10 -0
- package/icons/file.svg +10 -0
- package/icons/filter.svg +10 -0
- package/icons/fire.svg +5 -0
- package/icons/first-aid.svg +11 -0
- package/icons/firstaid-2.svg +12 -0
- package/icons/flag-2.svg +10 -0
- package/icons/flag.svg +10 -0
- package/icons/flashlight.svg +8 -0
- package/icons/flask-round.svg +10 -0
- package/icons/flask.svg +10 -0
- package/icons/flip.svg +12 -0
- package/icons/floppy.svg +11 -0
- package/icons/folded-hands.svg +12 -0
- package/icons/folder-add.svg +11 -0
- package/icons/folder-delete.svg +11 -0
- package/icons/folder-empty.svg +10 -0
- package/icons/folder-remove.svg +11 -0
- package/icons/folder.svg +10 -0
- package/icons/fork.svg +10 -0
- package/icons/forward.svg +11 -0
- package/icons/frame.svg +11 -0
- package/icons/franc.svg +11 -0
- package/icons/free-drag.svg +11 -0
- package/icons/gay.svg +10 -0
- package/icons/genderless.svg +10 -0
- package/icons/gift.svg +10 -0
- package/icons/globe.svg +10 -0
- package/icons/google.svg +10 -0
- package/icons/grid-2.svg +10 -0
- package/icons/grid.svg +10 -0
- package/icons/grinning-squinting-emoji.svg +6 -0
- package/icons/guitar.svg +10 -0
- package/icons/hand-truck.svg +5 -0
- package/icons/hand.svg +10 -0
- package/icons/happy-emoji.svg +8 -0
- package/icons/headphone.svg +10 -0
- package/icons/heart-beat.svg +10 -0
- package/icons/heart-eyes-emoji.svg +6 -0
- package/icons/heart.svg +10 -0
- package/icons/hetro.svg +10 -0
- package/icons/hide.svg +12 -0
- package/icons/home-1.svg +10 -0
- package/icons/home.svg +10 -0
- package/icons/horizontal-scroll.svg +12 -0
- package/icons/hot-air-balloon.svg +3 -0
- package/icons/ice-cream.svg +10 -0
- package/icons/icon.svg +12 -0
- package/icons/info.svg +5 -0
- package/icons/injection.svg +10 -0
- package/icons/instagram.svg +12 -0
- package/icons/intersex.svg +10 -0
- package/icons/key.svg +11 -0
- package/icons/laugh-emoji.svg +8 -0
- package/icons/layer.svg +10 -0
- package/icons/left-align-2.svg +12 -0
- package/icons/left-align.svg +13 -0
- package/icons/lesbian.svg +10 -0
- package/icons/link.svg +11 -0
- package/icons/linkedin.svg +13 -0
- package/icons/list.svg +15 -0
- package/icons/location-pin.svg +10 -0
- package/icons/location.svg +4 -0
- package/icons/lock.svg +11 -0
- package/icons/login.svg +11 -0
- package/icons/logout.svg +11 -0
- package/icons/lungs.svg +10 -0
- package/icons/magic-wand.svg +13 -0
- package/icons/mail-gift.svg +10 -0
- package/icons/mail-open.svg +10 -0
- package/icons/mail-surprise.svg +10 -0
- package/icons/mail.svg +10 -0
- package/icons/male.svg +10 -0
- package/icons/map.svg +13 -0
- package/icons/maximize.svg +13 -0
- package/icons/medical-logo.svg +10 -0
- package/icons/megaphone.svg +10 -0
- package/icons/menu-2.svg +12 -0
- package/icons/menu.svg +12 -0
- package/icons/message-2.svg +13 -0
- package/icons/message.svg +13 -0
- package/icons/mic-2.svg +11 -0
- package/icons/mic.svg +10 -0
- package/icons/microscope.svg +10 -0
- package/icons/minimize.svg +13 -0
- package/icons/money-plant.svg +11 -0
- package/icons/move.svg +10 -0
- package/icons/movie-clapper.svg +13 -0
- package/icons/multi-touch.svg +11 -0
- package/icons/music-2.svg +10 -0
- package/icons/music-3.svg +10 -0
- package/icons/music.svg +10 -0
- package/icons/mute.svg +11 -0
- package/icons/navigation-2.svg +10 -0
- package/icons/navigation.svg +10 -0
- package/icons/night.svg +10 -0
- package/icons/non-binary-transgender.svg +10 -0
- package/icons/note.svg +11 -0
- package/icons/offer.svg +9 -0
- package/icons/ok.svg +10 -0
- package/icons/other-gender.svg +10 -0
- package/icons/page-move.svg +11 -0
- package/icons/paint-brush-2.svg +10 -0
- package/icons/paint-brush.svg +10 -0
- package/icons/paint-bucket-2.svg +10 -0
- package/icons/paint-bucket.svg +10 -0
- package/icons/paint-roller.svg +10 -0
- package/icons/paper-clip-2.svg +10 -0
- package/icons/paper-clip.svg +10 -0
- package/icons/paste.svg +10 -0
- package/icons/pause.svg +11 -0
- package/icons/paypal.svg +10 -0
- package/icons/pen-tool.svg +10 -0
- package/icons/pen.svg +10 -0
- package/icons/pencil-2.svg +10 -0
- package/icons/pencil-3.svg +11 -0
- package/icons/pencil-ruler.svg +11 -0
- package/icons/pencil.svg +10 -0
- package/icons/phone-setting.svg +12 -0
- package/icons/phone.svg +11 -0
- package/icons/photo.svg +11 -0
- package/icons/pie.svg +11 -0
- package/icons/piggy-bank.svg +13 -0
- package/icons/pills.svg +10 -0
- package/icons/pin.svg +10 -0
- package/icons/pinterest.svg +10 -0
- package/icons/pizza-2.svg +10 -0
- package/icons/pizza.svg +33 -0
- package/icons/plane.svg +3 -0
- package/icons/play.svg +10 -0
- package/icons/png.svg +11 -0
- package/icons/point-down.svg +10 -0
- package/icons/point-left.svg +10 -0
- package/icons/point-right.svg +3 -0
- package/icons/point-up.svg +10 -0
- package/icons/popsicle.svg +10 -0
- package/icons/pound.svg +11 -0
- package/icons/product-hunt.svg +10 -0
- package/icons/puzzle-2.svg +10 -0
- package/icons/puzzle.svg +10 -0
- package/icons/qr.svg +21 -0
- package/icons/question-2.svg +11 -0
- package/icons/question.svg +11 -0
- package/icons/rain-heavy.svg +17 -0
- package/icons/rain-light.svg +13 -0
- package/icons/record.svg +11 -0
- package/icons/rectangle.svg +10 -0
- package/icons/right-align-2.svg +12 -0
- package/icons/right-align.svg +13 -0
- package/icons/rocket.svg +7 -0
- package/icons/rotate.svg +11 -0
- package/icons/rss.svg +13 -0
- package/icons/ruble.svg +11 -0
- package/icons/ruler.svg +10 -0
- package/icons/rupee.svg +11 -0
- package/icons/sad-emoji.svg +8 -0
- package/icons/safe.svg +13 -0
- package/icons/sale.svg +7 -0
- package/icons/satellite.svg +3 -0
- package/icons/saving.svg +15 -0
- package/icons/scan-2.svg +15 -0
- package/icons/scan-fingerprint.svg +20 -0
- package/icons/scan.svg +14 -0
- package/icons/screen-rotate.svg +12 -0
- package/icons/scroll-down.svg +11 -0
- package/icons/scroll-left-1.svg +11 -0
- package/icons/scroll-left.svg +11 -0
- package/icons/scroll-up.svg +11 -0
- package/icons/search.svg +10 -0
- package/icons/send-2.svg +10 -0
- package/icons/send-3.svg +11 -0
- package/icons/send.svg +10 -0
- package/icons/server-2.svg +15 -0
- package/icons/server.svg +3 -0
- package/icons/setting-2.svg +11 -0
- package/icons/setting-3.svg +10 -0
- package/icons/setting.svg +12 -0
- package/icons/shape.svg +10 -0
- package/icons/shield-2.svg +11 -0
- package/icons/shield.svg +11 -0
- package/icons/ship.svg +4 -0
- package/icons/shocked-emoji.svg +8 -0
- package/icons/shop.svg +4 -0
- package/icons/shopping-cart-2.svg +4 -0
- package/icons/shopping-cart-3.svg +4 -0
- package/icons/shopping-cart.svg +5 -0
- package/icons/shuffle.svg +10 -0
- package/icons/signal.svg +14 -0
- package/icons/skype.svg +11 -0
- package/icons/smiling-emoji.svg +6 -0
- package/icons/smiling-with-eyes-emoji.svg +8 -0
- package/icons/snapchat.svg +10 -0
- package/icons/snow.svg +22 -0
- package/icons/snowflake.svg +10 -0
- package/icons/snowman.svg +16 -0
- package/icons/sofa.svg +10 -0
- package/icons/speaker.svg +12 -0
- package/icons/spoon.svg +10 -0
- package/icons/spotify.svg +13 -0
- package/icons/square.svg +10 -0
- package/icons/star.svg +10 -0
- package/icons/stethoscope.svg +11 -0
- package/icons/stopwatch.svg +11 -0
- package/icons/suitcase.svg +10 -0
- package/icons/sun-2.svg +11 -0
- package/icons/sun-3.svg +18 -0
- package/icons/sun-4.svg +11 -0
- package/icons/sun.svg +18 -0
- package/icons/sunny.svg +18 -0
- package/icons/surprised-emoji.svg +8 -0
- package/icons/swipe-left.svg +11 -0
- package/icons/swipe-right.svg +11 -0
- package/icons/switch-1.svg +11 -0
- package/icons/switch.svg +10 -0
- package/icons/sync.svg +11 -0
- package/icons/tablet-2.svg +19 -0
- package/icons/tablet.svg +11 -0
- package/icons/tag-2.svg +3 -0
- package/icons/tag.svg +4 -0
- package/icons/tap-1.svg +15 -0
- package/icons/tap.svg +11 -0
- package/icons/target-2.svg +11 -0
- package/icons/target.svg +13 -0
- package/icons/test-tube.svg +10 -0
- package/icons/thumbs-down.svg +11 -0
- package/icons/thumbs-up-2.svg +11 -0
- package/icons/thumbs-up.svg +11 -0
- package/icons/thunderstorm.svg +11 -0
- package/icons/tick-2.svg +4 -0
- package/icons/tick.svg +3 -0
- package/icons/tik-tok.svg +10 -0
- package/icons/tooth.svg +12 -0
- package/icons/tornado.svg +25 -0
- package/icons/touch-hold.svg +13 -0
- package/icons/transform.svg +10 -0
- package/icons/transgender-2.svg +10 -0
- package/icons/transgender.svg +10 -0
- package/icons/tree-2.svg +10 -0
- package/icons/tree.svg +10 -0
- package/icons/trend-down-square.svg +11 -0
- package/icons/trend-down.svg +10 -0
- package/icons/trend-up-square.svg +11 -0
- package/icons/trend-up.svg +10 -0
- package/icons/trophy-2.svg +3 -0
- package/icons/trophy.svg +10 -0
- package/icons/truck.svg +4 -0
- package/icons/tumblr.svg +11 -0
- package/icons/tv.svg +11 -0
- package/icons/twitch.svg +13 -0
- package/icons/twitter.svg +10 -0
- package/icons/uber.svg +10 -0
- package/icons/unbox.svg +3 -0
- package/icons/unhide.svg +11 -0
- package/icons/unlink.svg +12 -0
- package/icons/upload.svg +11 -0
- package/icons/user-add.svg +12 -0
- package/icons/user-caution.svg +13 -0
- package/icons/user-delete.svg +12 -0
- package/icons/user-female.svg +11 -0
- package/icons/user-male.svg +11 -0
- package/icons/user-remove.svg +12 -0
- package/icons/user.svg +11 -0
- package/icons/v.svg +10 -0
- package/icons/vector.svg +10 -0
- package/icons/vibrate.svg +15 -0
- package/icons/video-camera.svg +10 -0
- package/icons/volume-down.svg +11 -0
- package/icons/volume-up.svg +12 -0
- package/icons/wallet.svg +11 -0
- package/icons/warehouse.svg +3 -0
- package/icons/water.svg +10 -0
- package/icons/wave-left.svg +11 -0
- package/icons/wave-right.svg +11 -0
- package/icons/webflow.svg +10 -0
- package/icons/whatsapp.svg +11 -0
- package/icons/wheelchair.svg +11 -0
- package/icons/wind.svg +14 -0
- package/icons/windows.svg +10 -0
- package/icons/wink-emoji.svg +6 -0
- package/icons/won.svg +11 -0
- package/icons/worried-emoji.svg +8 -0
- package/icons/y-combinator.svg +10 -0
- package/icons/yen.svg +11 -0
- package/icons/youtube.svg +11 -0
- package/icons/zap.svg +10 -0
- package/icons/zoom-in-2.svg +12 -0
- package/icons/zoom-in.svg +11 -0
- package/icons/zoom-out-1.svg +11 -0
- package/icons/zoom-out-2.svg +11 -0
- package/icons/zoom-out.svg +11 -0
- package/manifest.json +43 -0
- package/package.json +53 -0
- package/sidepanel/app.js +437 -0
- package/sidepanel/components/bottom-dock.js +148 -0
- package/sidepanel/components/empty-state.js +40 -0
- package/sidepanel/components/file-list-item.js +58 -0
- package/sidepanel/components/file-list.js +134 -0
- package/sidepanel/components/filter-bar.js +261 -0
- package/sidepanel/components/progress-overlay.js +79 -0
- package/sidepanel/components/recording-indicator.js +32 -0
- package/sidepanel/components/stats-header.js +37 -0
- package/sidepanel/icons/file-type-icons.svg +55 -0
- package/sidepanel/icons/ui-icons.svg +85 -0
- package/sidepanel/index.html +37 -0
- package/sidepanel/styles.css +934 -0
- package/sidepanel/utils/clipboard.js +33 -0
- package/sidepanel/utils/format.js +23 -0
- package/sidepanel/utils/virtual-scroll.js +107 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to SourceGrab will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [1.0.0] - 2026-07-24
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Real-time resource capture via Chrome webRequest API
|
|
9
|
+
- Deep scan mode using Chrome DevTools Protocol (CDP)
|
|
10
|
+
- Category-based filtering (Images, Scripts, Stylesheets, Fonts, Media, SVG, PDF, Documents)
|
|
11
|
+
- Search by filename or URL
|
|
12
|
+
- Sort by load order, file size, file type, alphabetical, newest
|
|
13
|
+
- Size range filtering
|
|
14
|
+
- Select All / Deselect All
|
|
15
|
+
- Individual file download with exact filename matching
|
|
16
|
+
- Batch ZIP download with timestamped filenames
|
|
17
|
+
- Copy filenames to clipboard
|
|
18
|
+
- Auto-scroll page to discover lazy-loaded content
|
|
19
|
+
- Data persistence across tab switches and panel close/reopen
|
|
20
|
+
- Master clear button in header
|
|
21
|
+
- Custom dropdown menus (no native selects)
|
|
22
|
+
- Glassmorphism header and footer dock
|
|
23
|
+
- Spring physics button animations
|
|
24
|
+
- Recording indicator with pulse animation
|
|
25
|
+
- Empty state with contextual messages
|
|
26
|
+
- Toast notification system
|
|
27
|
+
- Responsive narrow-width layout
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Souvik Dey
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🌐 SourceGrab
|
|
4
|
+
|
|
5
|
+
**Capture every file your browser loads — in real-time.**
|
|
6
|
+
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[]()
|
|
9
|
+
[](https://github.com/imsovikde/source-grab/releases/latest)
|
|
10
|
+
[](https://github.com/imsovikde/source-grab/pkgs/npm/source-grab)
|
|
11
|
+
[](https://github.com/imsovikde/source-grab/actions)
|
|
12
|
+
[](AGENTS.md)
|
|
13
|
+
[]()
|
|
14
|
+
[]()
|
|
15
|
+
|
|
16
|
+
A high-performance Manifest V3 Chrome Extension that continuously intercepts, catalogs, and downloads every network resource loaded by any active web tab using a **Three-Layer Interception Engine** powered by the **Chrome DevTools Protocol (CDP)**.
|
|
17
|
+
|
|
18
|
+
[⚡ Live Landing Page](https://source-grab.vercel.app/) • [📦 Download v1.0.0 Package](https://github.com/imsovikde/source-grab/releases/tag/v1.0.0) • [📖 Architecture Docs](ARCHITECTURE.md) • [💬 Support Center](SUPPORT.md)
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 📖 Executive Summary & Vision
|
|
25
|
+
|
|
26
|
+
Extracting assets from modern dynamic websites often requires opening DevTools, repeatedly scrolling to trigger lazy-loaded images, right-clicking elements individually, or missing background Blobs and Web Worker streams entirely.
|
|
27
|
+
|
|
28
|
+
**SourceGrab** solves this by replicating the combined power of Chrome DevTools' **Network** and **Sources** panels inside a persistent side panel UI. It intercepts files as they travel across the browser pipeline, categorizes them automatically, allows copy-to-clipboard of file names, and enables bulk downloads individually or as compressed `.zip` archives while preserving original website path structures.
|
|
29
|
+
|
|
30
|
+
> [!NOTE]
|
|
31
|
+
> **SourceGrab operates 100% locally inside your browser.** No network data, URLs, or captured files leave your device.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 🎯 Target Audiences & Key Use Cases
|
|
36
|
+
|
|
37
|
+
| User Persona | Core Problem Solved | Primary SourceGrab Feature |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| 💻 **Web Developers** | Asset auditing & network payload inspection | Real-time CDP capture & category filtering |
|
|
40
|
+
| 🎨 **UI/UX Designers** | Extracting reference SVGs, fonts, and high-res images | One-click SVG filter & filename copying |
|
|
41
|
+
| 🧪 **QA Engineers** | Verifying loaded network assets match build manifests | Size sorting & total count tracking |
|
|
42
|
+
| 📚 **Archivists & Researchers** | Capturing complete single-page offline asset snapshots | Timestamped ZIP bundle download |
|
|
43
|
+
| ⚡ **Content Scrapers** | Bulk saving media without manual right-click saving | Auto-scroll lazy-load capture engine |
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## ✨ Comprehensive Feature Matrix
|
|
48
|
+
|
|
49
|
+
### 1. 🔍 Three-Layer Interception Pipeline
|
|
50
|
+
- **Layer 1 (Passive `webRequest`):** Listens to `chrome.webRequest.onBeforeRequest`, `onResponseStarted`, and `onCompleted` events to index standard HTTP/HTTPS traffic.
|
|
51
|
+
- **Layer 2 (MAIN-World DOM & Fetch Hook):** Overrides `window.fetch` and `XMLHttpRequest`, attaches `MutationObserver` for dynamically injected DOM nodes, and hooks Shadow DOM elements.
|
|
52
|
+
- **Layer 3 (Chrome DevTools Protocol - CDP Deep Scan):** Connects `chrome.debugger` to capture raw response bodies, data URIs, Web Workers, inline Blobs, and stream buffers invisible to standard network listeners.
|
|
53
|
+
|
|
54
|
+
### 2. 🏷️ Category Filtering & Real-Time Search
|
|
55
|
+
- Instant one-click filter buttons for **Images**, **SVG**, **PDF**, **Scripts**, **Stylesheets**, **Fonts**, **Media**, **Documents**, and **Other**.
|
|
56
|
+
- Live search bar supporting instant filtering by filename substring or full URL path.
|
|
57
|
+
- Sorting controls: **Load Order**, **File Size**, **File Type**, **Alphabetical**, and **Newest First**.
|
|
58
|
+
|
|
59
|
+
### 3. 📦 Dual Download Engine & ZIP Export
|
|
60
|
+
- **Individual File Download:** Triggers Chrome native downloads while preserving website directory hierarchy (`SourceGrab/<hostname>/<path>/<filename>`).
|
|
61
|
+
- **Timestamped ZIP Packaging:** Generates compressed `.zip` archives directly in browser memory formatted as `SourceGrab-YYYY-MM-DD_HH-mm-ss.zip`.
|
|
62
|
+
- **Concurrency Control:** Queue manager caps active downloads at 5 concurrent streams with automatic 3-retry backoff.
|
|
63
|
+
|
|
64
|
+
### 4. 📋 One-Click Copy Filenames
|
|
65
|
+
- Filter your view (e.g., all SVG files) and click **Copy** in the dock.
|
|
66
|
+
- Copies all visible filenames (with file extensions) to the system clipboard, separated by line breaks.
|
|
67
|
+
|
|
68
|
+
### 5. 🔄 Auto-Scroll & Lazy Content Discovery
|
|
69
|
+
- Toggles automated incremental page scrolling to trigger `IntersectionObserver` lazy-loaded images, infinite-scroll JSON requests, and video stream segments.
|
|
70
|
+
|
|
71
|
+
### 6. 💾 Multi-Tab Storage Isolation & Service Worker Safety
|
|
72
|
+
- Uses `chrome.storage.local` indexed by tab ID (`registry_${tabId}`) ensuring captured data survives sidepanel toggling, tab switching, and Chrome service worker restarts.
|
|
73
|
+
|
|
74
|
+
### 7. 🎨 Premium Glassmorphic Side Panel UI
|
|
75
|
+
- Designed with **Space Grotesk** (display titles) and **Outfit** (UI body) Google Fonts.
|
|
76
|
+
- Squircle content cards (20px border radius) and pill controls (999px border radius).
|
|
77
|
+
- Smooth spring-physics animations without performance-heavy backdrop blurs.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## ⚡ Installation & Setup Guide
|
|
82
|
+
|
|
83
|
+
### 1. Developer Sideload Guide (Unpacked Extension)
|
|
84
|
+
|
|
85
|
+
> [!IMPORTANT]
|
|
86
|
+
> SourceGrab is built using pure Vanilla ES Modules. No `npm install`, Node.js build, or Webpack bundling step is required to run the extension in Chrome!
|
|
87
|
+
|
|
88
|
+
1. **Clone the Repository:**
|
|
89
|
+
```bash
|
|
90
|
+
git clone https://github.com/imsovikde/source-grab.git
|
|
91
|
+
cd source-grab
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
2. **Open Chrome Extensions Page:**
|
|
95
|
+
Navigate to `chrome://extensions` in your Google Chrome browser.
|
|
96
|
+
|
|
97
|
+
3. **Enable Developer Mode:**
|
|
98
|
+
Toggle the **Developer mode** switch in the top-right corner of the page.
|
|
99
|
+
|
|
100
|
+
4. **Load Unpacked Extension:**
|
|
101
|
+
- Click the **Load unpacked** button in the top-left toolbar.
|
|
102
|
+
- Select the cloned `source-grab` root folder.
|
|
103
|
+
|
|
104
|
+
5. **Open Side Panel:**
|
|
105
|
+
- Click the extension icon in your Chrome toolbar or open the Chrome Side Panel.
|
|
106
|
+
- Select **SourceGrab** from the panel dropdown.
|
|
107
|
+
- Click **Start** to begin capturing resources in real-time!
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### 2. Installing `@imsovikde/source-grab` via npm
|
|
112
|
+
|
|
113
|
+
Because `@imsovikde/source-grab` is published to **GitHub Packages (`npm.pkg.github.com`)**, npm needs to know which registry handles the `@imsovikde` scope.
|
|
114
|
+
|
|
115
|
+
#### Step 1: Configure `.npmrc`
|
|
116
|
+
Create a `.npmrc` file in your project directory (or user directory `~/.npmrc`) with the following scope rule:
|
|
117
|
+
|
|
118
|
+
```ini
|
|
119
|
+
@imsovikde:registry=https://npm.pkg.github.com
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### Step 2: Run npm Install
|
|
123
|
+
```bash
|
|
124
|
+
npm install @imsovikde/source-grab@1.0.0
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
> [!TIP]
|
|
128
|
+
> If you have a GitHub Personal Access Token (`read:packages`), you can authenticate by adding `//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN` to your `.npmrc`.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 🛠️ Project Directory Tree
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
source-grab/
|
|
136
|
+
├── manifest.json # Manifest V3 extension configuration
|
|
137
|
+
├── package.json # Project metadata & packaging scripts
|
|
138
|
+
├── design-tokens.css # Central CSS variables & theme tokens
|
|
139
|
+
├── AGENTS.md # AI coding agent development guidelines
|
|
140
|
+
├── ARCHITECTURE.md # System architecture & Mermaid flow diagrams
|
|
141
|
+
├── SUPPORT.md # Troubleshooting & user support guide
|
|
142
|
+
├── GOVERNANCE.md # Project maintainership & RFC standards
|
|
143
|
+
├── LICENSE # Permissive MIT License
|
|
144
|
+
├── README.md # Product page & main documentation
|
|
145
|
+
├── CHANGELOG.md # Version history log
|
|
146
|
+
├── CODE_OF_CONDUCT.md # Contributor Covenant v2.1
|
|
147
|
+
├── CONTRIBUTING.md # Developer contribution guidelines
|
|
148
|
+
├── SECURITY.md # Vulnerability disclosure policy
|
|
149
|
+
├── PRD.md # Product Requirements Document
|
|
150
|
+
├── TRD.md # Technical Requirements Document
|
|
151
|
+
├── .github/ # GitHub workflow & community templates
|
|
152
|
+
│ ├── CODEOWNERS # Maintainer code mapping
|
|
153
|
+
│ ├── DEPENDABOT.yml # Weekly dependency security config
|
|
154
|
+
│ ├── FUNDING.yml # Maintainer sponsorship config
|
|
155
|
+
│ ├── PULL_REQUEST_TEMPLATE.md # PR review checklist
|
|
156
|
+
│ ├── ISSUE_TEMPLATE/ # Bug report & feature templates
|
|
157
|
+
│ └── workflows/ # CI & Release Actions
|
|
158
|
+
│ ├── ci.yml # Extension build verification
|
|
159
|
+
│ ├── release.yml # Release ZIP automation workflow
|
|
160
|
+
│ └── publish-package.yml # GitHub Packages npm publication
|
|
161
|
+
├── background/ # Service Worker & Data Engine
|
|
162
|
+
│ ├── service-worker.js # Central event & messaging hub
|
|
163
|
+
│ ├── capture-registry.js # Local storage registry manager
|
|
164
|
+
│ ├── debugger-capture.js # CDP debugger deep-scan driver
|
|
165
|
+
│ └── download-engine.js # Downloader & ZIP builder
|
|
166
|
+
├── content-scripts/ # DOM Hooks & Injectors
|
|
167
|
+
│ ├── main-world-hook.js # Fetch/XHR/DOM overrides
|
|
168
|
+
│ ├── isolated-bridge.js # Content script message bridge
|
|
169
|
+
│ └── auto-scroll.js # Automated page scroll driver
|
|
170
|
+
├── sidepanel/ # User Interface Shell
|
|
171
|
+
│ ├── index.html # Main side panel markup
|
|
172
|
+
│ ├── app.js # Main UI controller & storage listeners
|
|
173
|
+
│ ├── styles.css # Glassmorphic component styles
|
|
174
|
+
│ ├── components/ # UI Component Modules
|
|
175
|
+
│ │ ├── bottom-dock.js # 8-button dock controller
|
|
176
|
+
│ │ ├── file-list.js # Virtual scroll file list
|
|
177
|
+
│ │ ├── file-list-item.js # Individual list item DOM renderer
|
|
178
|
+
│ │ ├── filter-bar.js # Category filter buttons
|
|
179
|
+
│ │ ├── stats-header.js # Resource counters
|
|
180
|
+
│ │ ├── recording-indicator.js # Animated capture status pill
|
|
181
|
+
│ │ ├── progress-overlay.js # Batch download overlay
|
|
182
|
+
│ │ └── empty-state.js # Contextual empty state renderer
|
|
183
|
+
│ └── utils/ # Side panel helper modules
|
|
184
|
+
│ ├── clipboard.js # Copy to clipboard helper
|
|
185
|
+
│ ├── format.js # Bytes & time formatters
|
|
186
|
+
│ └── virtual-scroll.js # High-performance 60fps list engine
|
|
187
|
+
├── icons/ # 450+ Vector SVG Icon Set
|
|
188
|
+
└── docs/ # Vercel / Cloudflare Landing Page
|
|
189
|
+
├── index.html # Responsive self-contained landing page
|
|
190
|
+
├── robots.txt # Search engine crawling rules
|
|
191
|
+
└── sitemap.xml # SEO Sitemap index
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 📐 Architecture & Data Flow
|
|
197
|
+
|
|
198
|
+
```mermaid
|
|
199
|
+
flowchart TD
|
|
200
|
+
subgraph Active Browser Tab
|
|
201
|
+
PAGE[Web Page DOM]
|
|
202
|
+
MWH[MAIN World Script Hook]
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
subgraph Service Worker Layer
|
|
206
|
+
SW[background/service-worker.js]
|
|
207
|
+
REG[capture-registry.js]
|
|
208
|
+
CDP[debugger-capture.js]
|
|
209
|
+
DL[download-engine.js]
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
subgraph Storage API
|
|
213
|
+
LOCAL[(chrome.storage.local)]
|
|
214
|
+
SESSION[(chrome.storage.session)]
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
subgraph Sidepanel UI
|
|
218
|
+
APP[sidepanel/app.js]
|
|
219
|
+
LIST[VirtualScroll Engine]
|
|
220
|
+
DOCK[Persistent Bottom Dock]
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
PAGE -->|webRequest events| SW
|
|
224
|
+
PAGE -->|Fetch/DOM Overrides| MWH
|
|
225
|
+
MWH -->|postMessage| SW
|
|
226
|
+
CDP -->|CDP Network events| SW
|
|
227
|
+
SW --> REG
|
|
228
|
+
REG -->|Persist Tab Registry| LOCAL
|
|
229
|
+
SW -->|Capture State| SESSION
|
|
230
|
+
LOCAL -->|storage.onChanged| APP
|
|
231
|
+
SESSION -->|storage.onChanged| APP
|
|
232
|
+
APP --> LIST
|
|
233
|
+
APP --> DOCK
|
|
234
|
+
DOCK -->|Action Dispatch| DL
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
For complete technical specifications, see [ARCHITECTURE.md](ARCHITECTURE.md).
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## ❓ Frequently Asked Questions (FAQ)
|
|
242
|
+
|
|
243
|
+
> [!TIP]
|
|
244
|
+
> Need help or found an issue? Check our [SUPPORT.md](SUPPORT.md) guide or submit a [Bug Report](https://github.com/imsovikde/source-grab/issues/new?template=bug_report.md).
|
|
245
|
+
|
|
246
|
+
### Q: Why does `npm install` fail with 404 Not Found?
|
|
247
|
+
**A:** By default, `npm` queries `https://registry.npmjs.org`. Because `@imsovikde/source-grab` is hosted on **GitHub Packages (`https://npm.pkg.github.com`)**, you must configure `.npmrc` with `@imsovikde:registry=https://npm.pkg.github.com` before running `npm install`.
|
|
248
|
+
|
|
249
|
+
### Q: Why does SourceGrab request Debugger permissions?
|
|
250
|
+
**A:** The Chrome Debugger permission enables **Chrome DevTools Protocol (CDP)** deep scanning. This allows SourceGrab to inspect data URLs, inline Blobs, Web Worker responses, and stream buffers that standard extension network listeners miss.
|
|
251
|
+
|
|
252
|
+
### Q: Why doesn't SourceGrab capture files on `chrome://` pages?
|
|
253
|
+
**A:** Google Chrome security policies strictly restrict extensions from injecting scripts or attaching debuggers to internal system pages (`chrome://`, `chrome-extension://`, or the Chrome Web Store).
|
|
254
|
+
|
|
255
|
+
### Q: Are my captured files or browsing data sent to an external server?
|
|
256
|
+
**A:** **No.** SourceGrab runs 100% locally within your Chrome browser. It contains zero analytics, tracking scripts, or external network requests.
|
|
257
|
+
|
|
258
|
+
### Q: How does the ZIP download work?
|
|
259
|
+
**A:** SourceGrab compiles selected resource buffers into an uncompressed/STORE ZIP file in browser memory using a custom binary encoder, triggering a single `.zip` file save dialog.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## 🛡️ Native GitHub Community & Documentation Index
|
|
264
|
+
|
|
265
|
+
- 🤖 **[AGENTS.md](AGENTS.md)** — AI coding agent development guidelines.
|
|
266
|
+
- 📐 **[ARCHITECTURE.md](ARCHITECTURE.md)** — System architecture & data pipeline specs.
|
|
267
|
+
- 🤝 **[CONTRIBUTING.md](CONTRIBUTING.md)** — Contribution protocols & standards.
|
|
268
|
+
- ⚖️ **[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)** — Community standards (Contributor Covenant v2.1).
|
|
269
|
+
- 🛡️ **[SECURITY.md](SECURITY.md)** — Security policies & vulnerability reporting.
|
|
270
|
+
- 🛠️ **[SUPPORT.md](SUPPORT.md)** — User help center & FAQs.
|
|
271
|
+
- 👥 **[GOVERNANCE.md](GOVERNANCE.md)** — Maintainer roles & RFC process.
|
|
272
|
+
- 📜 **[LICENSE](LICENSE)** — Permissive MIT License.
|
|
273
|
+
- 📋 **[CHANGELOG.md](CHANGELOG.md)** — Complete version release history.
|
|
274
|
+
- 📄 **[PRD.md](PRD.md)** — Product Requirements Document.
|
|
275
|
+
- 📄 **[TRD.md](TRD.md)** — Technical Requirements Document.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
<div align="center">
|
|
280
|
+
<sub>Built with ❤️ by <a href="https://github.com/imsovikde">Souvik Dey</a></sub>
|
|
281
|
+
</div>
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
// background/capture-registry.js
|
|
2
|
+
// v1.1 — Uses chrome.storage.local for persistent registry data
|
|
3
|
+
|
|
4
|
+
export class CaptureRegistry {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.memoryCache = new Map(); // Map<tabId, { resources: Map<normalizedUrl, ResourceEntry>, nextIndex: number }>
|
|
7
|
+
this.writeTimeouts = new Map();
|
|
8
|
+
this.pendingFlush = new Set(); // Track tabs that need flushing
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async init() {
|
|
12
|
+
try {
|
|
13
|
+
const data = await chrome.storage.local.get(null);
|
|
14
|
+
for (const [key, value] of Object.entries(data)) {
|
|
15
|
+
if (key.startsWith('registry_')) {
|
|
16
|
+
const tabId = parseInt(key.replace('registry_', ''), 10);
|
|
17
|
+
const resources = new Map();
|
|
18
|
+
if (value && value.resources) {
|
|
19
|
+
for (const [url, entry] of Object.entries(value.resources)) {
|
|
20
|
+
resources.set(url, entry);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
this.memoryCache.set(tabId, { resources, nextIndex: value.nextIndex || 0 });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
} catch (e) {
|
|
27
|
+
console.error('Failed to init CaptureRegistry:', e);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
normalizeUrl(url) {
|
|
32
|
+
try {
|
|
33
|
+
// Skip data: and blob: URLs — just return as-is
|
|
34
|
+
if (url.startsWith('data:') || url.startsWith('blob:')) return url;
|
|
35
|
+
|
|
36
|
+
const parsed = new URL(url);
|
|
37
|
+
parsed.hash = '';
|
|
38
|
+
|
|
39
|
+
const ignoreParams = ['_t', '_ts', 'timestamp', 'v', 'ver', '_v', 'cb', 'cache', '_', '_=', 't', 'nocache', 'rand', 'random'];
|
|
40
|
+
const params = Array.from(parsed.searchParams.keys());
|
|
41
|
+
for (const p of params) {
|
|
42
|
+
if (ignoreParams.includes(p.toLowerCase())) {
|
|
43
|
+
parsed.searchParams.delete(p);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const sortedParams = Array.from(parsed.searchParams.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
48
|
+
parsed.search = new URLSearchParams(sortedParams).toString();
|
|
49
|
+
parsed.hostname = parsed.hostname.toLowerCase();
|
|
50
|
+
|
|
51
|
+
let result = parsed.toString();
|
|
52
|
+
if (result.endsWith('/') && parsed.pathname !== '/') {
|
|
53
|
+
result = result.slice(0, -1);
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
} catch (e) {
|
|
57
|
+
return url;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
extractFilename(url) {
|
|
62
|
+
try {
|
|
63
|
+
// Handle data: URLs
|
|
64
|
+
if (url.startsWith('data:')) {
|
|
65
|
+
const mimeMatch = url.match(/^data:([^;,]+)/);
|
|
66
|
+
const mime = mimeMatch ? mimeMatch[1] : 'application/octet-stream';
|
|
67
|
+
const extMap = {
|
|
68
|
+
'image/svg+xml': 'inline.svg', 'image/png': 'inline.png',
|
|
69
|
+
'image/jpeg': 'inline.jpg', 'image/gif': 'inline.gif',
|
|
70
|
+
'image/webp': 'inline.webp', 'application/pdf': 'inline.pdf',
|
|
71
|
+
'text/css': 'inline.css', 'text/javascript': 'inline.js',
|
|
72
|
+
'application/javascript': 'inline.js'
|
|
73
|
+
};
|
|
74
|
+
return extMap[mime] || 'inline-data';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const parsed = new URL(url);
|
|
78
|
+
let pathname = parsed.pathname;
|
|
79
|
+
if (pathname.endsWith('/')) pathname = pathname.slice(0, -1);
|
|
80
|
+
const parts = pathname.split('/');
|
|
81
|
+
let lastPart = parts[parts.length - 1];
|
|
82
|
+
if (!lastPart) return 'index';
|
|
83
|
+
return decodeURIComponent(lastPart);
|
|
84
|
+
} catch (e) {
|
|
85
|
+
return 'unknown';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
extractExtension(filename) {
|
|
90
|
+
// Strip query string remnants from filename
|
|
91
|
+
const clean = filename.split('?')[0].split('#')[0];
|
|
92
|
+
const parts = clean.split('.');
|
|
93
|
+
if (parts.length > 1) {
|
|
94
|
+
return parts[parts.length - 1].toLowerCase();
|
|
95
|
+
}
|
|
96
|
+
return '';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
categorize(url, mimeType, resourceType) {
|
|
100
|
+
const filename = this.extractFilename(url);
|
|
101
|
+
const ext = this.extractExtension(filename);
|
|
102
|
+
|
|
103
|
+
// Priority 1: Extension-based
|
|
104
|
+
if (ext === 'svg' || mimeType?.includes('image/svg+xml')) return 'svg';
|
|
105
|
+
if (ext === 'pdf' || mimeType?.includes('application/pdf')) return 'pdf';
|
|
106
|
+
|
|
107
|
+
const images = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', 'ico', 'bmp', 'tiff', 'tif', 'heic', 'heif', 'jxl'];
|
|
108
|
+
if (images.includes(ext) || (mimeType?.startsWith('image/') && !mimeType?.includes('svg'))) return 'images';
|
|
109
|
+
|
|
110
|
+
const scripts = ['js', 'mjs', 'cjs', 'jsx', 'ts', 'tsx'];
|
|
111
|
+
if (scripts.includes(ext) || mimeType?.includes('javascript') || mimeType?.includes('ecmascript')) return 'scripts';
|
|
112
|
+
|
|
113
|
+
if (ext === 'css' || mimeType?.includes('text/css')) return 'stylesheets';
|
|
114
|
+
|
|
115
|
+
const fonts = ['woff', 'woff2', 'ttf', 'otf', 'eot'];
|
|
116
|
+
if (fonts.includes(ext) || mimeType?.includes('font/') || mimeType?.includes('application/font')) return 'fonts';
|
|
117
|
+
|
|
118
|
+
const media = ['mp4', 'webm', 'ogg', 'ogv', 'mp3', 'wav', 'flac', 'aac', 'm4a', 'avi', 'mov', 'mkv', 'm3u8'];
|
|
119
|
+
if (media.includes(ext) || mimeType?.startsWith('video/') || mimeType?.startsWith('audio/')) return 'media';
|
|
120
|
+
|
|
121
|
+
const documents = ['html', 'htm', 'json', 'xml', 'xhtml', 'txt', 'csv', 'md', 'yaml', 'yml', 'toml'];
|
|
122
|
+
if (documents.includes(ext) || mimeType?.includes('text/html') || mimeType?.includes('application/json') || mimeType?.includes('text/xml') || mimeType?.includes('text/plain')) return 'documents';
|
|
123
|
+
|
|
124
|
+
// Priority 2: webRequest resourceType fallback
|
|
125
|
+
if (resourceType) {
|
|
126
|
+
if (resourceType === 'image') return 'images';
|
|
127
|
+
if (resourceType === 'script') return 'scripts';
|
|
128
|
+
if (resourceType === 'stylesheet') return 'stylesheets';
|
|
129
|
+
if (resourceType === 'font') return 'fonts';
|
|
130
|
+
if (resourceType === 'media') return 'media';
|
|
131
|
+
if (resourceType === 'main_frame' || resourceType === 'sub_frame') return 'documents';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return 'other';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async upsert(tabId, partialData) {
|
|
138
|
+
if (!partialData.url) return;
|
|
139
|
+
|
|
140
|
+
// Skip chrome-extension:// and chrome:// URLs
|
|
141
|
+
if (partialData.url.startsWith('chrome-extension://') || partialData.url.startsWith('chrome://')) return;
|
|
142
|
+
|
|
143
|
+
if (!this.memoryCache.has(tabId)) {
|
|
144
|
+
this.memoryCache.set(tabId, { resources: new Map(), nextIndex: 0 });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const tabState = this.memoryCache.get(tabId);
|
|
148
|
+
const normalizedUrl = this.normalizeUrl(partialData.url);
|
|
149
|
+
|
|
150
|
+
let entry = tabState.resources.get(normalizedUrl);
|
|
151
|
+
if (!entry) {
|
|
152
|
+
const filename = partialData.filename || this.extractFilename(partialData.url);
|
|
153
|
+
entry = {
|
|
154
|
+
url: normalizedUrl,
|
|
155
|
+
originalUrl: partialData.url,
|
|
156
|
+
filename,
|
|
157
|
+
extension: this.extractExtension(filename),
|
|
158
|
+
category: 'other',
|
|
159
|
+
mimeType: null,
|
|
160
|
+
size: null,
|
|
161
|
+
statusCode: null,
|
|
162
|
+
timestamp: Date.now(),
|
|
163
|
+
source: 'other',
|
|
164
|
+
domContext: null,
|
|
165
|
+
status: 'requested',
|
|
166
|
+
downloadState: 'none',
|
|
167
|
+
isNew: true,
|
|
168
|
+
index: tabState.nextIndex++,
|
|
169
|
+
requestId: null,
|
|
170
|
+
frameId: null
|
|
171
|
+
};
|
|
172
|
+
tabState.resources.set(normalizedUrl, entry);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Merge partial data, preserving originalUrl
|
|
176
|
+
const oldOriginalUrl = entry.originalUrl;
|
|
177
|
+
Object.assign(entry, partialData, {
|
|
178
|
+
url: normalizedUrl,
|
|
179
|
+
originalUrl: oldOriginalUrl || partialData.url
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Re-derive filename and category after merge
|
|
183
|
+
if (!entry.filename || entry.filename === 'unknown') {
|
|
184
|
+
entry.filename = this.extractFilename(entry.originalUrl);
|
|
185
|
+
entry.extension = this.extractExtension(entry.filename);
|
|
186
|
+
}
|
|
187
|
+
entry.category = this.categorize(entry.originalUrl, entry.mimeType, entry.resourceType);
|
|
188
|
+
|
|
189
|
+
this.debouncedSave(tabId);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
debouncedSave(tabId) {
|
|
193
|
+
this.pendingFlush.add(tabId);
|
|
194
|
+
if (this.writeTimeouts.has(tabId)) return;
|
|
195
|
+
|
|
196
|
+
this.writeTimeouts.set(tabId, setTimeout(async () => {
|
|
197
|
+
this.writeTimeouts.delete(tabId);
|
|
198
|
+
this.pendingFlush.delete(tabId);
|
|
199
|
+
await this.flushTab(tabId);
|
|
200
|
+
}, 100));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async flushTab(tabId) {
|
|
204
|
+
const tabState = this.memoryCache.get(tabId);
|
|
205
|
+
if (!tabState) return;
|
|
206
|
+
const obj = Object.fromEntries(tabState.resources);
|
|
207
|
+
const dataToSave = {
|
|
208
|
+
[`registry_${tabId}`]: { resources: obj, nextIndex: tabState.nextIndex }
|
|
209
|
+
};
|
|
210
|
+
try {
|
|
211
|
+
await chrome.storage.local.set(dataToSave);
|
|
212
|
+
} catch (e) {
|
|
213
|
+
console.error('CaptureRegistry save error:', e);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Flush all pending writes immediately (called on service worker suspend)
|
|
218
|
+
async flushAll() {
|
|
219
|
+
for (const tabId of this.pendingFlush) {
|
|
220
|
+
if (this.writeTimeouts.has(tabId)) {
|
|
221
|
+
clearTimeout(this.writeTimeouts.get(tabId));
|
|
222
|
+
this.writeTimeouts.delete(tabId);
|
|
223
|
+
}
|
|
224
|
+
await this.flushTab(tabId);
|
|
225
|
+
}
|
|
226
|
+
this.pendingFlush.clear();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async getAll(tabId) {
|
|
230
|
+
const tabState = this.memoryCache.get(tabId);
|
|
231
|
+
if (!tabState) return [];
|
|
232
|
+
return Array.from(tabState.resources.values()).sort((a, b) => a.index - b.index);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async getStats(tabId) {
|
|
236
|
+
const entries = await this.getAll(tabId);
|
|
237
|
+
const stats = {
|
|
238
|
+
total: entries.length,
|
|
239
|
+
byCategory: { images: 0, svg: 0, pdf: 0, scripts: 0, stylesheets: 0, fonts: 0, media: 0, documents: 0, other: 0 },
|
|
240
|
+
newSinceLastBatch: 0
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
for (const entry of entries) {
|
|
244
|
+
if (stats.byCategory[entry.category] !== undefined) {
|
|
245
|
+
stats.byCategory[entry.category]++;
|
|
246
|
+
} else {
|
|
247
|
+
stats.byCategory.other++;
|
|
248
|
+
}
|
|
249
|
+
if (entry.isNew) {
|
|
250
|
+
stats.newSinceLastBatch++;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return stats;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async markBatchDownloaded(tabId, urls, batchId) {
|
|
258
|
+
const tabState = this.memoryCache.get(tabId);
|
|
259
|
+
if (!tabState) return;
|
|
260
|
+
|
|
261
|
+
for (const u of urls) {
|
|
262
|
+
const normalized = this.normalizeUrl(u);
|
|
263
|
+
const entry = tabState.resources.get(normalized);
|
|
264
|
+
if (entry) {
|
|
265
|
+
entry.downloadState = 'completed';
|
|
266
|
+
entry.isNew = false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
this.debouncedSave(tabId);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async clear(tabId) {
|
|
273
|
+
this.memoryCache.delete(tabId);
|
|
274
|
+
if (this.writeTimeouts.has(tabId)) {
|
|
275
|
+
clearTimeout(this.writeTimeouts.get(tabId));
|
|
276
|
+
this.writeTimeouts.delete(tabId);
|
|
277
|
+
}
|
|
278
|
+
this.pendingFlush.delete(tabId);
|
|
279
|
+
await chrome.storage.local.remove(`registry_${tabId}`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Clear ALL registries (browser close cleanup)
|
|
283
|
+
async clearAll() {
|
|
284
|
+
const data = await chrome.storage.local.get(null);
|
|
285
|
+
const keysToRemove = Object.keys(data).filter(k => k.startsWith('registry_'));
|
|
286
|
+
if (keysToRemove.length > 0) {
|
|
287
|
+
await chrome.storage.local.remove(keysToRemove);
|
|
288
|
+
}
|
|
289
|
+
this.memoryCache.clear();
|
|
290
|
+
for (const [tabId, timeout] of this.writeTimeouts) {
|
|
291
|
+
clearTimeout(timeout);
|
|
292
|
+
}
|
|
293
|
+
this.writeTimeouts.clear();
|
|
294
|
+
this.pendingFlush.clear();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async addNavigationMarker(tabId, url) {
|
|
298
|
+
await this.upsert(tabId, {
|
|
299
|
+
url: url,
|
|
300
|
+
mimeType: 'text/html',
|
|
301
|
+
source: 'webRequest',
|
|
302
|
+
status: 'completed',
|
|
303
|
+
filename: this.extractFilename(url) || 'index.html',
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|