|
Download ponytail/docs/platform-native.md from smlflg/CodingAgent: direct link, hf CLI and curl.
- Browser
- Download file 9.43 kB
-
https://huggingface.co/smlflg/CodingAgent/resolve/main/ponytail/docs/platform-native.md
- Command line
-
hf download hf://smlflg/CodingAgent/ponytail/docs/platform-native.md
-
curl -L -o platform-native.md https://huggingface.co/smlflg/CodingAgent/resolve/main/ponytail/docs/platform-native.md
9.43 kB
| # Platform-Native Solutions | |
| The lazy senior dev's first question is always: *does the platform already do this?* | |
| This document answers that question for the most common cases. Before reaching for a package, scan here. The platform ships with your app for free, doesn't break on updates, and was written by people whose job is exactly that problem. | |
| --- | |
| ## HTML Elements | |
| Things the browser already has as a form control. | |
| | You think you need | What the platform has | | |
| |---|---| | |
| | Date picker library | `<input type="date">` | | |
| | Time picker library | `<input type="time">` | | |
| | Color picker library | `<input type="color">` | | |
| | Range slider library | `<input type="range">` | | |
| | Progress bar component | `<progress value="70" max="100">` | | |
| | Meter/gauge component | `<meter value="0.7">` | | |
| | Modal/dialog library | `<dialog>` + `dialog.showModal()` | | |
| | Accordion/FAQ component | `<details><summary>Title</summary>…</details>` | | |
| | Tooltip library | `title` attribute + CSS `::before`/`::after` | | |
| | Searchable dropdown | `<input list="id"> <datalist id="id">` | | |
| | Auto-growing textarea | `field-sizing: content` (CSS) | | |
| | Sticky header | `position: sticky; top: 0` (CSS) | | |
| --- | |
| ## CSS Capabilities | |
| Things developers reach for JavaScript to do. | |
| | You think you need JS for | What CSS has | | |
| |---|---| | |
| | Responsive font size | `font-size: clamp(1rem, 2.5vw, 2rem)` | | |
| | Fluid spacing | `padding: clamp(1rem, 5vw, 3rem)` | | |
| | Dark mode | `@media (prefers-color-scheme: dark)` | | |
| | Reduced motion | `@media (prefers-reduced-motion: reduce)` | | |
| | Responsive layout without breakpoints | `grid-template-columns: repeat(auto-fill, minmax(250px, 1fr))` | | |
| | Component-level responsive design | `@container` queries | | |
| | Global design tokens / theming | CSS custom properties (`--color-primary: #7c3aed`) | | |
| | Smooth scroll | `scroll-behavior: smooth` | | |
| | Scroll-snap carousel | `scroll-snap-type: x mandatory` + `scroll-snap-align: start` | | |
| | Aspect ratio enforcement | `aspect-ratio: 16 / 9` | | |
| | Truncate text with ellipsis | `overflow: hidden; text-overflow: ellipsis; white-space: nowrap` | | |
| | Multi-line text clamp | `-webkit-line-clamp: 3` | | |
| | CSS cascade layers (style isolation) | `@layer base, components, utilities` | | |
| | Nested CSS selectors | Native CSS nesting (no preprocessor needed) | | |
| | `has()` parent selector | `:has(input:checked)` | | |
| --- | |
| ## JavaScript / Browser APIs | |
| Libraries people install that the runtime already ships. | |
| | You think you need | What the platform has | | |
| |---|---| | |
| | `query-string` / `qs` | `new URLSearchParams(location.search)` | | |
| | `lodash.clonedeep` | `structuredClone(obj)` | | |
| | `lodash.groupby` | `Object.groupBy(arr, fn)` | | |
| | `lodash.debounce` | see debounce one-liner below | | |
| | `numeral` / `accounting` | `new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" })` | | |
| | `date-fns` format | `new Intl.DateTimeFormat("en-US", { dateStyle: "long" }).format(date)` | | |
| | `date-fns` relative time | `new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(-3, "day")` | | |
| | `plural` / `i18n` plurals | `new Intl.PluralRules("en-US").select(count)` | | |
| | `clipboard.js` | `navigator.clipboard.writeText(text)` | | |
| | `uuid` (v4) | `crypto.randomUUID()` | | |
| | Infinite scroll library | `new IntersectionObserver(cb).observe(sentinel)` | | |
| | Resize listener library | `new ResizeObserver(cb).observe(element)` | | |
| | DOM mutation watcher | `new MutationObserver(cb).observe(el, options)` | | |
| | `uuid-validate` | `/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(id)` | | |
| | `is-online` / `connectivity check` | `navigator.onLine` + `online`/`offline` events | | |
| | `sharesheet` library | `navigator.share({ title, text, url })` | | |
| | `store.js` / `localForage` (simple case) | `localStorage.setItem(key, JSON.stringify(val))` | | |
| | Abort fetch on timeout | `AbortSignal.timeout(5000)` passed to `fetch` | | |
| | Custom event bus | `new EventTarget()` / `dispatchEvent(new CustomEvent("x", { detail }))` | | |
| **Debounce one-liner** (no library): | |
| ```js | |
| // ponytail: 3 lines beats a dependency | |
| let t; | |
| const debounce = (fn, ms) => (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), ms); }; | |
| ``` | |
| --- | |
| ## Swift / SwiftUI | |
| UI components people reach for a library or a custom view for. | |
| | You think you need | What the platform has | | |
| |---|---| | |
| | Date/time picker library | `DatePicker` | | |
| | Color picker library | `ColorPicker` | | |
| | Search bar + filtering | `.searchable(text:)` | | |
| | Pull-to-refresh library | `.refreshable { }` | | |
| | Swipe-to-delete / row actions | `.swipeActions { }` | | |
| | Async image loading + cache | `AsyncImage` | | |
| | Charting library | Swift Charts (`import Charts`) | | |
| | Markdown rendering | `Text(...)` markdown / `AttributedString(markdown:)` | | |
| | Share sheet wrapper | `ShareLink` | | |
| | Loading spinner | `ProgressView()` | | |
| | Photo picker | `PhotosPicker` | | |
| | Map SDK (basic) | `Map` (MapKit for SwiftUI) | | |
| | Grid layout library | `Grid` / `LazyVGrid` | | |
| Frameworks and stdlib that wrappers wrap. | |
| | You think you need | What the platform has | | |
| |---|---| | |
| | JSON library (SwiftyJSON) | `Codable` + `JSONDecoder` / `JSONEncoder` | | |
| | HTTP client (Alamofire, simple use) | `URLSession` async/await; Alamofire earns it for complex retry/multipart at scale | | |
| | Date/number/currency formatting | `.formatted()` / `FormatStyle` | | |
| | Regex library | Swift regex literals + `Regex` | | |
| | Crypto library (CryptoSwift) | `CryptoKit` | | |
| | Keychain wrapper | Security `SecItem`; a few lines, not a dependency | | |
| | Persistence / ORM | `SwiftData`, or `@AppStorage` for small key-values | | |
| | Logging library | `Logger` (`os.log`) | | |
| | UUID / Base64 helpers | `UUID()`, `Data(...).base64EncodedString()` | | |
| | Image downsampling | ImageIO `CGImageSourceCreateThumbnailAtIndex` | | |
| | Combine wrappers for async | async/await + `AsyncSequence` | | |
| --- | |
| ## Node.js Standard Library | |
| Packages that wrap Node built-ins. | |
| | You think you need | What Node has | | |
| |---|---| | |
| | `mkdirp` | `fs.mkdirSync(path, { recursive: true })` | | |
| | `rimraf` | `fs.rmSync(path, { recursive: true, force: true })` | | |
| | `make-dir` | `fs.mkdirSync(path, { recursive: true })` | | |
| | `slash` (win paths) | `path.posix` or `path.normalize()` | | |
| | `uuid` (v4) | `crypto.randomUUID()` | | |
| | `ms` (parse duration strings) | keep `ms`, it's genuinely useful and tiny | | |
| | `is-stream` | `val instanceof stream.Readable` | | |
| | `object-assign` | `Object.assign()` / spread | | |
| | `array-uniq` | `[...new Set(arr)]` | | |
| | `array-flatten` | `arr.flat(Infinity)` | | |
| | `flat` | `arr.flat(depth)` | | |
| | `path-exists` | `fs.existsSync(path)` | | |
| | `load-json-file` | `JSON.parse(fs.readFileSync(path, "utf8"))` | | |
| | `write-json-file` | `fs.writeFileSync(path, JSON.stringify(obj, null, 2))` | | |
| | `pkg-dir` | `path.resolve(__dirname, "..")` / `import.meta.dirname` | | |
| --- | |
| ## Python Standard Library | |
| Packages that wrap what Python already ships. | |
| | You think you need | What Python has | | |
| |---|---| | |
| | `python-dateutil` (basic parsing) | `datetime.fromisoformat()` (Python 3.7+) | | |
| | `pytz` | `zoneinfo.ZoneInfo("America/New_York")` (Python 3.9+) | | |
| | `attrs` (simple data classes) | `@dataclass` | | |
| | `six` | drop it, Python 2 is gone | | |
| | `pathlib2` | `pathlib.Path` (built-in since Python 3.4) | | |
| | `enum34` | `enum.Enum` (built-in since Python 3.4) | | |
| | `typing_extensions` (common types) | `from __future__ import annotations` + built-in generics | | |
| | `simplejson` (basic use) | `json` (stdlib) | | |
| | `requests` (simple GET) | `urllib.request.urlopen(url)`, `requests` for anything real | | |
| | `click` (single command) | `argparse` (stdlib) | | |
| | `mergedeep` | `dict \| other_dict` (Python 3.9+) | | |
| | `more-itertools` (basic) | `itertools` (stdlib): `chain`, `islice`, `groupby`, `product` | | |
| | `toolz` (basic) | `functools`: `lru_cache`, `partial`, `reduce` | | |
| | `tabulate` (dev/debug only) | `pprint.pprint()` for quick inspection | | |
| --- | |
| ## Database | |
| Things the application layer implements that the database already does. | |
| | You think you need app code for | What the database has | | |
| |---|---| | |
| | Pagination offset/limit | `LIMIT 20 OFFSET 40` | | |
| | Running totals | `SUM(...) OVER (ORDER BY date)` (window function) | | |
| | Rank within group | `RANK() OVER (PARTITION BY category ORDER BY score DESC)` | | |
| | Pivot / cross-tab | `FILTER (WHERE ...)` + conditional aggregation | | |
| | Deduplication | `SELECT DISTINCT` / `ON CONFLICT DO NOTHING` | | |
| | Soft-delete filtering | Generated column + partial index | | |
| | Tree traversal | Recursive CTE (`WITH RECURSIVE`) | | |
| | Full-text search (basic) | `tsvector` / `MATCH AGAINST` / `FTS5` | | |
| | JSON storage + query | `jsonb` (Postgres) / `JSON_EXTRACT` (SQLite/MySQL) | | |
| | UUID generation | `gen_random_uuid()` (Postgres) / `UUID()` (MySQL) | | |
| | Timestamps on insert/update | `DEFAULT now()` + trigger or `ON UPDATE CURRENT_TIMESTAMP` | | |
| | Enforce uniqueness | `UNIQUE` constraint, not application-level checks | | |
| | Enforce referential integrity | `FOREIGN KEY`, not application-level checks | | |
| | Enforce value ranges | `CHECK (price > 0)`, not application-level validation | | |
| --- | |
| ## The Pattern | |
| Across every layer, the pattern is the same: | |
| ``` | |
| Platform team spends years solving the problem. | |
| Package author wraps it. | |
| You install the wrapper. | |
| The wrapper goes unmaintained. | |
| You debug the wrapper. | |
| ``` | |
| Skip the wrapper. The platform ships with your app for free. | |
| When the native solution is genuinely insufficient (old browser support, edge cases it doesn't handle, ergonomics that matter at scale), the library earns its place. Install it then, not before. | |