Backstory
Files lived inside chats
- People upload a lot into Strive: screenshots of ad dashboards, brand guidelines as PDFs, product shots for a blog post. Each file lived only in the chat it came in, so using it again meant finding that chat, or uploading it twice.
- So I scoped v1 to the smallest version that works, built to extend: more formats and share permissions can follow without a rewrite. On the UI, I went for clean and uncluttered, taking heavily after ChatGPT on the web.
1
2
3
4
5
- A home in the sidebar. A dedicated place in the workbench.
- Upload from here too. Files are a platform entity, not tied to chat alone.
- All files, or Yours. Everything in the workspace, or just what you uploaded.
- Search by name. Debounced as you type.
- Filter, grid, list. Filter by type, and views that give each type a personality of its own.
Design call · the grid
Know the shape before the picture
- Why masonry. Images are most of what people upload, and the pile only grows. Their names (“image (13).png”) say little, so thumbnails do the scanning. Cropped or pixelated previews would look clumsy, and uneven scaling wouldn’t work either. So, like Google Photos, every image keeps its own shape. Worth the extra effort.
- The catch: the browser doesn’t know an image’s shape until it downloads, so tiles start wrong and the page jumps.
- So we fixed it at the source. The server records each image’s size at upload, and every tile is drawn at its final size before any image loads. It pulses softly, then the image fades in.
- A tile stays between half and twice its width tall
- Older images, with no size on record, get a square
- PDFs get a fixed card
- The next page only adds to the bottom
Design call · views
Grid to look, list to find
- The grid is for looking. The list is for finding: name, type, size, who and when, with the matching part of each name highlighted as you search.
- The URL remembers where you are. The tab, the type filter and the search live in the address bar, so a reload or a link to a teammate opens the same view.
1
2
3
4
- The smallest thumbnail. Every upload gets four sizes. Rows use the 160px one, the grid the 480 or 960, and the panel the largest.
- What you scan for. Type, size, who uploaded it and when.
- A menu on every row. The same actions as a tile in the grid.
- Your view, remembered. Grid or list is a personal preference, so it’s saved in your browser. Grid is the default.
Design call · preview
Open it without leaving
- A click opens the file in the same side panel Tasks and Blogs use, so it already feels familiar.
- The no-jump rule holds here too. While an image loads, a placeholder sits at exactly the size it will be drawn, and the image fades in where it was. That’s the recording at the top.
1
2
3
4
5
- The page makes room. It tightens its margins, and the open file stays outlined so you don’t lose your place.
- Shown whole, never stretched. An image is never scaled up past its real size.
- Ask about this file. Starts a chat with it attached. More on that below.
- Download, full screen, close. A download keeps the file’s original name, even with accents or emoji, and the page stays where it is.
- The facts, quietly. Type, size, who uploaded it and when.
Loading · PDFs
A skeleton that steps aside
- PDFs open in the browser’s own viewer. Until it’s ready, you see a skeleton page: a heading and a few lines.
- Some browsers never say a PDF has finished loading. So the skeleton steps aside after eight seconds, whatever happens.
Into the chat
From the Library into chat
- Built once, as a common module. An upload from a chat, the blog image picker or the Library shows up in all of them. And since artifacts are made in chat, any file here can feed one.
- Nothing is sent for you. Ask about this file opens a new chat with the file in the composer, and the first message is whatever you type. Auto-sending “Tell me about this file” would put words in your mouth.
1
2
3
The details
Calm is a lot of small things
- These are the ones I’m quietly proudest of, because if they work, no one sees them.
-
Never a blank page
Change a filter, and the current results stay on screen at half opacity until the new ones swap in.
-
Uploads that don’t blink
Files land in the grid the moment you pick them, dimmed with “Uploading…”, already at their final shape. The placeholder stays until the real file is in the list, so nothing disappears and comes back.
-
A next step in every empty state
“No files match ‘…’” comes with Clear search, “No PDFs yet” with Show all types, and an empty library with Upload files.
-
Errors you can walk away from
A failed load shows inside the card, under the tabs, so you can still switch tab or view to get away from it.
-
Scrolling stays ahead
Files come 40 at a time, and the next page is asked for 800px before you reach the bottom. At the end it says “That’s everything.”
-
Menus that respect touch
A tile’s more button shows on hover or focus, and always on touch screens, where there’s no hover.
What dried
What I’d keep
-
MissDrew the first panel skeleton in our loading grey. It sat on a panel of almost the same lightness (95.9% against 95.5%), so nobody could see it.
LessonCheck a placeholder against what’s behind it. Panel skeletons now sit on a white card with a thin border.
-
MissWaited for each image’s load event. A cached image could finish before React was listening, and its tile waited forever.
LessonDon’t trust an event you might have missed. Each image now checks if it’s already complete as it mounts.
-
LessonShip small, and finish it. No folders, rename or delete in v1: each is a feature with its own questions, like who can delete a teammate’s upload. Real use decides what comes next.
If it feels like nothing happened, it worked.