All notable changes to this project will be documented in this file.
- Download counts on project cards. Pkglnk.dev's directory API now returns a
downloadCountsdictionary alongsideinstallCounts, populated from the sameinstallstable (rows withevent_type='download', de-duped by session and bot-filtered via the existingfetchDownloadCountshelper). The Unity client reads it and surfaces the count in the right chamber of the v-split pill on project cards — the exact same affordance as install counts on package cards. - After a successful project download, the local count is optimistically incremented and the card chamber re-renders, matching the existing install flow.
PackageDetailViewstats row reads "N downloads" for projects (was always "N installs").
- Project card right chamber now shows the download count instead of the target Unity version. The Unity version is still available in the detail view and on pkglnk.dev — the card is a denser/listy surface and the count is the more useful at-a-glance signal there.
DirectoryResponsegains adownloadCountsfield, populated byPkgLnkApiClient.ParseDownloadCounts(parallel toParseInstallCounts). The two parsers now share a generalisedParseIntDictFieldhelper.PackageCard.BindandPackageDetailView.Showboth take an extradownloadCountargument.
- Project downloads silently failed when a Unity domain reload (script recompile, play-mode toggle, asset import, …) fired mid-download. The in-flight
UnityWebRequestclosure was killed by the reload, soop.completednever ran — no success dialog, no failure dialog, no file on disk, just an orphaned Progress entry that eventually vanished. ProjectDownloadernow tracks every in-flight job in a static list and registers anAssemblyReloadEvents.beforeAssemblyReloadhandler that aborts each request, finishes its Progress entry asFailed, cleans up temp files, and emits a[PkgLnk] Aborting N project download(s) — Unity editor is reloading.Console warning.- Both HTTP requests now have explicit timeouts so a stalled connection can't hang forever (30s on
POST /api/projects/{id}/track-download, 10 min on the archive GET). - Every failure path — cancellation, error, timeout, reload abort — now logs to the Unity Console (
Debug.Log/LogError) so future silent failures leave a paper trail.
- Project download progress appeared frozen at 0% even while the file was successfully downloading. Codeload archives (GitHub/GitLab/Bitbucket) are served chunked with no
Content-Lengthheader, soUnityWebRequest.downloadProgressstays at zero until the response completes — there was no determinate fraction to display. ProjectDownloadernow starts itsProgresstask withProgress.Options.Indefiniteso the bar visibly pulses through both phases, and updates the description with the live byte count (Downloading… 4.7 MB) usingUnityWebRequest.downloadedBytes. Extract phase reportsExtracting… n/totalin the same task.
- Projects are now reached via a Packages / Projects toggle under the search bar on the Directory tab, defaulting to Packages. Replaces the dedicated Projects tab introduced in 0.11.0 — one less top-level destination, projects discoverable from the same surface users already know.
- Toggle selection persists in
SessionStateacross reloads (PkgLnk_ListingType). - Directory empty-state message branches on the selected listing type ("No packages found." / "No projects found.").
- Standalone Projects tab and its
BrowseTab.Projectsenum entry, the_projectsTabbutton, and theTabIcons.Cubeprocedural icon — all replaced by the inline toggle.
- Projects tab in the browser. Lists every pkglnk.dev listing with
listing_type='project'— full Unity repos, not UPM packages — viaGET /api/directory?type=project. - Download flow for projects via the new
ProjectDownloader. HitsPOST /api/projects/{id}/track-downloadfor the archive URL (logged for analytics), streams the zip to a temp file, extracts entry-by-entry into a temp directory adjacent to the target so the final move is an intra-volume rename, and offers a "Reveal in Explorer" dialog on success. - File picker defaults to the parent of the current Unity project root (
Path.GetDirectoryName(<projectRoot>)). - Aborts cleanly with a dialog when
<chosenDir>/<repoName>already exists. - Non-blocking progress reporting via the editor's
ProgressAPI (the bottom-right background-tasks panel). Reports both phases —Downloading… <pct>%thenExtracting… <n>/<total>— and registers a cancel callback that aborts the in-flight web request. - Project card variant of the v-split install pill: violet Download chamber + neutral chamber showing the project's target Unity version (
project_unity_version). Card shows "Downloading…" state while the download is in flight. PackageDetailViewinstall button switches to "Download" +ProjectDownloaderflow when the package is a project.- New procedural
TabIcons.Cubeicon for the Projects tab.
PackageDataextended withlisting_typeandproject_unity_versionfields to mirror the directory API.BrowseTabenum gains aProjectsvalue (appended to the end so existing session-restored tab indices stay valid).PkgLnkApiClient.FetchDirectorytakes an optionallistingTypeargument that maps to the?type=query param.
- Card and avatar images randomly disappearing after play-mode toggles, asset imports, or other
Resources.UnloadUnusedAssets()triggers. Cached textures are now flaggedHideAndDontSave | DontUnloadUnusedAssetso Unity's GC sweep can't destroy them whileImageLoader's cache still holds references. ImageLoadercache now cleared before each assembly reload (AssemblyReloadEvents.beforeAssemblyReload) so post-reload lookups don't return stale references to destroyedUnityEngine.Objecttextures.- Stale cache entries (texture destroyed despite hideFlags) detected on lookup and dropped so the next request re-fetches instead of silently returning a fake-null reference.
ImageLoadercache bounded to 256 entries with LRU eviction. Evicted textures are explicitly destroyed.- Per-card image recheck interval bumped from 60s to 5 minutes, and capped at 3 attempts per card. Previously every card without an image polled
/api/directoryevery 60s indefinitely — with ~40 visible imageless cards, that was 40+ requests/min while the window was open. Window now makes at most ~120 requests over 15 minutes for a fresh worst-case session, then the polling stops entirely (which is also why new packages appearing on pkglnk.dev seemed to land in the Editor "instantly" — they were being repolled aggressively).
- README architecture section corrected:
Editor/Styles/directory listed there never actually existed (USS lives insideEditor/PkgLnkWindow/), and the "dark + light themes" mention is stale since v0.9.0's single-theme consolidation.
- Install-source tagging.
PackageInstaller.Install()now takes an optionalsourceargument identifying which UI surface triggered the install:InstallSource.PkglnkWeb— passed byInstallConfirmWindowwhen the install was deep-linked from a click on pkglnk.devInstallSource.PkglnkUnityWindow— default; used by all in-editor install paths (PackageBrowserView,PackageDetailView,CollectionDetailView,BatchInstaller) Source flows through/api/v1/install-start(which now accepts and validates asourcefield) onto the resulting install_session row, and from there onto the install analytics row at proxy time. pkglnk.dev's analytics panel now distinguishes website-driven vs editor-driven vs direct UPM-paste installs.
BuildInstallUrl(pkg, installId)appends?install=<16-hex>to the UPM Git URL. The proxy reads that param and attaches the resulting install row to the session by primary key — replaces the brittle daily-salted IP-hash join that was losing most rows. Stripped before forwarding to the upstream Git host so it never reaches GitHub/GitLab/Bitbucket.
- UPM
InstallConfirmWindowresult label now wraps and splits the success message onto two lines (slug, then status) — same fix as 0.10.4 applied to the sibling component, since long slugs would hit the samewhite-space: nowraptruncation bug.
- NuGet install-confirm window's success message no longer truncates on long package ids. The result label inherits
white-space: nowrapfromlogin-card-title, which clipped "Microsoft.Bcl.AsyncInterfaces installed successfully" mid-word. OverridewhiteSpacetoNormaland split the message onto two lines (package id on top, status below).
- Post-install verification now queries
NugetForUnity.InstalledPackagesManager.InstalledPackagesto confirm the package actually landed before reporting success. Previously a silent no-op fromInstallIdentifier(e.g. unresolvable version, missing source) would be reported as success because the bool-cast guard returned true on a null result. Now any handoff that doesn't end with the package in NFU's installed list surfaces asHandoffFailedwith a hint pointing the user at NFU's source configuration. InstallIdentifiernon-bool / null return now treated as failure rather than silent success.
- After a verified install, the freshly-installed
Assets/Packages/{Id}.{Version}/folder is pinged in the Project window viaEditorGUIUtility.PingObject. NuGet packages don't appear in Unity's Package Manager, so this gives the user visible confirmation of where the install landed. Falls back silently on custom NFU install locations.
- NuGet handoff now invokes
NugetForUnity.NugetPackageInstaller.InstallIdentifier(INugetPackageIdentifier, bool, bool, bool)directly, rather than searching for an "install from local file" method that doesn't exist in NuGetForUnity v4. Constructs aNugetPackageIdentifier(id, version)via reflection and hands it to NFU, which then resolves the install through its own configured sources. Library/PkglnkCache/is now the .nupkg download location instead ofAssets/Packages/Pkglnk/— the cache lives in Unity's per-project Library folder where it's git-ignored and excluded from the asset pipeline. The pre-download is still useful (it's where pkglnk's analytics events come from) but it no longer pollutes the project's Assets tree.
- NuGetForUnity does its own redundant fetch from nuget.org when we hand it the identifier. The pkglnk pre-download captures the install event regardless. v2 (pkglnk's own .nupkg unpacker) collapses both into a single fetch.
Tools / PkgLnk / Diagnostics / Probe NuGetForUnitymenu item (added in 0.10.1 publishing window — listed here for completeness). Prints loaded NuGetForUnity assemblies, candidate types, and public-static methods to the Console; used to identify the API shape the installer reflects against.
- NuGet installer now resolves the latest version when the web modal sends a
downloadUrlending in/index.json(versions list) rather than a fully-qualified.nupkgURL — fetches the versions list, picks the last entry per NuGet v3's ascending semver order, then continues into the .nupkg download
/install/nugetlocalhost endpoint mirroring the existing UPM/installroute. Body shape:{ packageId, version?, downloadUrl }.downloadUrlis restricted topkglnk.dev/nuget/flatcontainer/*for security, matching the UPM URL-prefix allowlistNuGetPackageInstaller— downloads the.nupkgviaUnityWebRequestfrom pkglnk's flat container so install events flow through the same analytics pipeline as UPM clones, then hands off to NuGetForUnity via reflection (no hard reference — assembly is optional)InstallNuGetConfirmWindow— confirmation popup mirroringInstallConfirmWindowfor browser-initiated NuGet installs, sharing the same Resolving → Downloading → Importing → Complete phase animation
- NuGetForUnity API binding is best-effort reflection. If the assembly isn't installed, or the API has drifted from the heuristic, the
.nupkgis left atAssets/Packages/Pkglnk/{file}.nupkgwith a clear error message pointing the user at the manual fallback (drag into NuGet Package Manager window)
- Card install/installed icons replaced with crisp Lucide-style download arrow and checkmark, matching pkglnk.dev's InstallButton SVG
- Icons now sourced from PNGs rendered from canonical SVGs at 128×128, instead of hand-drawn 14×14 ASCII pixel bitmaps
TabIcons.DownloadandTabIcons.Checkmarkprocedural bitmap textures (replaced byEditor/Icons/download-icon.pngandcheck-icon.png)
- Card install button restyled as a two-chamber pill matching pkglnk.dev directory cards: violet "Install" chamber with download icon + neutral chamber showing the install count
- Install count is now part of the install button click target instead of a separate footer label
- Removed the standalone "N installs" footer label (count moved into the button)
- Installed cards collapse the button to a single-chamber success-green "Installed" pill
- Theming overhauled to match the latest pkglnk.dev visual identity: neutral dark surfaces with violet accent
- Stylesheet refactored to use USS custom properties (
--pkglnk-bg,--pkglnk-accent, etc.) for single-source palette tuning - Card hover state now reads as a violet border accent instead of muddy white
- OAuth callback HTML and markdown link colour brought on-palette
- Light (mint) and Grey theme variants — package is now single-theme to mirror pkglnk.dev
- Theme toggle button removed from header bar
PkgLnkWindowStylesDark.uss,PkgLnkWindowStylesLight.uss,PkgLnkWindowStylesGrey.uss(consolidated into the base stylesheet)- Unused brand assets:
pkglnk-box-green/orange/trans.png,toggle-icon-green/white/grey.png
- EditMode tests failing to compile due to missing
InternalsVisibleTodeclaration on the editor assembly
- Card images failing to load when source URL serves unsupported formats (GIF, WebP) without a matching file extension
- Now always prefers server-optimised PNG for card images, falling back to original URL only when no PNG is available
- Download icon distortion on package card install buttons
- Download icon vertical centering within install button
- Star on GitHub button in profile dropdown with themed styling per colour scheme
- GIF first-frame decoding for card images via GifDecoder
- PNG fallback for card images when primary URL is an unsupported format (GIF, WebP)
- Checkmark icon for installed packages replacing text label on card install button
- Pool size zero guard prevents layout error when no cards are allocated
- Scroll performance: position caching skips redundant style writes on cards that haven't moved
- Scroll performance: card width/height set once on layout instead of every scroll frame
- Scroll performance: display visibility caching avoids writes on already-visible/hidden cards
- Scroll performance: install, bookmark, and description state guards skip unchanged class/style updates
- Scroll performance: increased installed-package cache TTL from 2s to 10s
- Install URLs now use
https://pkglnk.dev/{slug}.gitformat (removed/track/path segment) - Legacy
/track/URLs still recognised for backwards compatibility with existing installs
- Version number display in header bar next to pkglnk.dev brand
- Automatic update detection via GitHub releases API on window open
- One-click update to latest version via highlighted version label
- Platform source icons (GitHub, GitLab) on package card footers using PNG assets
- Download icon on card install button replacing text label
- Bookmark and install button tooltips
- Periodic image recheck for packages without card images (60s interval)
- VersionUtils for semver comparison and installed version reading
- Install button on cards now shows download icon instead of "Install" text
- Platform icons loaded from PNG assets instead of procedural bitmaps for GitHub and GitLab
- README rendering in package detail view with markdown-to-VisualElement converter
- HTML block element support: headings, tables, pre/code, details/summary, lists, blockquotes, hr
- HTML inline element support: strong, em, code, kbd, a, br, del, sup, sub
- HTML entity decoding (amp, lt, gt, quot, nbsp)
- Readme title heading above rendered content
- Table rendering with header row styling and equal-width columns
- README cache to avoid refetching on back/forward navigation
- Grey theme with greyscale colour palette and orange logo variant
- Three-state theme toggle cycle: dark, light, grey
- Collection CRUD: create, edit, delete collections from Unity
- Add-to-collection dropdown on package detail view
- Collection form window with slug availability checking
- Theme toggle moved into header bar alongside auth controls
- MarkdownRenderer converts HTML inline tags to markdown before processing
- Collections tab with collection cards, detail view, and batch install with per-package progress
- Install confirmation popup for browser-initiated installs with pkglnk branding and animated progress
- Install progress tracking with phase callbacks (Resolving, Downloading, Importing, Complete)
- Multi-strategy installed package detection (PackageInfo registry, manifest.json, and Packages lock file)
- Tab icons for Directory, Collections, Bookmarks, and My Packages tabs
- Unit tests for InstallProgressTracker
- Package description area now reserves space for 4 lines for consistent card heights
- Browser install listener uses confirmation popup instead of silent installation
- CORS origin matching for browser install requests (www.pkglnk.dev support)
- Tracking URL validation now accepts both pkglnk.dev and www.pkglnk.dev prefixes
- Background thread crash when checking install state from HTTP listener thread
- Install listener for browser-to-editor communication (localhost HTTP server on port 29120)
- Website "Install in Unity" button now triggers package installation directly in the editor
- Owner avatars on package cards (GitHub, GitLab, Bitbucket)
- Search bar available on all tabs with client-side text filtering for Bookmarks and My Packages
- Close button on filter dropdown
- Install count displayed in card footer
- Bookmark icons changed from emoji to image-based assets (bookmark-outline/filled)
- Filter dropdown made smaller and scrollable with reduced spacing
- Card images use contain scaling instead of crop to preserve readability
- Filter button moved inside search field as compact icon
- Browse tab renamed to Directory
- Profile dropdown rendered above all siblings for correct layering
- Image loading race condition when same URL requested multiple times (callback queuing)
- My Packages tab not loading images due to API response format mismatch
- Removed unused HandleUserPackagesResponse from API client
- Profile dropdown menu with avatar and username as trigger button
- Account option in dropdown opens pkglnk.dev account page in browser
- Sign Out moved into profile dropdown
- Avatar and username now displayed as a pill-shaped profile button
- Sign Out button replaced with profile dropdown menu
- User profile picture displayed next to username in header when logged in
- Avatar loaded from GitHub OAuth profile via pkglnk.dev auth callback
- Filter dropdown menu accessible from all tabs (Browse, Bookmarks, My Packages)
- Filter by install status (All / Installed / Not Installed)
- Filter by bookmark status (All / Bookmarked / Not Bookmarked)
- Filter by platform (GitHub, GitLab, etc.)
- Filter by visibility (Public / Private)
- Filter by topics (multi-select)
- Active filter count badge on the filter button
- Auto-prefetch when filters reduce visible results below viewport
- Topic tags on cards now limited to a single line instead of wrapping
- Topic tag border-radius changed from 9999px to 8px to fix oval distortion
- Initial release
- Editor window for browsing pkglnk.dev package directory
- Package search with debounced text input
- Package detail view with install URL
- One-click package installation via Unity Package Manager
- Topic-based filtering
- Paginated results with Load More