A list of the most recently updated pages.
Jul 21, 2024 | » | Minimum Penalty for a Shop
4 min; updated Jul 21, 2024
Minimum Penalty for a Shop. leetcode.com . Accessed Jul 21, 2024. Problem You are given the customer visit log of a shop represented by a zero-indexed string customers consisting only of characters N and Y. If the \(i^{th}\) character is Y, it means that customers come at the \(i^{th}\) hour, whereas N indicates that no customers come at the \(i^{th}\) hour. If the shop closes at the \(j^{th}\) hour (\(0 \le j \le n\)), the penalty is calculated as follows:... |
Jul 20, 2024 | » | Queen's Movements on a Chessboard w/ Obstacles
5 min; updated Jul 20, 2024
Queen's Attack II. www.hackerrank.com . Accessed Jul 20, 2024. Problem A queen is standing on an \(n \times n\) chess board. The chess board’s rows are numbered from \(1\) to \(n\), going from bottom to top. Its columns are numbered from \(1\) to \(n\), going from left to right. Each square is referenced by a tuple \((r, c)\), describing the row, \(r\), and column, \(c\), where the square is located.... |
Jul 20, 2024 | » | Minimizing Bottom-Right Paths in a 2xN Grid
4 min; updated Jul 20, 2024
Grid Game - LeetCode. leetcode.com . Accessed Jul 20, 2024. Problem You are given a 0-indexed 2D array grid of size \(2 \times n\), where grid[r][c] represents the number of points at position \((r, c)\) on the matrix. Two robots are playing a game on this matrix. Both robots initially start at \((0, 0)\) and want to reach \((1, n-1)\). Each robot may only move to the right or down.... |
Jun 22, 2024 | » | Testing in a Monorepo
7 min; updated Jun 22, 2024
Testing Web Components While any test framework can work, it’s better to test web components in a browser environment because that’s where they’ll be used. Node-based frameworks would require too much shimming of DOM calls that’d make the tests unrepresentative. and are good options for browser-based testing. is powered by ES-build, and so is the client-side of the app; let’s go down this path and see where it leads.... |
Jun 16, 2024 | » | Database Layer
4 min; updated Jun 16, 2024
Currently using MongoDB’s free tier, which has shared RAM, and up to 5GB of storage . So far, the overall DB usage has been less than 5MB. Query Injection Current State of Affairs Currently have this protection implemented back in 2018: /** * @description Prevent a NoSQL Injection in the search parameters. This is * achieved by deleting all query values that begin with `$`. */ export function sanitizeQuery(query: any) { const keys = Object.... |
Jun 9, 2024 | » | Continuous Integration Tooling
4 min; updated Jun 9, 2024
What automatic tools can I add to keep code quality high? CodeQL CodeQL is a tool that runs variant analysis on code. The idea is that we create a query from a known vulnerability, e.g., SQL injection, and then run it against a codebase to find instances of that vulnerability. GitHub authorizes the use of CodeQL for public repos, and so we are covered . ql-analysis.sh has a recipe for running the analysis locally in the repo.... |
May 28, 2024 | » | Of Stale UI and Re-renders
3 min; updated May 28, 2024
More than once, I’ve been surprised by a web component either showing data that should no longer be there, or not showing data that should be there. This page aims to reason through such cases for a better mental model of web components. Rendering Lists Both <search-bar> and <search-results> need to render a collection of N items. offers two options: looping, or using the repeat(items, keyFunction, itemTemplate) directive. const cards = html` ${cards.... |
May 5, 2024 | » | Inheritance
2 min; updated May 5, 2024
Mixins Introduced to mixins by . Didn’t know that there are passionate advocates for this, e.g., ’s “You can even look at normal subclass inheritance as a degenerate form of mixin inheritance where the superclass is known at class definition time, and there’s only one application of it.” A mix-in is an abstract subclass. This technique is especially useful in languages where a class can only have a single superclass.... |
May 5, 2024 | » | Home Page
2 min; updated May 5, 2024
When a user lands at /home, this UI is shown. A couple of components are shareable from /browse, e.g., search-bar, search-results. Sharing Code with /browse Components initially created for the /browse page are useful in /home as well. The CardsViewingPage Interface This functionality can be shared between the two pages: export class CardsViewingPage extends LitElement { @provide({ context: searchResultsContext }) @state() protected searchResults: CardSearchResult[] = []; @state() protected selectedResult: Card | null = null; @provide({ context: cardsCarouselContext }) @state() protected cardsCarousel = new CardsCarousel([]); protected cardFetcher: CardFetchEndpoint; constructor(cardFetcher: CardFetchEndpoint) { super(); this.... |
May 3, 2024 | » | Reusable Cards
4 min; updated May 3, 2024
Context How can I reuse code in these user experiences? Current card templates. TL: editable card owned by the user. TR: the overall modal experience. BL: a publicly viewable card. BR: a card owned by the user but is in the trash. The current API surface for the different types of UIs is: interface EditableCardViewer { displayNewCard(): void; displayFullCard(cardId: string): void; renderCard(card: Partial<ICard> | null): void; fetchCard(caller: () => Promise<Partial<ICard> | null>): void; fetchPreviousCard(): void; fetchNextCard(): void; updateStreakBar(streak: IStreak): void; handleInputChange(elementId: string): void; handleCardUrgencyChange(): void; handleTagsInputChange(ev: KeyboardEvent): void; displayRawCardDescription(): void; insertTabsIfNecessary(ev: KeyboardEvent): void; saveCard(renderAfterSave: Boolean = true): void; moveCardToTrash(): void; restoreCardFromTrash(cardId: string, cardUrgency: number): void; toggleOption(elementId: "reviewModeToggle" | "card_is_public_toggle"): void; makeInvisible(elementId: string): void; colorUrgencyQuartiles(quartiles: number[]): void; suggestNewTags(tagsInputElement: HTMLInputElement): void; removeTagSuggestions(): void; updateTagsButtons(newTag: string): void; removeTagFromCard(tag: string): void; } interface PublicCardViewer { displayFullCard(cardId: string): void; renderCard(card: Partial<ICard> | null): void; fetchPreviousCard(): void; fetchNextCard(): void; handleSearchInputChange(): void; flagCard(reason: "markedForReview" | "markedAsDuplicate"): void; copyCardToOwnCollection(); } interface TrashedCardViewer { renderTrashedCard(card: Partial<ICard> | null); fetchPreviousCard(): void; fetchNextCard(): void; modifyTrash(endpoint: "/delete-card" | "/restore-from-trash"): void; } The web components v0 has:... |
Apr 28, 2024 | » | The Cards Manager
4 min; updated Apr 28, 2024
UI Design Legacy card viewing UI at /browse. This time we’ll use the more semantic and a11y-friendly <dialog> element . Centering it in the page is done by the browser, and that saves us a bit of hassle – thought it would have been feasible with how <search-bar>’s <ul> floats above the page. CardsManager Interface The previous/next buttons make use of the CardsManager object that has the API:... |
Apr 27, 2024 | » | Markup Features
4 min; updated Apr 27, 2024
Syntax Highlighting Previously, we’d highlight code on the client by loading src/lib/highlight.pack.js, a bundle downloaded from but served from our domain, and then execute hljs.highlightBlock on demand, e.g., on page load, when showing a card, etc. This doesn’t work well with a web-component-centric design. Running hljs.highlightBlock through possible Shadow DOM boundaries is a hassle. Back in 2018 , we installed highlightjs, a shim for the official HighlightJS.... |
Apr 20, 2024 | » | Of Builds and Bundlers
3 min; updated Apr 20, 2024
Moving to TypeScript entails configuring how the JavaScript will be eventually consumed by both the server and the client. Separating the Client Bundle from the Server Bundle The server code runs in Node while the client code runs in the browser. advises separating the configurations for advantages like faster type-checking and compiling, lower memory usage when using an editor, and improved enforcement of the logical groupings of your program.... |
Apr 20, 2024 | » | Browse Page
3 min; updated Apr 20, 2024
When a user lands at /browse, this UI is shown. A couple of components seem to emerge: search-bar, tags-list, card-results, and mini-card. <search-bar> Currently, this is rendered by search_bar_dropdown.ejs, a partial that that is included in both /home and /browse. The fact that there it has JS, no server-delivered content, and has CSS makes it a good candidate for a web component. Revving up search-bar.ts. Ran into Uncaught TypeError: Class constructor s cannot be invoked without 'new' on the export class SearchBar extends LitElement line.... |
Apr 19, 2024 | » | Client/Server Interface
8 min; updated Apr 19, 2024
How to handle redirects without setting window.location.href? Right now, there’s a pattern of doing: sendHTTPRequest("POST", "/login/", {}) .then((_) => { window.location.href = "/"; }) .catch((err) => { console.error(err); }); Isn’t this something that the server can do? In response, why not issue a redirect? Screenshot of the redirect chain from /login. The POST request gets a 303 (See Other) redirect to /home. The browser then makes a GET request to /home, which results in a 304 (Not Modified).... |
Apr 19, 2024 | » | App Layout
1 min; updated Apr 19, 2024
Is there a use-case for the container that lays out the various app elements? When considering a <nav-bar> , the conclusion was that a web component wasn’t necessary. Possible Organization The high-level picture is: <body> <nav> <div id="topnav-banner">...</div> <ul id="topnav-items">...</ul> </nav> <div id="main_div">...</div> <footer>...</footer> </body> We should move #topnav-banner out of the <nav> so that we can potentially apply lateral spacing to #topnav-items and #main_div without it applying to #topnav-banner.... |
Apr 18, 2024 | » | Use of Local Storage
3 min; updated Apr 18, 2024
Back when I wrote this, the motivation for using localStorage was to reduce the trips to the server so that the app is usable offline. However, with two data stores (localStorage and the server), the former has a possibility of going stale. What usage is correct and how can we avoid stale data? localStorage['session_info'] getAccountInfo: () => AuthenticateUser | null fetches the session_info entry and JSON.parses it into an AuthenticateUser. This is a possible failure point because the parsed JSON cannot be trusted to be a valid AuthenticateUser instance.... |
Apr 18, 2024 | » | Wiki Page
2 min; updated Apr 18, 2024
Overview Starting with this page because it’s mostly static. The body is basically: <div class="topnav">...</div> <div id="main_div"> <div id="study_buddy_details"> <div class="details_section">...</div> <div class="details_section">...</div> ... <div class="details_section">...</div> </div> </div> <footer>...</footer> The corresponding web components would be something along the lines of: <nav-bar></nav-bar> <wiki-container> {Table of Contents} <wiki-section></wiki-section> <wiki-section></wiki-section> </wiki-container> <wiki-container> Maybe <wiki-section> can be a template owned by <wiki-container>? Nah, there are benefits to a separate <wiki-section> component, e.g., each component can define its title for use in the ToC.... |
Mar 27, 2024 | » | Hosting the Flashcards App
3 min; updated Mar 27, 2024
Current State: SaaS The app is hosted at render.com on Render’s free tier that gives us these free web services: Custom domains Managed TLS certificates Pull request reviews Log streams Rollbacks up to the two most recent previous deploys. … with these limitations: Spins down after 15min of no inbound traffic. Spinning up on the next request causes a noticeable delay for a couple seconds.... |
Feb 21, 2023 | » | Animal Man (1988 - 1995)
12 min; updated Mar 4, 2024
body { background-image: url('/img/fiction/morrison-animal-man.jpeg'); } Animal Man. Grant Morrison. www.dc.com . www.hoopladigital.com . dc.fandom.com . 1988. Accessed Mar 5, 2023. Snapshots He took himself into the desert. And rose up. And went into the presence of God. And God spoke unto Crafty saying, “You must be punished for this rebellion against my will. Nevertheless, I am a good God and my judgment will be tempered with mercy.... |