This place is where we track our engineering tasks and notes. We are dogfooding 🌭🐕 at the moment and working towards a point where this tool is friendly enough for others to use. Cleaning up the UI and providing some basic edit capability are some steps towards that goal.
comments
TL;DR: Effectively this page is just a scrappy plaintext reader (of Org file to be precise, for the non-Org-mode readers on our team). You should be able to edit notes and tasks in a Confluence/Notion-like manner soon while simultaneously allowing teammembers on your team to interface with this knowledge-base and project-management setup through git and thus keep a full history of your project-management and knowledge-base on their machine.
I, for example, use this file inside of Emacs but am well aware that it would be torture to ask my fellow devs, let alone non-engineering colleagues, to fire up Emacs to get any work done. 😅
Using git underneat will allow us to facilitate nifty diffing features, time-machine like features or proper roll-backs when shit hits the fan. Git is a helluva tool and its time to expose more folks to the magic (magit users will appreciate the pun here) of git without exposing them to git mechanics.
Scroll down to section Background for more information and use the top-right perspective-switch to switch between Prose and Kanban view on this document.
comments
🙊 It's very scrappy but it's a little easier than sending ox-publish HTML or LateX exports over the wire (Slack or mail) or by asking folks to learn Emacs 🙊😅.
Offer something that is way more fun/pleasant to use than Confluence and which is more tolerable than Notion. We want to bring the joy back in collaboration and are approaching this from a developer-first angle.
Knowledge bases and project management tools are currently a source of frustration 😡 and devs are largely keeping notes in separate silos: in different note-taking tools or just on folders on their local machines for easy access simply because the current tools are clunky.
There is a much intel lost which hurts organizations! 📉
Removing the barriers to share knowledge helps teams keep relevant intel together 🧠 and should minimize knowledge loss when people are out-of-office or move on to other challenges. People should ideally ideate in their knowledge management/project-management tools and not be forced to document ideas there as a rote task after conception. We're out to break the barriers to make documenting and ideating together much easier -- challenge accepted. 😅
See what I did with FullScreenshotHero
. We need to do the same with PrimaryFeatures
where instead of two components, one dark and one light, we just have a single component that checks the isDark
prop to determine light/dark coloring and exposes all content (images and text) through props -- same as I did with the hero component.
See LargeScreenshotFeaturesDark
which I just copied from TailwindUI. Compare this to LargeScreenshotFeatures
to stub in the {isDark ? :}
as needed.
Checkout untheme branch and start your work from there.
This is the scrappy app (scr-app 🤣) that we are using to validate some early ideas.
⚒️ Mason's code: Mark tasks as =DONE= only when they are merged into branch =hack=.
See Remove Grommet-related styling is not rendered in staging.
- State "CANCELLED" from "IN_TEST" [2023-02-13 Mon 17:40] \\ Sorted with https://gitlab.com/formation.tools/eng/proto-01/-/merge_requests/223 which may not be too relevant considering all the changes upcoming with the new design but for now we consider this sorted.
Run the storybook, using npm run storybook
and observe headings stories or section story.
The baselines between the workflow states, the heading title and the tags is not correctly aligned and needs to be resolved.
- State "CANCELLED" from "TODO" [2023-02-13 Mon 17:37] \\ Resolved by undoing the infinite scroll (Tijan sorted this)
Because of the temporary lazy-loading (infinite scroll) implementation, links to sections that are not rendered on first paint are not automatically scrolled to. This makes it practically impossible to share URLs to sections later in a doc.
We have to figure out the data eng issues to populate a full index for every page (no matter how long) such that we know how to resolve navigation to sections that are not covered within "first paint".
Note that this concern is related to Implement internal link following and Linking aids.
- State "DONE" from "IN_TEST" [2023-02-13 Mon 17:37] \\ Already merged into mainline
As a user, expanding or collapsing a section is sometimes confusing because it isn't immediately clear what the borders/scope of such a section is. Since thet typography does not have sufficiently distinguishing features at all times to help a user determine scope, we should use highlighting (like a single pulse animation/transition), to indicate what the object of interest (or rather, scope of interest) is.
Put this feature behind a highlight-section-when
feature flag (see featureEnabled
function in core/helpers).
comments
For now featureEnabled is just a dummy that always returns true
but the components will already be developed to deal with upcoming customization capability.
- State "DONE" from "IN_TEST" [2023-02-13 Mon 17:37] \\ Already merged into mainline
As a user, I expect a folded/collapsed section to be unfolded/expanded upon clicking on it.
Put this feature behind a autounfold-focused-section
feature flag (see how featureEnabled
function in core/helpers is used in the TOC).
- State "DONE" from "IN_DEV" [2023-01-05 Thu 21:11] \\ Closed with 65e0bdbeb8b27ad075820b585175143553e3492e
Lists seem to show their discs somewhere that is more center/middle-aligned instead of along the top. This issue can be reproduced when looking at nested lists in Firefox.
- State "DONE" from "IN_TEST" [2023-01-05 Thu 21:12] \\ Completed as live for a while now since da55ef834821dd8953d0597456a727a429a1bb53 - State "IN_TEST" from "TODO" [2023-01-02 Mon 12:54] \\ Merged with 774baa5977cff99f1051b64427c7c2382149dbae
- State "DONE" from "TODO" [2023-01-02 Mon 12:55] \\ Wrapped up with 774baa5977cff99f1051b64427c7c2382149dbae as the overarching point
Not sure what I meant here but we have the overflow set up on the wrapper around Prose to decouple the scrollbar from the confines of the Prose component which is width-constrained. This way the scrollbar shows up along the edges of the container of Prose which looks considerably better.
- State "DONE" from "TODO" [2022-12-31 Sat 18:38] \\ Merged to =hack= and also stubbed some activity highlighting feature.
Jessy said that he understands I'm trying to keep things clean/minimal/low-distraction but this is a bad pattern so we'll have to rip this out.
- State "DONE" from "IN_TEST" [2023-01-05 Thu 21:13] \\ Reduce stem with 5576a803aa18c63d5d3760376a50df5c20866f10
Jessy mentioned that this will really improve the readability of the ToC. The stems are a bit too long now AFAIU.
- State "DONE" from "WIP" [2022-12-31 Sat 17:37] \\ Tijan seems to understand this so we're clear on the task - State "WIP" from "TODO" [2022-12-30 Fri 12:22] \\ Clarified in [[file:README.org::*Implement spy on content to highlight active ToC entry][Implement spy on content to highlight active ToC entry]]
As a user, I want to see where in the ToC sidebar where I'm currently located in a document. This should improve spatial awareness for end-users.
- State "DONE" from "IN_TEST" [2023-01-03 Tue 16:22] \\ Merged into =hack=
We may have to use the IntersectionObserver API for this as @edris, @tijan and @stefano have already been looking at for other tasks and is also showcased in the following CSS-tricks article and StackOverflow thread.
My dinosaur 🦎 brain recalls similar functionality from my Scrollspy days (from Bootstrap and also outlined in this article from 2016) but I have not clue what the best-practice is for such functionality these days.
- State "IN_TEST" from "TODO" [2023-01-19 Thu 15:04] \\ Tijan has implemented something that works. We'll have to provide some implementation that may scale better but that is an opimization point.
- State "DONE" from "TODO" [2022-12-31 Sat 17:36] \\ Done. Tijan brought this home and it's live already.
In order to simplify the reading of hierachy in the ToC sidebar, we want to present the user some margins/cues/lines to indicate relationships.
Kind of like the left-most tree-structure indicated by lines in the output of the tree
command, we want to provide line-like visual cues such that it is easier to understand how items relate to each other.
. ├── data │ └── Profile-20220910T222813.json ├── gen │ └── infra.overview.png ├── ltximg │ └── org-ltximg_190ec1b93750927668e485b3c5d4ec0bac2a7161.png ├── README.org ├── README.org~ └── README.sync-conflict-20220912-104529-HONTRKX.org
- State "DONE" from "IN_TEST" [2022-12-31 Sat 17:36] \\ Done but content needs to be updated. Different problem tho.
Stub a place where we can provide legal notices and other app information that may be useful to users of the application.
Things we'll need to list there:
- State "CANCELLED" from "WIP" [2023-01-03 Tue 16:27] \\ Awaiting design input from Jessy before we continue
For the sake of consistency we want the gap between the expand/collapse chevron and the left-most line of the headline text to be a bit wider.
Furthermore, we also want the anchor link icon to show up before the text such that the link icons always show up along the same place (on the x-axis) as opposed to the current situation where these anchors show up at the end of the line (which could be anything since line-lengths vary quite a bit 🤷🏿♂️).
My expectation is that working on the left-most spacing of the chevron may require us to address the padding/margin on document components such that the alignment is correct. Conceptually it would be fair to introduce a invisible left-margin along which we left-adjust text as sketched https://app.excalidraw.com/l/AoDvHfNWirc/2ogauhhMKj5.
See http://localhost:6006/?path=/story/application-organisms-layouts-contentcontainer--side-by-side-view to test out resizing of prose containers.
Things to be done:
Possible designs are (in order of preference):
- State "DONE" from "IN_DEV" [2023-01-06 Fri 14:33] \\ Right-aligned comments for now.
Since comments are interspersed through a text rendering them in a right-most column may be tricky.
comments
Rendering comments in a separate columns, may require us to
Observe the COMMENTS DISPLAY
section in https://app.excalidraw.com/l/AoDvHfNWirc/2ogauhhMKj5 for input as to how to structure comment blocks in a desktop view.
:COOKIE_DATA: todo recursive
- State "DONE" from "WIP" [2023-01-06 Fri 14:35] \\ Fonts needed for now are added.
Not sure if the following links are relevant here:
- State "DONE" from "IN_SCOPING" [2022-12-23 Fri 21:34] \\ Done with Tijan!
Emojis are currently not consistently rendered. On my Linux box, I see a painful mess of just monochrome Wingdings-style emojis and the more colorful icons. Setting up an emoji font to ensure that everyone sees the same thing would improve consistency here and should be a small enough win for us to lock in.
From a chat with @stefano it appears that this may be tricky to accomplish without some heavy content moderation magic. So we're going back to Twemoji https://github.com/twitter/twemoji.
A way to implement this may be to use https://www.npmjs.com/package/react-twemoji and wrapping text-bearing components inside of a Twemoji
component in order to bring in that conversion magic. Manually modifying the text at this point through custom-code would be premature optimization for what we need atm. We just need something to fix the consistency problem: emojis looking very different between Windows, MacOS/iOS, Linux and Android because the browser default to the platform-specific emoji assets.
Just spoke with Edris and perhaps we something like pulling in a Noto Color Emoji font https://fonts.google.com/noto/specimen/Noto+Color+Emoji would be a more scalable fix. Anything that rewrites the DOM is pretty heavy and will cost us down the line but just plain ol' fonts may be a must gentler solution to this problem.
- State "DONE" from "TODO" [2023-01-06 Fri 14:35] \\ Added with 674be5927832d0d066d1ff480112d74eb47e5e69
Add attribution https://github.com/twitter/twemoji#attribution-requirements for Twemoji usage.
Tijan is already stubbing some place/option modal/tooltip/sidebar or whatever seems most practical where we can drop legal notices and other background information of this sort.
- State "DONE" from "TODO" [2022-09-28 Wed 13:57] \\ Completed with https://gitlab.com/formation.tools/eng/proto-01/-/merge_requests/93
On different platforms the selected serif font will look different. In order to control the quality of the reading experience, let's pick a decent open font to use here.
It is important the the fonts have varying weights such that we can still distinguish from bold text when it is presented inside of a Headline which is already presented in bold.
- State "DONE" from "TODO" [2022-09-28 Wed 13:56] \\ Merged with https://gitlab.com/formation.tools/eng/proto-01/-/merge_requests/93
Here are the options approved by @siarhei:
- State "DONE" from "TODO" [2022-09-28 Wed 13:56] \\ Merged with https://gitlab.com/formation.tools/eng/proto-01/-/merge_requests/93
Here are the options approved by @siarhei:
For readability's sake, we want to color code all the tags such that the same value tags always appear in the same color. This should allow readers to visually associate related items provided that the readers/observers are capable of discerning the colors.
As a follow-up point, we should define a task to break inventorize which colors are fair to use from an a11y perspective in order to maximize the serviced audience by this feature.
- State "CANCELLED" from "WIP" [2023-01-06 Fri 14:36] \\ Cancelling since we're probably changing the general approach. Current entrypoint remains until new design direction/requirements are clarified. - Note taken on [2022-09-05 Mon 22:32] \\ Instead of creating a UI flow, we are improving and simply rendering the Engineering document at index. This is a quick-fix to buy us some time. 😅
Page /reader/index needs to be restyled to align stylistically with the main reader, otherwise users may be confused into thinking that these are different apps.
What basically has to happen:
- State "DONE" from "TODO" [2022-12-23 Fri 21:36] \\ First prototype delivered by Tijan
When accessing from a mobile device, the table of contents is no longer visible. We need to fix this to provide some ability for a reader to navigate through a text.
The top of the hamburger menu contains an avatar and some placeholders for controls that we may need in the future, we can add a container underneath that displays the ToC.
- State "DONE" from "TODO" [2022-11-25 Fri 10:51] \\ Merged with 08e7c4302406c4753d182a5e6b95e33681820b6d
Within ContentContainer, we need all buttons to show up in a uniform style. Perhaps we accomplish this by defining a ContainerContainer-internal component that has the background and the rounded borders that we can reuse.
Furthermore, I want all buttons to be rendered with about 50% opacity and become full-opacity when hovered over. Just to make them a bit less "in your face".
- State "DONE" from "TODO" [2022-12-06 Tue 07:22] \\ merged with 38065af94065626fe1584f67a47a13c30497d241
The current ToC is quite prominently (almost aggresively) present with the high contract white-on-dark design. Let's
- State "CANCELLED" from "TODO" [2022-11-17 Thu 11:49] \\ Not in need of themes for now. We'll just service a base case and count on folks using extensions such as DarkReader to have control over dark-mode.
- State "DONE" from "TODO" [2022-09-07 Wed 22:09] \\ All handled by @tijan 🏆🥳
When expanding/collapsing the ToC sections, the disclosures are not smoothly animated. The setup seems to be unaware of the changing height of the children and only effects a height-related change when the child elements are removed altogether which creates a bit of a visual shock as the space for the children instantly disappears from one moment to another.
I very likely lack the lingual skills to explains this very clearly, so just have a look in the storybook and then hop into my Google Meet room for us to talk it through for clarification.
comments
💡 I'm an idiot! Whatever animation I currently implemented there is horrible. Especially on the longer list displayed in the index, the ugliness is very pronounced. It all looked decent in the shorter lists in Storybook but now it's clear that we can remove whatever I did there.
It's so ugly it's technically criminal! My "engineering" license should be revoked for this one. 😅 Good thing I don't do this for a living.
- State "CANCELLED" from "TODO" [2023-01-11 Wed 16:38] \\ Not sure what this means now.
Note that every section can contain its own definition of title
- State "DONE" from "TODO" [2022-09-04 Sun 16:21] \\ Just merged to mainline and added some notes in-source tagged to @tijan about restructuring the architecture to optimize DRY-ness of the setup.
- State "DONE" from "TODO" [2022-09-14 Wed 11:40] \\ Already live!
Based on HN input by moeffju, we do readers a service by not displaying non-functional anchors and nudging them into trying to use them and getting lead astray. This is totally on-point so, let's remove all anchors until we sort out the link following.
- State "DONE" from "TODO" [2022-09-25 Sun 21:01] \\ Done by @tijan and already merged. Had something to do with me resolving a merge to use a element instead of divs. 🤦🏿
The TOC is broken in Storybook and this Stack Overflow thread may provide some explanation of the issue at hand. Let's either fix the Storybook or figure out of this case is not testable in Storybook and then just make a note of this issues such that future developers know not to attempt testing click-through inside of Storybook.
- State "DONE" from "TODO" [2022-09-25 Sun 21:03] \\ Mark headline linking as done for now.
Headlines ids can not be randomly generated on a per-session basis (to map between ToC entries and headlines) as these id are not guaranteed to be the same during another session and therefore not shareable.
Bookmarked links need to survive non-destructive diffs (when just new content is added).
If the headline is removed then notify the user that the link has been removed at a given version, timepoint $t$
, and provide an option to navigate to state $t$
to view the headline anyways
Rough outline how we will deal with headline identifiers.
This is the scenario where a headline is completely created within our tool.
⚠️ Any headline created in another tool is not guaranteed to satisfy the characteristics of a headline populated by us so, we have to be graceful in how we handle headlines. 😅
This operation does not mutate the document in any form.
With indexing we keep track of headline identifiers throughout the lifetime of a document. This indexing should enable us to provide guidance to the user when a link is followed to a headline that may no longer be there or may have been modified (for example when a CUSTOMID is defined for a heading that was previously referenced through the id obtained by the heading-to-id conversion logic).
:ID: 904ea339-a66d-4c68-8278-7b80594f9e70
Org implements its own <<<heading-to-id conversion logic>>> that roughly works as follows:
- State "DONE" from "TODO" [2022-09-25 Sun 21:02] \\ Mark link following on headline links as done.
Following links is a core feature of any document. Let's provide some basic implementation. In the long-tail, we will need to keep track of heading references over the lifetime of a document just to ensure that we can provide useful guidance on how to resolve links (for example, inform a user to go back to a previous version of a document).
- State "DONE" from "TODO" [2022-09-23 Fri 17:37] \\ Index can be populated but we are populating it at the end of parsing instead of populating it on an ongoing basis. The reason for this is that I simply couldn't figure it out fast enough. Recursing over a document leaves the problem of sharing some document-global state to verify that human-friendly slugs do not collide. Trying to do this with map/reduce like mechanisms is problematic because the index state is copied along and there is a change that competing branches settle on the same slugs, allowing for collisions. Sleep deprivation and tunnel-vision are almost certainly in the mix here, so after a chat with @stefano we opted for the dead-simple approach of generating unique ids for now and then indexing them post-parse. This sucks because we now have to figure out how to lookup the human-friendly slugs in the rendering step. This will require us to pass some document-local state along to all the render functions. Just ideas. Maybe this is all BS and we'll find a better design after we've enjoyed quality sleep. 🤷🏿♂️
Links such as [[*Headline]]
should basically work and resolve to the first match. So if there are multiple headlines with the same text, the first one is always the one reached by such a link.
We can link to a headline by providing the headline text sans the keyword, statistics cookie and tags. Observe list item 1 in the snippet below that demonstrates a valid link to the headline defined at the bottom of the snippet.
The following links will link to the section below: 1. [[*\[#B\] This is a *bold* heading][link]] works 2. [[*This is a *bold* heading][link]] does not work because the prio is missing Now follows the section that we link into: * TODO [%] [#B] This is a *bold* heading :taga:tagb: Here we are ***** This isn't love, this is destiny One of the previously working links was a trick. It was formatted incorrectly. The link below is simply because it is of a basic headline without any fancy bells or whistles. [[*This isn't love, this is destiny][a link with interesting punctuation]] ***** TODO [#A] COMMENT [100%] This isn't love, this is destiny :a:b:c: The following link is trickier because it basically contains all the bells and whistles that headings can afford. [[*This isn't love, this is destiny][correctly formatted link to self]] ***** TODO [%] [#B] How are you Below is a link to a trick heading. The order of the heading metadata is really important so here we have a link containing the priority metadata because we listed it in the wrong order in relation to the statistics cookie. [[*\[#B\] How are you][wonky link to self because priority is listed before statistics cookie]] ***** TODO [%] [#A] This isn't love, this is destiny :lyric: Here is an example from the tests in the code. The uniorg parser seems to return all of the metadata along with the heading text =[%] [#A] This isn't love, this is destiny= but Org (in Emacs) only returns the escaped priority along with the heading text [[*\[#A\] This isn't love, this is destiny][link]] * TODO [#A] This isn't love, this is destiny :lyric: ***** TODO Populate shareable heading [%] ids [[*Populate shareable heading ids][link]]
- State "DONE" from "WIP" [2022-09-25 Sun 20:37] \\ Done with latest merge to hack (see https://gitlab.com/formation.tools/eng/proto-01/-/merge_requests/88) - State "WIP" from "TODO" [2022-09-23 Fri 17:43] \\ We are currently generating these ids through a helper function which runs through the flat list of headings and produces all slugs that we need. In an ideal world, I imagine populating an index while the document is being transformed (to our internal representation). Fundamentally, I'm torn on whether this information belongs in the internal representation of the headings. Part of me says that slugs, ids and linkTexts belong in the heading while another part says that we should just keep these in indices and provide a fast reference to the data (the heading).
Multiple headings which resolve to the same plaintext, should be numbered such that the id uniqueness constraints remains honored (a given id may only exist once).
We cannot use PRNG-generated heading ids because such ids will be different between sessions.
* C Should be referred to by id =c= * C+ Should be referred to by id =c-1= * C++ Should be referred to by id =c-2= Kind of how GitHub ids README headings.
- State "DONE" from "TODO" [2022-09-23 Fri 17:46] \\ Consider the scrappy part done!
The availability of unique ids for every parsed headline may simplify the implementation of linking. Within a session we can just use the session-scoped unique ids.
These ids will not be stored as data as they are only meant to be used within the intermediate representation to provide us quick references that are "guaranteed" to not collide within a session scope.
With such unique ids, we simplify the implementation of linking as we can just implement a lookup table that points headline links, CUSTOM_ID
links, ID
links or other linking methods to a single reference.
We'll stub a scrappy implementation and will optimize later. 😅
The current implementation is extremely scrappy but reading through the uniorg codebase is seems that uniorg-slug already covers part of this functionality in a much cleaner way.
Linking is a little bit easier now, but we are still lacking some visual aids/cues that can make is considerably easier for readers.
- State "DONE" from "TODO" [2022-09-27 Tue 13:00] \\ @tijan came to the rescue 🏆 with https://gitlab.com/formation.tools/eng/proto-01/-/merge_requests/91
As a user, I want to be able to obtain the link to a heading by simply clicking it or by clicking some paragraph-icon (like ¶) or link icon alongside it (let's start with towards the end of the heading text).
Clicking a headline link shoulf link to itself and update the URL to that specific heading.
comments
Clicking the link icon (whether it is ¶ or something else like the link icon to the left of headings on GitHub README pages) should link to the heading by id.
Currently, I can only obtain this link by through the TOC. This is not very convenient when I am already scrolled to a certain part of the text and want to obtain some context by simply refering back to the ToC.
Link for a heading can be obtained by passing looking up the heading id in the headingIdToSlugIndex
index on the document (type FDocument
). See the TOC, where the lookup is being performed.
- State "DONE" from "TODO" [2022-09-28 Wed 21:45] \\ Merging with 4d5dcbc3542b254fcff861ff5f14e1631202b13c
When we navigate to a given heading through clicking an entry in the TOC or by following a link such as DOMAIN.TLD/PAGE/#heading-id
, where heading-id
is the heading id, we want the active id to be highlighted in the view such that it is easier for the user to spot it.
Sometimes entries highlighted in the previous task are hidden because the parent may be collapsed. As a user, I want all parents highlighted until the topmost that isn't folded away. This should allow us to follow the trace when we are unfolding the ToC tree to find our intended entry.
💡 It would be an idea to automatically unfold the tree to show the highlighted entry but this may be too disruptive if the user has folded the tree into a particular view for better overview. The idea of tracing the parents by higlighting them would be less disruptive to a reader's workflow.
Upon sorting out a headline linking strategy, we can implement functioning anchors or buttons wherever we have links. The links listed in the document and in the TOC would need to be updated to facilitate linking.
- State "DONE" from "IN_TEST" [2022-12-29 Thu 12:20] \\ Thanks to Tijan 👑 - State "IN_TEST" from "TODO" [2022-12-29 Thu 11:20] \\ Fixed with 90fde61f19338ca44b6c626b93854e36892304e2 in vidbina/fix-dropped-path-for-links-in-non-std-spaces
When visiting links on non-standard (not our dev board) spaces, the path is dropped from the heading-level links/anchors. So instead of going to DOMAIN.TLD/PATH#ID
the anchors navigate to DOMAIN.TLD/#ID
which is killing the usability on non-default pages.
- State "DONE" from "TODO" [2023-01-06 Fri 14:22] \\ Merged into mainline with a post-it style highlight on the section and ToC entry. We still need to "unwap" nested sections when they are folded to make some sections visible.
When following links, it isn't always clear where the reader is to focus their attention. Highlighting the ToC section along with the Prose section in a brief flash or something of that sort may solve this UX issue.
Observe Highlight all parent ToC entries which should be the step before we can unwrap sections on an as-needed basis.
- State "DONE" from "TODO" [2022-09-23 Fri 17:47] \\ The TOC now links through to the unique ids that we generate with the help of =nanoid= at the moment.
As an example for the later implementations.
:COOKIE_DATA: checkbox recursive
Internal document links in Org can refer to a headline by
Implement the indexing logic to facilitate heading lookup by the tokens mentioned above.
:COOKIE_DATA: checkbox recursive
The headline links in Org texts are actually called fuzzy links and they fuzzy link values are derivatives of actual heading text.
The following points are copied from the subpoints below:
Where in the Org-mode codebase is this implemented? Link to the source for future's reference.
Observe that headline fuzzy links are formatted as follows: Verify how [this] heading with an asterisk* links*, where:
Which means that we need to keep these changes in mind when indexing fuzzy links for headings.
Observe that headline fuzzy links are formatted as follows: Verify how heading with cookie links 🍪
Link following in Prose view requires an index to be populated which is a blocking task.
Upon navigating to a headline, we want a breadcrumb to indicate the level at which the document is being observed. This breadcrumb view can be used as a method of narrowing the Kanban view as well.
Images in the document need to be rendered correctly. We have no images in this document yet to demonstrate this but this task is here to expand on this point.
Evaluated auth libraries or services like Auth0, NextAuth, Clerk.dev and ory.sh with the end-goal of optimizing for best ease of use and minimal ops/management load.
As per , we're doubling down on ory.sh after trying NextAuth and realizing that it limits us to doing everything in NextJS. Furthermore, NextAuth allows for little control over the user journey which may trigger a need for reimplement soon in order provide a clearer UX and such a reimplementation would almost certaintly require us migrate NextAuth data.
- State "DONE" from "TODO" [2023-01-06 Fri 14:41] \\ Fixed with 0d3acdf1fc4e1244207f56536205666bf23055ea
I've added some UI for Auth (see AuthControl.tsx
) that proposes a Popover that is not correctly positioned
comments
it appears under the button that invokes it and is aways center-aligned in relation to this button which becomes an issue when the button is right-aligned. The popover now gets partially rendered off-screen.
This doesn't have to be a popover and could also be a sidebar like you used in the About modal.
Your idea on implementing this is likely considerably better that whathever the monstrosity is that I cooked up in my effort to move fast. 😅
- State "CANCELLED" from "IN_SCOPING" [2022-12-09 Fri 02:30] \\ We're focusing on ory.sh for now, which provides a batteries-included kind of path.
Updated the general scope of the Auth concern such that we don't have a large persistence burden on our-end since we are using a managed service for auth.
- State "DONE" from "IN_DEV" [2023-01-06 Fri 14:43] \\ Handled with Supabase Auth for now and already enabled
- State "DONE" from "IN_DEV" [2023-01-06 Fri 14:43] \\ Handled with Supabase auth and already enabled
- State "CANCELLED" from "IN_DEV" [2023-01-06 Fri 14:43] \\ To be handled with Supabase Auth, will be a matter of configuration. Implementation is there but configuration is cancelled until we need it.
We first need to made a decision on how we deal with non-git data.
- State "CANCELLED" from "TODO" [2022-12-09 Fri 02:30] \\ Using ory.sh for the reasons mentioned previously
- State "CANCELLED" from "TODO" [2022-12-09 Fri 02:31] \\ Using ory.sh for the reasons mentioned previously
- State "CANCELLED" from "TODO" [2022-12-09 Fri 02:31] \\ Using ory.sh for the reasons mentioned previously
- State "CANCELLED" from "TODO" [2023-01-06 Fri 14:44] \\ Not needed for early stage, probably more of an enterprise thing. Just a fun idea that adds no value for now.
As a user, I want to be able to login with my GitHub account or my Google account and see the same data if I use the GitHub account for the same org as my Google account. For this to work, I need to be able to associate multiple ids from various IdPs to the same invididual.
- State "CANCELLED" from "IN_SCOPING" [2023-01-06 Fri 14:53] \\ Cancelling to revisit when we really start to deal with forge integrations and determine that this is a must-have.
With spaces we initially meant a "space" as a container for files in a given context like an organization, a department or perhaps even a "mindspace" or "headspace" like "things to do in BKK in 2022" or "prompt engineering".
With login with GitLab or GitHub capabilities it is unclear if we
I'm (personally) leaning towards the latter but this may break the everything-in-git model. For simplicity's sake, we should opt for the first approach and probably drop the notion of spaces altogether unless an end-user wants to see their multiple forge orgs within the same session.
Just grabbed coffee with Marijn Haverbeke from ProseMirror and CodeMirror to explore how to approach this problem and whether to focus on collaborative editing right away.
ProseMirror offers OT-based collaborative editing. See https://marijnhaverbeke.nl/blog/collaborative-editing.html and https://prosemirror.net/docs/guide/#collab for relevant background information.
💡 We can implement authoring/editing without auth or persistence by just allowing folks to copy/paste the source. Really bad UX but I'm just saying that we can implement the UX side of this experience without blocking it with back-end-ish concerns (auth and persistence/storage).
For collaborative editing (multiplayer mode) we will need to offer a storage overlay that facilitates ephemeral data (like collab session interactions) until these are solidified enough to convert into a change request/MR/PR by way a git commit.
Some of the options in consideration are replicache, liveblocks and Yjs (which is P2P but may still require some information overlay to coordinate session discovery).
:ID: 542046cc-0783-4be1-bf6c-7a592d6e96e3
comments
⚠️ This task is oddly unspecific. The core point here is that there are settings encoded in a document body that we need to honor in our representation efforts. This means that we have to extract our parsing configurations from the document text (since some configuration is embedded therein) which almost certainly will require multiple passes in order to acurately parse/process a document. Eventually, we can cache these values to minimize the need to parse things immediately and on the fly.
The defaultOptions
for the uniorg parser sets todoKeywords
to TODO
and DONE
(see src) which is in line with Org's defaults. Custom TODO keywords that are defined through the TODO
keyword or its variants TYP_TODO
are not being honored and thus these keywords are parsed as "just text".
Examine how to make the parser options region dependent such that we can parse differently within limited scopes (for example parsing with certain keywords known for the scope of a section where these keywords are defined) how to implement honoring of TODO keywords that are defined to be file-local.
comments
💡 Perhaps we can study the streaming API of uniorg to figure out to which extend we can modify the options data structure and whether this mechanism implements some stack such that we can pop out of a given context in order to continue parsing of a document.
Streaming API won't help here as we need to sculpt/adapt/shape the document as we obtain more information. The streaming API may be helpful for the large file parsing timeout point.
See https://github.com/rasendubi/uniorg/issues/53 with the following response to our issue:
Definitely in the scope of the project. This is also closely related to =#+OPTIONS= as they also influence how file is parsed. The main challenge with these is that they can occur anywhere in the file. Options or todos might be specified on the last line of the file and they should still affect all the lines above. (In emacs, I believe they set buffer-local variables and re-fontify. That's why you have to C-c C-c when you change options line.) The easiest way to implement this in uniorg is to parse file twice. First time to extract keywords, and second—with options configured from the first parse.
- State "DONE" from "TODO" [2022-10-15 Sat 15:42] \\ Already implemented.
Disclaimer: This is a sloppy solution but will definitely work for now. Let's just get this to work reliably even if slow and shittily implemented. 💩
I already considered just parsing $n$
lines prior to parsing the full document in order to minimize the workload and keep things "responsive" but this is not the prime objective at the moment.
Keywords that we need to check for TODO sequences are:
See https://orgmode.org/worg/dev/org-syntax.html#Keywords for the definition of keyword. We need to parse
Note that defaultOptions in parse-options.ts defines TODO and DONE as the default values for todoKeywords
. According to a code-base grep, the only place to change the defaultOptions seems to be in the constructor of the Parser in parser.ts. The implementation in parser.ts doesn't seem to allow for updating of options
during parsing.
Currently many blocks remain unrendered. This should be resolved by rendering all block-like components in a Fallback component as a interim quick-fix.
We contributed to this feature but haven't bothered to update yet. In order to display task progress, we'll need it so let's get on with it.
As a user, I want some visual indicator of how far along something is.
Progress indicator has various modes:
As a user, I want progress color-coded to provide a quick sense of where things are.
In Emacs, completed or terminated tasks are rendered in green while ongoing tasks are rendered in red. This is sometimes a source of confusion because some may associate red with stopped and green with go given the usage of these colors in traffic lights and other problems (see Show Heading progress) to provide a more fool-proof solution here -- something that is less prone to misunderstanding.
We want something that is instantly clear with just a split-second glance. Perhaps we need to rely on a combination of color and shapes.
We probably want to gradually alter the color a WorkflowState in a workflow according to some gradient. Not clear what to do here yet. Ask design for input.
💡 See ~WorkflowStateColor~ in renderer.
Terminating states are easy because we parse them into our data structure into workflows
with the isActive
property set to false.
Starting states are a bit trickier, but we can start by assuming that the first non-terminating state in a workflow are the starting state.
When multiple workflows are defined, always take the first of each workflow to indicate a starting state. So we should be able to support multiple starting states this way.
:COOKIE_DATA: todo recursive
- State "CANCELLED" from "IN_DEV" [2023-01-06 Fri 14:51] \\ Unclear how critical this is. User can use multiple windows if this is such a big deal for them.
As a reader, I may sometimes want to have content juxtaposed to simplify reading or x-ref-ing. Perhaps I want two instances of Prose (scrolled to different locations) side-by-side, perhaps I want a Prose view shown next to a Calendar view.
We need the basic mechanisms in place to facilitate this for experimentation.
- State "DONE" from "TODO" [2022-11-23 Wed 06:38] \\ Been implemented using ResizablePane
As a user, I want to be able to resize a pane to fit my needs.
The pane should be resizable in the following modes:
When dragging we want to have clear borders visibly to guide the end-user in understanding what the frame is that is being dragged.
Our first implementation should just render an empty Pane. Don't worry about the content for now.
- State "DONE" from "TODO" [2022-11-23 Wed 06:38] \\ Done through introduction of ContentContainer
As a user, I want to be able to view content inside of a Pane and potentially be able to study multiple instances of a given perspective side-by-side.
Currently the control of perspective is global (in the navbar) and in order to do panes well, we want to delegate this control to the Pane level such that viewers of multiple panes can control the perspective of each pane within that pane itself.
comments
If we don't provide pane-local perspective control, we will have to provide users to mechanism to control which pane they refer to when switching perspective in a global (in the top navbar) control. This will already confuse us as users, let alone another user.
- State "CANCELLED" from "IN_SCOPING" [2023-01-06 Fri 14:50] \\ Was an early brain-fart, and it is not clear if we will really need this since people can split their windows as well if this is so important to them. 🤷🏿♂️
Earliest brainfart: Provide split-horizonally and split-vertically buttons somewhere in the pane to allow the user to split the pane how they want to. From that part on users can use the pane-local perspective controls to manage that pane.
⚠️ Focus on desktop experience.
We are going for a Pane-oriented approach in usage where users can click through a document and open successive panes of different types. Furthermore, users should be able to switch the type of a pane from, for example, from source to prose to calendar to kanban.
This task is the starting point to providing a base container that can be used to facilitate the composition of arbitrary panes. The user will be able to inform/control which panes are in view based on the manner in which they step through (or explore) a document.
See https://app.excalidraw.com/s/AoDvHfNWirc/4SswRbCtpkN for some sketches on the overal flow. These have been roughly explained to @jessy, @stefano and @tijan meanwhile in case you're looking for someone who can help clarify these points.
comments
A tool such as Glamorous Toolkit allows for a pane-oriented workflow that comes close to that which we are also thinking about.
We are not incorporating the ToC sidebar inside of the ContentContainer as @jessy clarified this to be an antipattern and we should just opt for a single global ToC sidebar which is alway highlighting the active entry in the pane currently in focus.
@Jessy explained pane-scoped ToC sidebars to be an antipattern so we have to make sure that the developers working on this know to not spend energy on this bit. Furthermore, task following from Define Implement spy on content to highlight active ToC entry will tackle the implementation of the ToC sidebar highlighting capability that is refered to here.
The ContentContainer should not occupy itself with such concerns. 🧐
- State "DONE" from "WIP" [2022-12-30 Fri 12:38] \\ Tijan delivered this one and it needs to be integrated.
As a author, I want to be able to edit the Org or Markdown source of the document in a non-WYSIWYG manner for optimal control.
This is a cheatcode to not have to built in the WYSIWYG editing capability from the start.
The usage mode here is comparable to the HedgeDoc https://demo.hedgedoc.org/.
Within the source perspective, we want Org syntax to be highlighted correctly. We may have to write an extension (or whatever the name is for some extension to the code editors native capabilities) to cover this.
As a user, I want to be able to switch to Source Perspective as one of the perspective candidates so I can eventually examine and edit the source of the document.
We're talking prototype level so we don't worry about optimization issues such as debouncing.
Since we can't provide a sub-100ms parse/render cycle (yet), we shouldn't run our heavy multi-sec blocking parse/render cycle on every keypress. We can do either of the following:
Just fuming ideas here and I think idea 1 is easiest so let's do that one first and then think about idea 2.
Looking at HedgeDoc's editorpane (which also uses React CodeMirror), we probably should be using some extension to handle changes (instead of stubbing onChange
handlers directly). This is probably not affecting performance much but rather an architecture/design concern.
I'm unclear on how changes are fed back into the rich-text output. From a cursory glance of the Chrome DevTools Performance flame chart recorded during input, it appears that we parse (input) and render (to HTML) upon keyup. This still feels very snappy in HedgeDoc and they kind of do the whole parse/render-to-HTML dance upon keyUp as well, so we'd have to figure out if they a full parse/render cycle or if this is incremental parsing at work.
From my recollection of CodeMirror-related tooling, my guess is that HedgeDoc uses incremental parsing (the Lezer parser is a staple in CodeMirror-land). This can be the reason why HedgeDoc feels snappy. Uniorg doesn't do incremental parsing which is part of the reason why any update may feel rather expensive.
💥 Just did a quick check and any of our parse
calls may take north of 1 second, whereas v.parse
in the HedgeDoc app take only 4.92 ms. In our case, performance is even more degraded as we parse twice.
- State "DONE" from "TODO" [2023-01-03 Tue 16:32]
- State "DONE" from "TODO" [2023-01-03 Tue 16:31] \\ Already in mainline
Implement a section component that uses something like headless's Disclosure to make the entire section foldable.
- State "DONE" from "TODO" [2023-01-03 Tue 16:31] \\ Completed
Currently the renderer doesn't render anything for a section (we just return an empty list). We may need to think through what rendering sections will "mean" since there are a lot of nested sections in a doc, so once we start rendering sections we should expect some fireworks. 🎆
Let's not worry about the explosions for now and just get this implemented! Once it's there it'll be my chore to figure out how to clean things up when merging the changes.
- State "DONE" from "TODO" [2023-01-03 Tue 16:32] \\ Merged in mainline already
Minimize the noise in the docyment by folding all sections that are marked as DONE
, same as Fold completed headings in ToC, above.
- State "DONE" from "TODO" [2022-09-27 Tue 13:04] \\ Implemented by @tijan with https://gitlab.com/formation.tools/eng/proto-01/-/merge_requests/92
Minimize the noise in the ToC by folding all the headings that are marked as DONE
(todoKeyword
from type FHeading
).
- State "DONE" from "TODO" [2023-01-05 Thu 21:49] \\ Merged with c4d748e4df2c290b9cb338bab5fd0e6cc7f42142
Mostly following the comments idea as discussing in the chat https://app.excalidraw.com/s/AoDvHfNWirc/2ogauhhMKj5
- State "DONE" from "TODO" [2023-01-05 Thu 21:20] \\ Closing comments implementation for now
- State "DONE" from "TODO" [2022-09-25 Sun 20:57] \\ Block comments are already being rendered albeit not in an optimal presentation.
I know what this means but essentially I want to be able to parse comments that are formatted as follows:
#+begin_comment This is a comment #+end_comment
and appears as:
comments
This is a comment
- State "DONE" from "TODO" [2022-09-25 Sun 21:10] \\ Just verified that this works!
Readers need to be able to read inline comments such as the comment below:
comments
This is an inline comment and should be visible on the page!
- State "DONE" from "IN_TEST" [2022-10-15 Sat 21:48] \\ Tested and integrated with a90309f4c014dfe6838838a844be1668249620db
A comment component should open up as a drawer. Recorded a video (on YouTube) to demonstrate the capability.
See Figma frames comment open and comment closed for a design candidate from @siarhei (our designer).
My thinking is that a third pane (to the right), like an aside could function like a side-notes/aside/footnotes section where we can present the comments (however long they are). This would keep the flow of the main text clean. In this case, we would just show some line in the main Prose view to indicate that there is some content hidden away and then the user can click on some icon on that line (like the plus or expand icon) to open the full comment block in the third and right-most pane -- the aside.
- State "DONE" from "IN_TEST" [2023-01-05 Thu 21:19] \\ Already in mainline for a while now and cleaned up as per 90ad707327877f3fab6aef7bf89f8818d8a39d42
Take the component that @tijan developed and wire it up to replace the FallbackBlocks where we need comments displayed.
- State "DONE" from "TODO" [2023-01-03 Tue 16:32] \\ In mainline already (by Tijan)
Currently all comments are rendered in individual blocks, but we really want multiple comments to be presented under a single disclosure that can render multiple comments when expanded.
- State "CANCELLED" from "TODO" [2023-01-05 Thu 21:20] \\ Latest implementation 90ad707327877f3fab6aef7bf89f8818d8a39d42 involved a right-aligned comment icon which lacks a title text so this point is hereby rendered moot
The left-most expand/collapse icon is rather tiny and annoying to hit when in a hurry. As a user, I want to be able to quickly expand/collapse a comment block by clicking on the title too.
- State "DONE" from "TODO" [2022-09-29 Thu 16:42] \\ Implemented by rendering source in a fallback component since https://gitlab.com/formation.tools/eng/proto-01/-/merge_requests/96. Future work will be to render this in a slicker code component. Battle for another day tho.
As a reader, I want to see the contents of source blocks presented as code. We don't expect any highlighting or any nifty features but we simply want to be able to read the contents of the code blocks.
We should be able to read the source block in section Parse block comments which is currently hidden.
The following code block should be parsed and displayed correctly (open as plaintext just to see what is in the source block).
#+begin_src txt This is a code-block inside of a an Org source block #+end_src
describe('source block', () => { const dut = (x) => parse(x).content[0] it('parses', () => { const raw = ` #+begin_src txt ....... . . . ....... #+end_src ` expect(dut(raw)).toMatchInlineSnapshot(` { "content": "....... . . . ....... ", "type": "{", } `) }) })
- State "DONE" from "TODO" [2022-09-15 Thu 12:53] \\ Looking good and merged into hack [2022-09-15 Thu 12:53].
- State "DONE" from "TODO" [2022-09-29 Thu 16:53] \\ Wrote this down and realized the table renders in a fallback-like style. Good enough for now.
We can't wait forever on this one, so meanwhile we need a way to render the following table in a fallback component and then touch it up into a slicker representation at a later point:
key | value |
name | formation.tools |
type | devtool |
stage | prototype |
current niche | that Emacs crowd |
eventual goal |
- State "DONE" from "WIP" [2023-01-11 Wed 16:35] \\ Merged with 4ed244fa0f0e6abbe12cd627d57569eb8bed2d3d
In order to display tables, we will need to parse them first. 😅 Right now the underlying datastructure is completely unclear (although I do have some ideas). Implementing the parsing of tables will inform which attributes and properties we need to keep in consideration.
Refer to previous task with example table for some example data to work with.
Here is an example table that lists some berries -- yes berries 🍓.
Name | German name | Calories | Carbohydrates (grams) | Protein (grams) | Fat (grams) | Fiber | Sodium (grams) | Potassium (grams) |
Avocado | Avocado | 240 | 13 | 3 | 22 | 10 | .011 | |
Banana | Banane | 110 | 28 | 1 | 0 | 3 | .450 | |
Pumpkin | Kurbis | 137 | 7 | 1 | 0.1 | 7 | 1 | 0.340 |
Cucumber | Gurke | 8 |
- State "DONE" from "TODO" [2023-01-11 Wed 16:36] \\ Merged with 4ed244fa0f0e6abbe12cd627d57569eb8bed2d3d
- State "DONE" from "IN_TEST" [2023-01-19 Thu 15:47] \\ Was altered along with tijan/table-pane-functionality but probably in mainline before that already
Now that tables are implemented, you'll notice that tables are sometimes to long and too wide. Limit the table height in the same way we limit a code block's height such that it never completely takes over the screen.
Here is a large table to test with:
Solution | Plaintext | Offline | Collaboration | Git | Prose | Productivity | Automation | License | Killer feature | Total Score | Missing feature | Comment |
Amie | ||||||||||||
AFFiNE | 1 | 1 | 1 | 1 | MIT | block-like as in Notion | ||||||
Quatro | 1 | GPL2 | ||||||||||
Outline | 1 | BUSL 1.1 | ||||||||||
Acreom | 1 | 1 | 0 | 1 | 1 | |||||||
Archbee | 0 | 1 | 0 | Org-capabilities (tasks, planning, productivity) | ||||||||
Notion | 0 | 1 | mobile-firstness | openness (proprietary formats, complex API with block as primitive entity) | block-based, team-focussed but many don't use it in teams, bought cron | |||||||
Confluence | 0 | 1 | Jira | openness (API is horrible, centralized) | ||||||||
Fibery | ||||||||||||
Org Mode (in Emacs) | 1 | 1 | 1 | Emacs | inaccessible to non-Emacs users, too flexible when you start tinkering with elisp | |||||||
Etherpad | 1 | 0 | 1 | 0 | 1 | 0 | Apache 2.0 | |||||
LogSeq | 1 | 1 | 1 | 0 | 💡 Possible base for a non-Emacs client | |||||||
Workflowy | 0 | |||||||||||
TiddlyWiki | ||||||||||||
HedgeDoc | 1 | 1 | ||||||||||
Linear | ||||||||||||
Obsidian | 1 | 1 | 💡 Possible base for a non-Emacs client | |||||||||
Roam Research | 1 | 1 | ||||||||||
ClickUp | ||||||||||||
RemNote | 1 | 1 | ||||||||||
Jira | 0 | 1 | ||||||||||
Coda | 1 | |||||||||||
Thymer | 1 | |||||||||||
Clover | 1 | |||||||||||
Basecamp | ||||||||||||
Organice | ||||||||||||
Orgzly | ||||||||||||
GitHub Projects | ||||||||||||
GitLab Projects | ||||||||||||
GitBook | ||||||||||||
Pivotal Tracker | ||||||||||||
Open Project | ||||||||||||
Shortcut | ||||||||||||
Awork | ||||||||||||
Notesnook (see HN) | ||||||||||||
Tana | ||||||||||||
Tyles | ||||||||||||
Vistaly | ||||||||||||
Standard Notes | ||||||||||||
iA Writer | ||||||||||||
zube.io | ||||||||||||
spreadsheet.com | found about from @ImpostorEngineer mid-stream | |||||||||||
OneNote | ||||||||||||
Totango (CS tool) |
As a user, I want to be able to expand a table to fill my entire working area. In this setup the table would assume as much space as the Kanban board assumes.
We need to provide some mechanism to click a "arrows-pointing-out" (heroicons) button that the end user can close again to return to the Prose view.
When pressing expand, we need to present a new pane (to the right of the current pane in which the item resides which we want to expand). This new pane should be closable.
- State "DONE" from "TODO" [2022-09-14 Wed 11:15] \\ Already implemented and live.
The current ToC is looking very cluttered. Let's show the TODO tag, the headline text (for majority of the width of the ToC pane) and then the tags. I'm also open to dropping the tags from the TOC just to keep things cleaner because I believe that this is very likely just too much information.
- State "CANCELLED" from "TODO" [2023-01-06 Fri 14:49] \\ This is a very old point that is moot at the moment since we have different abstractions in place.
Grep codebase for @tijan
and you'll find some notes on our Tailwind usage and DRY-ness (DRY as in "don't repeat yourself"). Let's stub an InlineContainer and a BlockContainer which we can reuse without having to redefine borders, shadows, background colors and basic typography.
Spoke with @tijan about this and we can look at extracting classes the Tailwind way https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply to minimize duplication.
For now we have a Block
element that also exposes blockClasses
in case we need block-like styling anywhere but this point is still outstanding for refactoring.
- State "DONE" from "TODO" [2022-11-25 Fri 10:50] \\ Just merged with fc45bdaeff3ba100964e8b5ab08ab54da07a34a7
Currently the entire Kanban view is y-scrolled as a whole but we want end-user to scroll individual columns for better control. Especially alter when we will want to facilitate DnD.
This probably requires some table-like approach in order to produce proper column spanning wrappers.
- State "DONE" from "TODO" [2022-09-07 Wed 21:53] \\ More or less done, albeit scrappy 😅
- State "DONE" from "IN_TEST" [2023-01-03 Tue 16:43] - State "IN_TEST" from "TODO" [2022-12-23 Fri 21:35] \\ Merged into mainline after a fix by Tijan. Just moving to test as I'd like to give it some time to see what emerges. Any checks conducted so far suggest that both typography and disc/bullet weirdness are resolved now.
Text is lists is typeset differently to the main text which should not be the case. All text in lists should be rendered in the same size as other prose.
Perhaps there is a text-sm
or something of the sort somewhere in the tree of the list that needs removing.
Furthermore, the discs in lists are broken in the mobile view. The alignment of the bullet or disc seems to move vertically as a list is expanded and collapsed.
- State "DONE" from "IN_DEV" [2023-01-06 Fri 15:09] \\ Merged with 4b53d958950d1c231fac33b9aff155202f05d1dc
Currently lists are not displaying checkboxes. Here is a snippet to test things with in case you want to use it in a Storybook or so. Mind you that FListItem supports the following values for checkbox
: on
, off
and trans
which is what you'll need to test this.
- [ ] this should start with a checkbox - this should not start with a checkbox - we can also do this with nested lists - [ ] with - without - [X] with and checked - [-] with and in trans mode + [-] funny :: what did the hacker say to the librarian?
See branch vidbina.prep/stefano/display_checkboxes
where I have already retrofitted ListItem to be checkbox aware so you don't have to worry about the parser/renderer logic behind the scenes.
Not sure if transitive is what trans
actually stands for but let's just pretend.
The snippet:
- [X] done - [-] partially done - [ ] not because the first child is marked done - [X] but because the second one is - [ ] not done - [X] a task item with children is fully done when - [X] all - [X] of - [X] its - [X] children - [X] are - [X] done - luckily we don't have so sort out that case in the front-end. The parser should figure out how to calculate the trans state of a task with multiple children
will produce the following list (which should render correctly once the implementation is complete):
Note that the trans state is informed by the state of the children tasks with the following cases:
Here is some documentation on the indeterminate state available in standard checkboxes https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#indeterminate_state_checkboxes. Let this be our guide.
comments
Tailwind does provides some styling for indeterminate states which I have posted in a Slack thread as https://asabina.slack.com/archives/C036C60HWQP/p1672774272401549?thread_ts=1672699828.406419&cid=C036C60HWQP
- State "DONE" from "WIP" [2023-02-13 Mon 17:40] \\ Already implemented by Stefano and merged for a while so we consider this sorted.
Definitions in definition lists are not rendered atm. The following snippet:
- Apple Computer :: a computer company - Apple Records :: record label - Grannie Smith :: green apple
Should render a definition list (see mdn) when this task is completed.
Let's start with a single line per entry where the term is either boldfaced or wrapped in a light-gray button-like frame and the definition itself is just regular text.
The following list types are supported:
The order in which we're implementing them are:
Where:
If all tasks are completed, we should be able to look at this task and see all the lists correctly rendered as is to be expected.
The raw text of the task is listed below such that you can at least read the task even while some parts of the content are not yet implemented in formation.tools:
The following list types are supported: - unordered - ordered - definition list The order in which we're implementing them are: 1. unordered 2. definition list 3. ordered Where: - unordered lists :: lists with bullets - ordered lists :: lists that are numbered - definition list :: list like there where /terms/ are paired/matched with a /definition/ If all tasks are completed, we should be able to look at this task and see all the lists correctly rendered as is to be expected.
- State "DONE" from "WIP" [2023-02-13 Mon 17:42]
Looking at Display checkboxes in lists, we still need to parse tags inside of list items. Not sure if we already have support for this in uniorg or if we'll have to implement it and post a PR. 🤷🏿♂️
See https://orgmode.org/worg/dev/org-syntax.html#Items for more information.
- State "DONE" from "TODO" [2022-09-05 Mon 22:33] \\ Implemented with vidbina/parse-lists, ready for @tijan to work on the component.
List can be parsed as follows:
import parse from core/parser' parse(rawText).content[0]
where you can use the following raw text samples:
💡 All of the previously listed samples are verified to parse correctly in the parser spec (core/parser.test.ts).
- State "DONE" from "TODO" [2022-09-14 Wed 11:42] \\ Merged into mainline but the question of folding/unfolding lists is yet to be solved.
In order to render lists correctly, we need to stub out a List component that can handle the inputs demonstated in Implement list parsing.
- State "DONE" from "TODO" [2022-12-07 Wed 09:30] \\ Merged with b3f857e2a3a319b2014c4a2677e0ba5733259504
Use a Disclosure to facilitate this. As Disclosures have been used in multiple places throughout the codebase, grepping for those examples may provide clues on how to implement this.
- State "DONE" from "TODO" [2022-12-07 Wed 16:57] \\ Resolved by using headlessui's Disclosure
Lists crop their content which is very likely to be attributed to the way CSS transitions are implemented.
export function ListChild({ label, children, }: PropsWithChildren<ListChildProps>) { const [showChildren, setShowChildren] = useState(true) const animation = '[&>div]:overflow-hidden [&>div]:transition-[max-height] [&>div]:duration-100 [&>div]:ease-in-out' const itemClasses = [ 'relative h-fit', showChildren ? `[&>div]:max-h-screen ${animation}` : `[&>div]:max-h-0 ${animation} [&>p]:hidden [&>p:first-child]:block`, //... ] }
In order to determine which sections to fold by default, we need to figure out what the terminal workflow states for a document are. This helper should default to treating DONE and CANCELED are defaults if a document does not register any workflows in order to capture the terminals that are defined by org-elements as default endingin workflow states.
- State "DONE" from "IN_DEV" [2023-01-03 Tue 16:44] - Note taken on [2022-09-05 Mon 22:30] \\ Just wired up the home page to display the Engineering doc with branch vidbina/wire-up-layouts. Consider this a babystep to the end-goal.
Page reader[url] will be using the NarrowSidebar which is already demonstrated in a story for reference's sake. The following inputs are needed for the new layout:
:COOKIE_DATA: todo recursive
As a developer, I sometimes catch snapshot breakage when I'm trying to work on a new feature and this should be caught much earlier by the build pipelines.
Failures in test should not be discovered by developers but should be discovered by the build pipelines.
⚠️ The current =hack= branch now contains a gitlab CI pipeline that runs in parallel to the external Vercel job. Let's figure out if we: 1. delay the Vercel job until the test stage in the GitLab CI pipeline passes. That way we don't waste compute on Vercel if the test pipeline fails and we block deploying code that has failing tests. I don't know if this is possible since this job is an external one and perhaps it is hard to influence it. 2. If idea 1 is too tricky, we can setup the Vercel token inside of Gitlab CI and build and deploy the app directly from GitLab CI (as opposed to from Vercel through an external job). Open to ideas here though especially compelling cons for either of the approaches.
As a team, we need to have some tooling in place for E2E testing. Better to set this up sooner rather than later such that we can already start stubbing the first E2E cases to minimize the cost of manual click-through during development.
The first test case, should complete the following steps:
As devs, we want E2E tests to automatically get run in a pipeline. Ideally, we have a way to obtain visual feedback from the pipeline as well.
Not sure if the GitLab CI runners are enough for this or if there is a service where we can run these test workloads (also on Safari, which would require a macOS environment which I think GitLab also offers so my gut tells me that GitLab CI should be up to the task). Perhaps we may have to opt for something like BrowserStack or https://www.lambdatest.com/ (see https://www.lambdatest.com/support/docs/playwright-tests-in-ci-cd/ for the CI/CD part and https://www.lambdatest.com/support/docs/gitlab-ci-integration-with-lambdatest/ for the Gitlab CI bit).
If we can extend GitLab CI to run the E2E suite let's do so! Otherwise, we need to get accounts setup at the service to try and adapt the GitLab CI pipeline to get this to work (if adaptation of the pipeline is even necessary).
Explore the different options (perhaps even my testing the tooling on test repos) to help us figure out how to best set either of these up for this project.
Setup Codecov for project because it is a FLOSS-friendly option so we'd happily give them our money.
See https://docs.codecov.com/docs/gitlab-tutorial for implementation details
The https://app.codecov.io/gitlab/formation.tools:eng/proto-01 page is not listing our coverage (it is only listed by going to commit-specific pages). My hunch is that codecov is having some trouble cataloging our coverage data because of our merging workflow which heavily uses fast-forward merges.
See @vidbina Tweet to @codecov for a different phrasing of the issue as I observed it then.
We have a main
branch which is deploys to production and a hack
branch which deploys to staging.
We merge feature branches to hack
(staging) and when this is done in some forge like GitLab (in our case) or GitHub, it a non-fast-forward merge into hack
is made which will trigger a deployment to staging.
We maintain a single line between hack
(staging) and main
(production) so main
is trailing hack until we decided that we can fast-forward its position. This means that we don't have unique revs for every event but often find our hack
and main
branches sharing the exact same point on the graph when everything is caught up.
Here is a visual breakdown:
main hack | | mainline o--o--o--o \ feature o----o <- HEAD of feature branch
Such a merge results into the following situation (note how the feature branch was merged into hack
).
main hack | | mainline o--o--o--o--------o-- \ / feature o----o
On a day-to-day basis, however; I don't always use GitLab's merge button to trigger merges but I may, start a WIP
branch on my machine (actually called vidbina/merge.hack
for me as I prefix all my merge-related branches with vidbina/merge.
but WIP
is shorter for the purpose of this description) starting from the upstream hack
and then perform a non-fast-forward merge (like the forges do) locally. This workflow allows me to resolve conflicts more conveniently and run some tests/experiments thereafter.
I may throwaway these local WIP
branches if I mess things up or want to approach the merge slightly differently.
main hack WIP | | | mainline o--o--o--o--------o-- \ / feature o----o
Anyways, after confirming that the results are okay, I fast-foward merge (using git merge --ff
) WIP
into hack
such that they share the same rev and then I push hack
upstream.
hack main WIP | | mainline o--o--o--o--------o-- \ / feature o----o
After the staging build has been out there without too much trouble, we catch up main
through a fast-forward merge.
main hack WIP | mainline o--o--o--o--------o-- \ / feature o----o
Once the task is done and considering that the merging workflow write-up is not too shabby, we should file it were devs can reference it more easily (like in the proto repo itself).
The storybook at http://localhost:6006/?path=/story/modes-board-organisms-kanbanspace--mock-board does not update when the text is updated unless one navigates away from the story and back into it.
See the following snippet:
╭──────────────────────────────────────────────────────────────────────────────────────────╮ │ │ │ Storybook 6.5.10 for React started │ │ 19 s for manager and 21 s for preview │ │ │ │ Local: http://localhost:6006/ │ │ On your network: http://192.168.228.210:6006/ │ │ │ │ A new version (6.5.12) is available! │ │ │ │ Upgrade now: npx storybook@latest upgrade │ │ │ │ Read full changelog: https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md │ │ │ ╰──────────────────────────────────────────────────────────────────────────────────────────╯
- State "DONE" from "TODO" [2022-09-29 Thu 16:40] \\ Just merged into Hack as part of wrapping up https://staging.formation.tools/#implement-source-blocks (https://gitlab.com/formation.tools/eng/proto-01/-/merge_requests/96)
The use of base64encode
Causes Buffer
-related errors within storybooks and other more limited environments (iirc unit tests may be struggling with this as well but I could be wrong here) than the browser or Node.js:
index.js:56 Unexpected error while loading ./app/LayoutNarrowSidebar.stories.tsx: Buffer is not defined ReferenceError: Buffer is not defined at Function.encode (http://localhost:6006/vendors-node_modules_storybook_addon-actions_preview_js-generated-config-entry_js-node_module-9a87b3.iframe.bundle.js:31241:5) at encodeTarget (http://localhost:6006/main.iframe.bundle.js:4472:59) at Module../core/helpers.tsx (http://localhost:6006/main.iframe.bundle.js:4470:21) at __webpack_require__ (http://localhost:6006/runtime~main.iframe.bundle.js:28:33) at fn (http://localhost:6006/runtime~main.iframe.bundle.js:339:21) at Module../components/app/LayoutNarrowSidebar.tsx (http://localhost:6006/main.iframe.bundle.js:2227:71) at __webpack_require__ (http://localhost:6006/runtime~main.iframe.bundle.js:28:33) at fn (http://localhost:6006/runtime~main.iframe.bundle.js:339:21) at Module../components/app/LayoutNarrowSidebar.stories.tsx (http://localhost:6006/main.iframe.bundle.js:5623:79) at __webpack_require__ (http://localhost:6006/runtime~main.iframe.bundle.js:28:33) error @ index.js:56 (anonymous) @ executeLoadable.js:58 (anonymous) @ executeLoadable.js:52 executeLoadable @ executeLoadable.js:51 executeLoadableForChanges @ executeLoadable.js:100 getProjectAnnotations @ start.js:129 configure @ start.js:165 configure @ index.js:21 ./generated-stories-entry.cjs @ generated-stories-entry.cjs:6 __webpack_require__ @ bootstrap:24 _requireSelf @ hot module replacement:102 apply @ jsonp chunk loading:444 (anonymous) @ hot module replacement:344 internalApply @ hot module replacement:342 hotApply @ hot module replacement:301 cb @ process-update.js:76 (anonymous) @ process-update.js:91 Promise.then (async) check @ process-update.js:90 module.exports @ process-update.js:52 processMessage @ client.js:279 handleMessage @ client.js:139 handleMessage @ client.js:102 react-dom.development.js:86 Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot
We can either
- State "CANCELLED" from "TODO" [2022-11-23 Wed 06:58] \\ This was not trivial to resolve, so we're parking this to perhaps revisit in the future. For now we'll just repeat Org content that is tailored to the component of interest.
In order to simplify development and to avoid us having to switch between storybook tabs and dev page tabs, it would be nice to have a story for Prose and Board where the content is the raw README file that we are displaying on the home page right now.
The objective is to have Storybook's publically accessible such that have a quick reference to use in conversations (on any channel). Sharing links around will make "talking about things" a bit easier.
Some use Netlify to deploy their Storybooks. For us it doesn't matter where we host these as long as it allows for public access and it a moderately friendly (ask me what this means over a beer 🍺) platform/tool.
- State "DONE" from "TODO" [2022-11-25 Fri 10:53] \\ Stefano has largely done the gruntwork. Now we test this out and find out over time if the taxonomy needs changes. If no issues arise over the next months, we can concretize this design by renaming all associated artifacts to reflect the naming scheme.
We have a bunch of components now and considering that this will grow going forward, it is a good moment to organize our components structure a bit better.
Look into atomic design for some guidance/ideas on how we can structure our components. We may already be "redefining" components (that may be atoms) in multiple places since it isn't always too clear what may already be there.
Update title
fields in the Storybook stories to suggest a new taxonomy that breaks our component structure down into:
Chapter 2 of the Atomic Design book provides some guidance on what these taxons mean.
Some brainfarts on what triggered this task:
:COOKIE_DATA: todo recursive
There is one case in Comments where we directly call parse()
inside the component. We typically shouldn't ever have to do that.
comments
Inside of storybooks and tests such calls are okay as we're trying to simplify the generating of those payloads that we can render whatever ReactNode
-like data we need to add to our components to see it at work.
Instead of having to explicitly list intermediate representation or verbatim HTML or JSX, we just type the Org (which is easier to read for a person) and then rely on the parser to provide the data structure that we can feed to our renderer.
Other uses of parse()
in non-test and non-storybook cases, should be limited.
Just had a call with Tijan where we spoke about the following issues
- State "DONE" from "TODO" [2023-01-05 Thu 18:14] \\ Resolved with d2bc92148499379ead6b82cc62ad5b626e5f1a08
Observe the following warnings:
next-dev.js?3515:24 Warning: Failed prop type: The prop `height` is marked as required in `ResizableBox`, but its value is `undefined`. at ResizableBox (webpack-internal:///./node_modules/react-resizable/build/ResizableBox.js:46:35) at ResizablePane (webpack-internal:///./components/app/ResizablePane.tsx:12:32) at main at HorizontalDiptychWithAside (webpack-internal:///./components/app/LayoutNarrowSidebar.tsx:627:22) at div at div at Layout (webpack-internal:///./components/app/LayoutNarrowSidebar.tsx:828:22) at HomePage (webpack-internal:///./pages/index.tsx:46:21) at SessionContextProvider (webpack-internal:///./node_modules/@supabase/auth-helpers-react/dist/index.js:42:3) at MyApp (webpack-internal:///./pages/_app.tsx:30:27) at ErrorBoundary (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:8:20740) at ReactDevOverlay (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:8:23199) at Container (webpack-internal:///./node_modules/next/dist/client/index.js:149:9) at AppContainer (webpack-internal:///./node_modules/next/dist/client/index.js:652:26) at Root (webpack-internal:///./node_modules/next/dist/client/index.js:774:27)
next-dev.js?3515:24 Warning: Failed prop type: The prop `height` is marked as required in `Resizable`, but its value is `undefined`. at Resizable (webpack-internal:///./node_modules/react-resizable/build/Resizable.js:46:35) at ResizableBox (webpack-internal:///./node_modules/react-resizable/build/ResizableBox.js:46:35) at ResizablePane (webpack-internal:///./components/app/ResizablePane.tsx:12:32) at main at HorizontalDiptychWithAside (webpack-internal:///./components/app/LayoutNarrowSidebar.tsx:627:22) at div at div at Layout (webpack-internal:///./components/app/LayoutNarrowSidebar.tsx:828:22) at HomePage (webpack-internal:///./pages/index.tsx:46:21) at SessionContextProvider (webpack-internal:///./node_modules/@supabase/auth-helpers-react/dist/index.js:42:3) at MyApp (webpack-internal:///./pages/_app.tsx:30:27) at ErrorBoundary (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:8:20740) at ReactDevOverlay (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:8:23199) at Container (webpack-internal:///./node_modules/next/dist/client/index.js:149:9) at AppContainer (webpack-internal:///./node_modules/next/dist/client/index.js:652:26) at Root (webpack-internal:///./node_modules/next/dist/client/index.js:774:27)
Warning: Prop `className` did not match. Server: "transition-all fill-black h-4 w-4 cursor-pointer select-none" Client: "transition-all fill-white h-4 w-4 cursor-pointer select-none" at svg at ChevronDownIcon at span at eval (webpack-internal:///./node_modules/@headlessui/react/dist/components/disclosure/disclosure.js:16:3918) at div at li at C (webpack-internal:///./node_modules/@headlessui/react/dist/internal/open-closed.js:8:255) at eval (webpack-internal:///./node_modules/@headlessui/react/dist/components/disclosure/disclosure.js:16:1808) at TableOfContentsEntry (webpack-internal:///./components/app/TOC.tsx:29:24) at ul at eval (webpack-internal:///./node_modules/@headlessui/react/dist/components/disclosure/disclosure.js:16:5848) at div at C (webpack-internal:///./node_modules/@headlessui/react/dist/internal/open-closed.js:8:255) at eval (webpack-internal:///./node_modules/@headlessui/react/dist/components/transitions/transition.js:18:2533) at eval (webpack-internal:///./node_modules/@headlessui/react/dist/components/transitions/transition.js:18:5475) at li at C (webpack-internal:///./node_modules/@headlessui/react/dist/internal/open-closed.js:8:255) at eval (webpack-internal:///./node_modules/@headlessui/react/dist/components/disclosure/disclosure.js:16:1808) at TableOfContentsEntry (webpack-internal:///./components/app/TOC.tsx:29:24) at ul at eval (webpack-internal:///./node_modules/@headlessui/react/dist/components/disclosure/disclosure.js:16:5848) at div at C (webpack-internal:///./node_modules/@headlessui/react/dist/internal/open-closed.js:8:255) at eval (webpack-internal:///./node_modules/@headlessui/react/dist/components/transitions/transition.js:18:2533) at eval (webpack-internal:///./node_modules/@headlessui/react/dist/components/transitions/transition.js:18:5475) at li at C (webpack-internal:///./node_modules/@headlessui/react/dist/internal/open-closed.js:8:255) at eval (webpack-internal:///./node_modules/@headlessui/react/dist/components/disclosure/disclosure.js:16:1808) at TableOfContentsEntry (webpack-internal:///./components/app/TOC.tsx:29:24) at ul at eval (webpack-internal:///./node_modules/@headlessui/react/dist/components/disclosure/disclosure.js:16:5848) at div at C (webpack-internal:///./node_modules/@headlessui/react/dist/internal/open-closed.js:8:255) at eval (webpack-internal:///./node_modules/@headlessui/react/dist/components/transitions/transition.js:18:2533) at eval (webpack-internal:///./node_modules/@headlessui/react/dist/components/transitions/transition.js:18:5475) at li at C (webpack-internal:///./node_modules/@headlessui/react/dist/internal/open-closed.js:8:255) at eval (webpack-internal:///./node_modules/@headlessui/react/dist/components/disclosure/disclosure.js:16:1808) at TableOfContentsEntry (webpack-internal:///./components/app/TOC.tsx:29:24) at ul at TOC (webpack-internal:///./components/app/TOC.tsx:185:26) at div at aside at div at Resizable (webpack-internal:///./node_modules/react-resizable/build/Resizable.js:46:35) at ResizableBox (webpack-internal:///./node_modules/react-resizable/build/ResizableBox.js:46:35) at ResizablePane (webpack-internal:///./components/app/ResizablePane.tsx:12:32) at main at HorizontalDiptychWithAside (webpack-internal:///./components/app/LayoutNarrowSidebar.tsx:627:22) at div at div at Layout (webpack-internal:///./components/app/LayoutNarrowSidebar.tsx:828:22) at HomePage (webpack-internal:///./pages/index.tsx:46:21) at SessionContextProvider (webpack-internal:///./node_modules/@supabase/auth-helpers-react/dist/index.js:42:3) at MyApp (webpack-internal:///./pages/_app.tsx:30:27) at ErrorBoundary (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:8:20740) at ReactDevOverlay (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:8:23199) at Container (webpack-internal:///./node_modules/next/dist/client/index.js:149:9) at AppContainer (webpack-internal:///./node_modules/next/dist/client/index.js:652:26) at Root (webpack-internal:///./node_modules/next/dist/client/index.js:774:27) See more info here: https://nextjs.org/docs/messages/react-hydration-error
- State "DONE" from "TODO" [2023-01-05 Thu 18:15] \\ Merged with 5277b63b6f06e045defe8226203c30f198444e36
next-dev.js?3515:24 Warning: Each child in a list should have a unique "key" prop. Check the render method of `RenderCommentHelper`. See https://reactjs.org/link/warning-keys for more information. at CommentsBlock (webpack-internal:///./components/doc/Comment.tsx:42:20) at RenderCommentHelper (webpack-internal:///./utils/RenderCommentHelper.tsx:16:25) at div at section at Section (webpack-internal:///./components/doc/Section.tsx:21:21) at RenderCommentHelper (webpack-internal:///./utils/RenderCommentHelper.tsx:16:25) at div at section at Section (webpack-internal:///./components/doc/Section.tsx:21:21) at RenderCommentHelper (webpack-internal:///./utils/RenderCommentHelper.tsx:16:25) at div at section at Section (webpack-internal:///./components/doc/Section.tsx:21:21) at RenderCommentHelper (webpack-internal:///./utils/RenderCommentHelper.tsx:16:25) at div at Prose (webpack-internal:///./components/mode/Prose/index.tsx:13:25) at div at section at main at HorizontalDiptychWithAside (webpack-internal:///./components/app/LayoutNarrowSidebar.tsx:627:22) at div at div at Layout (webpack-internal:///./components/app/LayoutNarrowSidebar.tsx:828:22) at HomePage (webpack-internal:///./pages/index.tsx:46:21) at SessionContextProvider (webpack-internal:///./node_modules/@supabase/auth-helpers-react/dist/index.js:42:3) at MyApp (webpack-internal:///./pages/_app.tsx:30:27) at ErrorBoundary (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:8:20740) at ReactDevOverlay (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:8:23199) at Container (webpack-internal:///./node_modules/next/dist/client/index.js:149:9) at AppContainer (webpack-internal:///./node_modules/next/dist/client/index.js:652:26) at Root (webpack-internal:///./node_modules/next/dist/client/index.js:774:27) window.console.error @ next-dev.js?3515:24 printWarning @ react-jsx-dev-runtim…elopment.js?17e1:87 error @ react-jsx-dev-runtim…elopment.js?17e1:61 validateExplicitKey @ react-jsx-dev-runtim…opment.js?17e1:1078 validateChildKeys @ react-jsx-dev-runtim…opment.js?17e1:1105 jsxWithValidation @ react-jsx-dev-runtim…opment.js?17e1:1276 RenderCommentHelper @ VM98527 RenderCommentHelper.tsx:22 renderWithHooks @ react-dom.development.js?ac89:16305 mountIndeterminateComponent @ react-dom.development.js?ac89:20074 beginWork @ react-dom.development.js?ac89:21587 beginWork$1 @ react-dom.development.js?ac89:27426 performUnitOfWork @ react-dom.development.js?ac89:26557 workLoopSync @ react-dom.development.js?ac89:26466 renderRootSync @ react-dom.development.js?ac89:26434 performConcurrentWorkOnRoot @ react-dom.development.js?ac89:25738 workLoop @ scheduler.development.js?bcd2:266 flushWork @ scheduler.development.js?bcd2:239 performWorkUntilDeadline @ scheduler.development.js?bcd2:533
- State "DONE" from "TODO" [2023-01-05 Thu 18:13] \\ Merged into mainline with 4de4147405ba68068095acad442196906de3a90b
Probably just set readOnly
for now.
Warning: You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`. at input at span at ListChildLabel (webpack-internal:///./components/doc/List.tsx:21:27) at li at ListChild (webpack-internal:///./components/doc/List.tsx:72:22) at ul at List (webpack-internal:///./components/doc/List.tsx:54:17) at RenderCommentHelper (webpack-internal:///./utils/RenderCommentHelper.tsx:21:32) at div at section at Section (webpack-internal:///./components/doc/Section.tsx:26:20) at RenderCommentHelper (webpack-internal:///./utils/RenderCommentHelper.tsx:21:32) at div at section at Section (webpack-internal:///./components/doc/Section.tsx:26:20) at RenderCommentHelper (webpack-internal:///./utils/RenderCommentHelper.tsx:21:32) at div at section at Section (webpack-internal:///./components/doc/Section.tsx:26:20) at RenderCommentHelper (webpack-internal:///./utils/RenderCommentHelper.tsx:21:32) at div at Prose (webpack-internal:///./components/mode/Prose/index.tsx:16:18) at div at section at main at HorizontalDiptychWithAside (webpack-internal:///./components/app/LayoutNarrowSidebar.tsx:575:39) at div at div at Layout (webpack-internal:///./components/app/LayoutNarrowSidebar.tsx:770:19) at HomePage (webpack-internal:///./pages/index.tsx:46:21) at SessionContextProvider (/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@supabase/auth-helpers-react/dist/index.js:42:3) at MyApp (webpack-internal:///./pages/_app.tsx:26:18) at StyleRegistry (/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/styled-jsx/dist/index/index.js:671:34) at AppContainer (/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/next/dist/server/render.js:404:29) at AppContainerWithIsomorphicFiberStructure (/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/next/dist/server/render.js:433:57) at div at Body (/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/next/dist/server/render.js:690:21)
Warning: data for page "/" is 346 kB which exceeds the threshold of 128 kB, this amount of data can reduce performance. See more info here: https://nextjs.org/docs/messages/large-page-data
- State "DONE" from "TODO" [2023-01-03 Tue 16:57] \\ Already in mainline. Thanks to Stefano and commit 9d229f2289d6d2c8dd76e8a44ee93cbe16ee67ca
The following warnings pop up when building the project:
./components/Board/EditTaskModal.tsx 58:17 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element 77:17 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element 96:17 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element 187:15 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element ./components/Board/Task.tsx 68:17 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element 95:27 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element ./components/app/LayoutExample.tsx 102:23 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element 140:27 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element 173:19 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element 211:23 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element 235:17 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element ./components/app/LayoutMultiColumn.tsx 98:23 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element 166:29 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element 257:27 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element 524:25 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element ./components/app/LayoutSidebar.tsx 117:21 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element 164:15 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element 246:23 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element ./components/app/Menu.tsx 34:5 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element ./components/app/NavigationBar.tsx 154:19 Warning: Do not use `<img>` element. Use `<Image />` from `next/image` instead. See: https://nextjs.org/docs/messages/no-img-element @next/next/no-img-element info - Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules info - Linting and checking validity of types ..Failed to compile.
The fix should be pretty trivial so it's a quick win. 🏆
- State "DONE" from "TODO" [2023-01-03 Tue 16:58] \\ Already done a while ago
Switching from Grommet to Tailwind because:
- State "DONE" from "TODO" [2022-08-23 Tue 17:36] \\ Sorted by @purcy on [2022-08-23 Tue] so we no longer have to worry about this. Future point would be to look into honoring Tailwind styles in the Storybook views as the current setup renders the components without any styling.
To simplify development of UI parts, we would like to be able to review components in Storybook. For this, we need to configure the project in order for npm run storybook
.
Fix the configuration and provide links to the relevant documentation that explains the design choicese in that configuration.
The following error is produced when running npm run storybook
:
info @storybook/react v6.5.9 info info => Loading presets info Addon-docs: using MDX1 info => Using PostCSS preset with postcss@7.0.39 info => Using default Webpack5 setup <i> [webpack-dev-middleware] wait until bundle finished 9% setup compilation DocGenPluginnode:internal/modules/cjs/loader:959 throw err; ^ Error: Cannot find module 'webpack/lib/util/makeSerializable.js' Require stack: - /home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/react-docgen-typescript-plugin/dist/dependency.js - /home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/react-docgen-typescript-plugin/dist/plugin.js - /home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/react-docgen-typescript-plugin/dist/index.js - /home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/react/dist/cjs/server/framework-preset-react-docs.js - /home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/core-common/dist/cjs/presets.js - /home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/core-common/dist/cjs/index.js - /home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/core-server/dist/cjs/index.js - /home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/core/dist/cjs/server.js - /home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/core/server.js - /home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/react/dist/cjs/server/index.js - /home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/react/bin/index.js at Function.Module._resolveFilename (node:internal/modules/cjs/loader:956:15) at Function.Module._load (node:internal/modules/cjs/loader:804:27) at Module.require (node:internal/modules/cjs/loader:1028:19) at require (node:internal/modules/cjs/helpers:102:18) at Object.<anonymous> (/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/react-docgen-typescript-plugin/dist/dependency.js:6:55) at Module._compile (node:internal/modules/cjs/loader:1126:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1180:10) at Module.load (node:internal/modules/cjs/loader:1004:32) at Function.Module._load (node:internal/modules/cjs/loader:839:12) at Module.require (node:internal/modules/cjs/loader:1028:19) { code: 'MODULE_NOT_FOUND', requireStack: [ '/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/react-docgen-typescript-plugin/dist/dependency.js', '/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/react-docgen-typescript-plugin/dist/plugin.js', '/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/react-docgen-typescript-plugin/dist/index.js', '/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/react/dist/cjs/server/framework-preset-react-docs.js', '/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/core-common/dist/cjs/presets.js', '/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/core-common/dist/cjs/index.js', '/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/core-server/dist/cjs/index.js', '/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/core/dist/cjs/server.js', '/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/core/server.js', '/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/react/dist/cjs/server/index.js', '/home/vidbina/src/formation.tools/eng/proto-01/main/node_modules/@storybook/react/bin/index.js' ] }
- State "DONE" from "WIP" [2022-08-23 Tue 17:37] \\ We need HeadlessUI for the React components and some of the dynamic behavior associated with them (think Dialogs for Modals, Disclosures for accordion-like components, etc.). Some of the TailwindUI components seem to depend on this library and other third-party component libraries such as Flowbite 😠 (yeah, I'm not amused about this product ATM) seem to do the same.
In branch vidbina/setup-tailwind-headlessui, we are trying out incorporating HeadlessUI components in our setup. If this works, it may simplify building components in React that are based on top of TailwindCSS.
The HeadlessUI repository is pretty small, as in, only few components.
- State "DONE" from "DONE" [2023-01-03 Tue 16:59] \\ We only took part of the Kanban experience and are building around it. DX (dev experience) was a PITA so I'm not keen to go back to use Flowbite unless it really presents a significant advantage in us moving faster.
Flowbite has a UI blocks (based on TailwindCSS) that we can build on such as:
We have to explore to which extend we need this and can bring this in.
If we start a branch e.g.: setup-tailwind-flowbite, start it from vidbina/setup-tailwind.
- State "CANCELLED" from "TODO" [2023-01-03 Tue 17:00] \\ Not relevant for now, so parking. Recommendation for dark mode is to tell folks to use DarkReader meanwhile. I'm using it in Chromium and Firefox and it offers me enough control to have a more personalized dark experience. Our objective should be to provide a design that looks good enough when DarkReader is on.
The Next.js docs outline in https://nextjs.org/docs/advanced-features/custom-document how we can set html
and body
level classes from within the pages/_document.tsx
file with the caveat that these are server-side rendered and therefore cannot support dynamic behavior (for example the use of event handlers such as onClick
).
The blog post https://smnh.me/add-class-to-body-tag-in-nextjs outlines some ways to deal with html/body-level dark-mode settings which are dynamic in the sense that they are change during run-time and will need to be updated. It isn't quite clear if the instructions here are relevant at this moment so we will need to investigate what the issue here is.
The reason why this information is relevant is because some of the TailwindUI components such as https://tailwindui.com/components/application-ui/application-shells/multi-column will require the following updates to our template:
<html class="h-full bg-white"> <body class="h-full overflow-hidden">
The use of class bg-white
is for non-dark-mode templates. Toggling dark mode will therefore require changes to the html
and body
classList at runtime in order to work adequately.
- State "DONE" from "TODO" [2023-01-03 Tue 17:02] \\ This has been done in some shape or form. We've abstracted away a bunch thanks to an effort led by @stefano to define a component taxonomy that works for us (following some parts of the Atomic Design idea). Renaming effort has been confined to updating Storybook names to provide some recommendation of how we should be structuring our component landscape. More effort will be needed to actually rename files but not while a lot of component-work remains high-flux.
@vidbina has copied over a few Application UI/Application Shells from TailwindUI and has made the necessary translations from HTML to JSX just to have something that looks okay-ish as a starting point.
We now have to take these compositions, basically UI dumps, and then decompose them into the larger subparts such as (following are just an example):
Note that the example in components/Layout.tsx is not complete in that some of the animations are broken and state is lacking which is why menus or other state-dependent elements will not behave correctly. All of these capabilities will need to be implemented.
💡 As a reference, look at component ToggleDarkMode which represents a basic TailwindCSS component which was translated from the TailwindUI code listing and then retrofitted to contain the correct images and to behave as expected by implementing event handling and by binding state into the component.
- State "DONE" from "TODO" [2022-09-25 Sun 14:37] \\ Basic TOC is implemented. Some improvements like linking are still pending but that is to be considered future work.
As a user, I want a ToC (table of contents) or sidebar present in the interface such to:
- State "DONE" from "TODO" [2022-08-23 Tue 17:39] \\ @purcy stubbed this component today [2022-08-23 Tue] and I just integrated it so we should be good to go. We still want to incorporate HeadlessUI's Disclosure components in order to facilitate folding but this is a nice-to-have that we can address later. First we have to address some higher-level layouting concerns because I just plopped the ToC on a page without much though or any grace. 😅
- State "DONE" from "TODO" [2022-09-25 Sun 14:36] \\ This issue is actually unresolved pending because we've refactored the TOC to show in dark-mode for better contrast during the light-mode. In the current form, we are not exposing the dark-mode functionality to end-users and will be recommending the use of a plugin like DarkReader to have a computed browser-wide dark-mode instead of one explicitly designed by us.
Switching between light and dark mode with the ToC rendered doesn't really change the text color. The ToC text should follow the classes for the main text which is altered during light/dark mode transitions. Not sure which TailwindCSS classes need to be set.
- State "DONE" from [2022-09-25 Sun 14:34] \\ Already merged into mainline (work done by @tijan)
- State "DONE" from [2022-09-25 Sun 14:34] \\ Implemented a naive approach (unique link ids per session)
See Design headline linking strategy for a characterization of the headline labelling problem. Once we've resolved this, we should roll an implementation for the ToC (and also the Prose but that is another concern).
- State "DONE" from "TODO" [2023-01-03 Tue 17:03] \\ Mostly sorted by previous decomposition/organization efforts
We still use Col, Row, Main, MainContent and AppContainer from components/View.tsx
I'm upgraded this task (instead of a child of the [[*Setup TailwindCSS][Setup TailwindCSS]] task it is now a sibling thereof) since it doesn't block any of the other tasks. We can therefore move along on the Storybook or TailwindCSS concerns without this affecting us at all. Having it here may allow @purcy to prototype things quickly and then we do the translation afterwards so I'm deprioritizing this as well by moving it to the bottom of the task list.
With Tailwind coming in, we should remove gradually remove Grommet. It isn't clear where all Grommet dependencies are, in terms of files that are Grommet-related, but we would need to identify them and then rid them from the dependency (remove Grommet dependency from core components) and also clean up the dependency manifest (packages.json).
Here is a result of a grep for "grommet" on the repo which indicates which parts still need removing:
components/View.tsx:import { Box, BoxExtendedProps } from 'grommet' components/doc/Code.tsx:// FIXME: Remove grommet remnants components/doc/Code.tsx:import { Box, ThemeContext } from 'grommet' components/generic/UIHeading.tsx:import { Heading as GrommetHeading } from 'grommet' [...snipped results from package.json and package-lock.json...] pages/r/index.tsx:import { Button, TextInput } from 'grommet' ↪ √ → ~/src/formation.tools/eng/proto-01/main @ 2023.02.13 17:45
Note that _app.tsx imported GlobalStyle
from styles/global.js. We have already removed the import but the styles/global.js file is still in place and will need to be removed.
Note that a grep for "global" indicates that we use the Grommet styles in the following places:
.storybook/preview.js:import '../styles/globals.css' [...snipped...] pages/_app.tsx:import '../styles/globals.css' pages/_app.tsx: <style jsx global>{`
Use https://www.npmjs.com/package/uuid instead of https://www.npmjs.com/package/uuidv4 as per UUIDv4's notice https://github.com/thenativeweb/uuidv4#please-note.
Perhaps we even remove unpackElementType
in order to fully rely on convert
which we may rename to unpack
in its new role.
The reasons being:
The components of this syntax can be divided into two classes: “objects” and “elements”. To better understand these classes, consider the paragraph as a unit of measurement. Elements are syntactic components that exist at the same or greater scope than a paragraph, i.e. which could not be contained by a paragraph. Conversely, objects are syntactic components that exist with a smaller scope than a paragraph, and so can be contained within a paragraph. Elements can be stratified into “headings”, “sections”, “greater elements”, and “lesser elements”, from broadest scope to narrowest. Along with objects, these sub-classes define categories of syntactic environments. Only headings, sections, property drawers, and planning lines are context-free1, 2, every other syntactic component only exists within specific environments. This is a core concept of the syntax. Expanding on the stratification of elements, lesser elements are elements that cannot contain any other elements. As such, a paragraph is considered a lesser element. Greater elements can themselves contain greater elements or lesser elements. Sections contain both greater and lesser elements, and headings can contain a section and other headings.
The function unpackElementType
returns an incorrect fallback. It returns a e
-typed fallback, which is reserved for Element types, for a GreaterElement element which should actually unpack into an E
-typed fallback.
Furthermore, there are a bunch of elements for which this function just returns nothing (denoted by the empty list) instead of a fallback entry so a bunch of information goes missing.
For naming consistency, we should drop the Type
prefix. I borrowed this from the reference implementation in uniorg but it is a bit more verbose than needed and actually confusing since I don't follow the pattern for ElementType in a consistent manner.
I did something stupid in the rush of dropping the build. When you study the classes you'll find a few classes named undefined
littering the codebase. 💩 Clean this up!
[Report Only] Refused to load the font '<URL>' because it violates the following Content Security Policy directive: "font-src 'self' <URL> <URL>". registration:1 [Report Only] Refused to load the font 'data:font/woff2;base64,d09GMgABAAAAAlzYAAoAAAAFpdwAAlyNAwEFAAAAAAAAAAAAAAAAAAAAAAAAAAAAATgCJAQgBmADqzgAgZBwypTaDMurPAWHcAcgpe3SklIgwPQAAFDdzsOIFM0eniJ6Vnt7AFRVVVXVlISAx2zbAaCqgh/95Ge/+NVvfveHP/3lb//4l3/7j//6n//7T4HBuPsHwrRsx+X2eH1+w3+49u+5s7sP6KfEKbBQ5SkRSlRTMPq7Gh+hKmyNItVYrFYSMYflpjoiqq8zlVfcXjmYZSNiKtCBjbBuYJJ2fizDlZ2I9+hMVF7Ud50o8CRhegrJkXm4vSSFrgRHEas4TNYk06I4dIPdJO3NFPAPhC/yu5QgIpy/n929CyKWNjSIiT3ErcBXVwKQzX8voe/bRBxmKgItTDSZGcEIRzkXqmYWlpc6gkSLFdhiryZ67YDD9yFHvsMeAhjTy+5g16iE/ZPAf4w/+M6teR8AF6ZrWuh4...ccGw3QNzgGbPbffH6oj3X+P9TPeF/+QwMc9/2lBz8/NESrb+1Dw1T6vj40zmP/54cmMH0/H5ol11/d5Rzjfflr9VHO3mzQnpVtuc6slRvtutbAa3xvs+DUy872nlld29vaWFvfksLjnNfZNtyXbbNm1ovX6uPkpu3rzNrqAtdPYkzDfq32pD7Wn/BzM03tvQBV/Yyb5WVcaPqRueckSqFZlBfznNwmVt5sIx+kpyFKqulLt56Ja6qXYKPUfUuiarfjouEaxRM7ZsSwb1gCSg+LsI1wTYZNefQblC6O1pi6N9hnFHDL+dYq7LDNHoZV1thjiw3WWGcLofBc2de/1f6zH2hYw7BO8TVaHulvv7q7VrdKiERSAvvUqN3C/U71ROrPGhma7zPhq0r07tPVV7cISxh8gZLyQYbj+TU2CwWaFOdLsJTolmQbbP2GpUGegNBDCfOrdKmDXuhSJ0Nw76juEcLNHE4JqcDmFkJEt9bMDo6IsdVKwj8MAwAAAA==' because it violates the following Content Security Policy directive: "font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net". registration:1 [Report Only] Refused to load the font 'data:font/ttf;base64,AAEAAAAKAIAAAwAgT1MvMmFLYkoAAAEoAAAAYGNtYXBVZFh1AAAXQAAASG5nbHlmD0UmvgAAdWwABS0MaGVhZCKn9FQAAACsAAAANmhoZWEEQgetAAAA5AAAACRobXR46UsPVgAAAYgAABW4bG9jYQ3zkrwAAF+wAAAVvG1heHAFhQF8AAABCAAAACBuYW1lLsmVwwAFongAAAPtcG9zdK7JwzUABaZoAABM0gABAAADAQUAFxrMGF8PPPUACwIAAAAAAN8EV2sAAAAA3wRXawAA/8ACgAHAAAAACAACAAEAAAAAAAEAAAHA/8AAAAKA/////gKAAAEAAAAAAAAAAAAAAAAAAAVuAAEAAAVuAXoAFQAAAAAAAQAAAAEAAQAAAAAAAAAAAAAABAICA4QABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAIACQMAAAAAAAAAAAABEAAAAAAAAAAAAAAAQVdTTQCAACH//wHA/8...Jvdy11cBJ0cmFzaC1jYW4tYXJyb3ctdXAKdXNlci1udXJzZQt3YXZlLXNxdWFyZQ1wZXJzb24tYmlraW5nCmJvcmRlci1hbGwLYm9yZGVyLW5vbmUPYm9yZGVyLXRvcC1sZWZ0DnBlcnNvbi1kaWdnaW5nA2ZhbgVpY29ucwpwaG9uZS1mbGlwEXNxdWFyZS1waG9uZS1mbGlwCnBob3RvLWZpbG0KdGV4dC1zbGFzaA5hcnJvdy1kb3duLXotYQxhcnJvdy11cC16LWEVYXJyb3ctZG93bi1zaG9ydC13aWRlE2Fycm93LXVwLXNob3J0LXdpZGUOYXJyb3ctZG93bi05LTEMYXJyb3ctdXAtOS0xC3NwZWxsLWNoZWNrCXZvaWNlbWFpbApoYXQtY293Ym95D2hhdC1jb3dib3ktc2lkZQ5jb21wdXRlci1tb3VzZQVyYWRpbwxyZWNvcmQtdmlueWwNd2Fsa2llLXRhbGtpZQdjYXJhdmFuAAA=' because it violates the following Content Security Policy directive: "font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net". registration:1 [Report Only] Refused to load the font 'data:font/woff2;base64,d09GMgABAAAAAZxAAAoAAAACr6QAAZv2AwEDAAAAAAAAAAAAAAAAAAAAAAAAAAAAATgCJAQgBmADj1AAjUjKiqgcy49UBYggByCl0VdRZwCC8wAAsFY/D6pI10ntHgBV1ToxuKkaEH765bc//vrnv78KjN0NsY7r+Yb/+v3Kc/v/mXkLxCqK8IVZrktFRsgOlB+1sexTUatSXdGkSBFIQJVvYY62iPrjo34/xN17JkBnNmmK+2Hb29BVEual5rB6NfWjuHjCr6j/PKrq3/MAvIeHSZAAHofEBRKQLMkiCRKwpdiSVxo3duIkdrN/nHSM6UZd6drpWOpYW+kfa6n9c3WsT/08+dX295yb61YKL+R6r6o7v1zd093TPXk3z86mDJuAXUKWOMQkYARZATMIigEUdVRUzJgwpDWF7zd+Mfwe/3l+lfp/194H6AJXVRd0V03XbRio4hlpQJJBsi2DjJJsJwoYIQ44MCHAD3GI...WKpXqyu/VPu9pQNt/vass0ndrVkfWmZVcnUmberu5MMC9vdw+6tjJLXejtgl1B4loCm7ML40JYTuzKoLqjuRDXvI+Smel0xYW+8MPjfqrkWlbXArsrKI5NbItLvI1ctKO5ENt6yYU2CbwthGXrnWtuqvvUT4/w2iBO6i60+dyMVCabzWcb8tPtuMnNzskeoZk2l8qmsuMXuWhPXK/WvB1XGm8XPLbUhX5Gk1uDP3kL5l+92jw9ednyQgUsxRHisSxgFwEJjhYCLDksC4kHolpcJsEOMYTiVXbQTIGYGh5PRMJM0qQDib1hiKewLJeitFS3mtqSk7sIKDL2H9eCG2qj+1oiHNHSnaU+ZzLEkhBMxU/Sz+JxOJppoo7vxrhatmH2WgJiEupLL5onxwxSZMiSJU+WBvJMJwdpgUtevHNJeua6XIrs/7vxLNqW5B5i6lT30leeZRwlxmOXq96bYxsW5z7/UpP3/wI/45xbPueR6578medYLr7zC/8fAwM=' because it violates the following Content Security Policy directive: "font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net". registration:1 [Report Only] Refused to load the font 'data:font/ttf;base64,AAEAAAAKAIAAAwAgT1MvMl9ZV1UAAAEoAAAAYGNtYXBup5HrAAAJWAAABsZnbHlm3yn0iQAAF/QAApQcaGVhZCFecRoAAACsAAAANmhoZWEERAQmAAAA5AAAACRobXR4swkIbQAAAYgAAAfQbG9jYQIufOwAABAgAAAH1G1heHACHQQ6AAABCAAAACBuYW1la7WliwACrBAAAAQdcG9zdKecxYsAArAwAAAWKQABAAADAQMAaqfFLV8PPPUACwIAAAAAAN5fls4AAAAA3l+WzgAA/8ACgAHAAAAACAACAAEAAAAAAAEAAAHA/8AAAAKA////8QKCAAEAAAAAAAAAAAAAAAAAAAH0AAEAAAH0BDgAJwAAAAAAAQAAAAEAAQAAAAAAAAAAAAAABAHkAZAABQAAAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAIABQMAAAAAAAAAAAABEAAAAAAAAAAAAAAAQVdTTQDAACP46AHA/8...Vha3MLcmVhY3RldXJvcGUKYXJ0c3RhdGlvbglhdGxhc3NpYW4TY2FuYWRpYW4tbWFwbGUtbGVhZgZjZW50b3MKY29uZmx1ZW5jZQNkaGwIZGlhc3BvcmEFZmVkZXgGZmVkb3JhBWZpZ21hCGludGVyY29tCGludmlzaW9uBGppcmEIbWVuZGVsZXkMcmFzcGJlcnJ5LXBpBnJlZGhhdAZza2V0Y2gKc291cmNldHJlZQRzdXNlBnVidW50dQN1cHMEdXNwcwR5YXJuBmFpcmJuYgpiYXR0bGUtbmV0CWJvb3RzdHJhcAZidWZmZXIKY2hyb21lY2FzdAhldmVybm90ZQdpdGNoLWlvCnNhbGVzZm9yY2UMc3BlYWtlci1kZWNrB3N5bWZvbnkEd2F6ZQZ5YW1tZXIHZ2l0LWFsdAlzdGFja3BhdGgNY290dG9uLWJ1cmVhdQtidXktbi1sYXJnZQNtZGIFb3JjaWQFc3dpZnQHdW1icmFjbwAAAA==' because it violates the following Content Security Policy directive: "font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net". registration:1 [Report Only] Refused to load the font 'data:font/woff2;base64,d09GMgABAAAAAYI0AA0AAAAEnsgAAYHWAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGogMG4SmRhyBqkoGYACBywoKiKAchus5C89UAAE2AiQDz04EIAWMcgeByS1bpRu01kps292SXFogOJmeCkL8ShEEBfHTOaQSbbEAbk63++9nEZWMuT9eCEFEIdDqGKIDEwlKp3X+WVeoc3+EKZXOW9RKhDzS7P////////////////9vJPnPo5t/7n3JeSMvIWEFCQGKgigIMrRW7Vj6RwtqJKyj8CFkMS86yFCWVZfwKNFDHw4xoDMY9rMsq0ewF1kBJ6IbXPTHiPDNJHMeLu8UWZYVCGinmAgHpxHUCKpDfabO23lFfBhl6WsWl1dLjGJbVahQXZZipVC/pkomVRTrjsLCssMFHW8Su6U51c1wq7gBMunv/E4jqOztyikWpVUs5uO2Iuw9DvpxW4oV6lPdsOFF/6zeVebWvFXH...UrqrnKoBXkBXMXdc3mVq4hiWD2IcREtVHHZTBlVN4g9F6155z2YjxoorGINHqG+wQcbBCiNqgNorwhwYNWhUljiCfWtMjZqeZm0koEC7CVhdlVvo53gRS2YJzg2hQKlyNZQXTrKh/emUj31d0xq1VP6c+wBxBhQhkX0lPa2HEXiwnpKW1srooIE8q4kJ7SxuZqhAllXEhPaWNzdYAIc6Uzs6/jItlTxoUydtwpOg0XMm3nTs+4Pe7CqTZAhAllXEhPaWNzHYAIE8q4kJ7Sxua6ABEmlHEhPaWNzfUAIkyoNnbctTm53lPa2HHXOWLKpco8yubaAI/5X7hyrXG5PYyVcjetHXciwF6msSsmuG+IVs7j/RGIZKsAESaUCekpbVYNkZmyC8B4yszpufE8OFGu3sbmGgARJpTxEV++95w7n+X0eyXt+5YAIkwo40J6SoOnOQOK1+fuRd51q0q8Ah6BoTUqsC6YJ+RyZT+Xw+LAzdIylzkud3zedO49DAAA' because it violates the following Content Security Policy directive: "font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net". registration:1 [Report Only] Refused to load the font 'data:font/woff;base64,d09GRgABAAAAAgrUAA0AAAAElPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHREVGAAGSRAAAApIAAAQMLxsv50dQT1MAAZTYAABOhQABE0aIvxr7R1NVQgAB42AAACd0AABVSptVOxRPUy8yAAABoAAAAFcAAABgKreuYmNtYXAAAA9sAAAubAAAZYo1eewlZ2x5ZgAATwQAARrNAAIG2WorKltoZWFkAAABMAAAADQAAAA2LWDLTmhoZWEAAAFkAAAAIAAAACQe9RnAaG10eAAAAfgAAA1yAAAnzgHTThRsb2NhAAA92AAAESkAACfUCRk8iG1heHAAAAGEAAAAHAAAACAKCwDkbmFtZQABadQAAAKHAAAF4qga4kpwb3N0AAFsXAAAJeUAAGSt3GSztnjaY2BkYGBgZrl74inDrnh+m68M0twMIHDn065PEHo3+/cHf2pksnhAXDYGJpAOALPYDwZ42mNgZGDgWvE3g...ajkDJ0Jy7m9fYARuhAd8H1vMp64lFeZT/BskRhohC/TbRIXIJ1iV6JFHYkZibuxgeJhxKL8YfEksTT+CTxXOI5/D3xduJtnEp8nPgY3/HTBYcUEp8mPlPK/USutIlMpBKck4bKBO8Eu5UN64b5KgxLw1KVDMeG41SO+31R5bnfDlWl8LFwharsfi9U1cP3w32qRtQsaq5qRa2iVqqu+61O5UczozvVOdHsaLaqH22JnlcN3G9sqiD6KjqiGp1+Pf6fzB8I9l/JW4U1X88UBW2C66CYq5jGQdPgqgo9Gpg2PG/HSo8aphnPz+xpT06G0lLbuHk78rQWKy35PKuLTpvJBcg7+0hA0OI7xPeE+G5O1z77ygeBZAVDVrD2ev4pZ46Bt4ViFPy7OQGByse9QvTqm9a87sdIJKqbpry+rz7NpySIS9mXTvfr39w1C0DQFVZbXTQsd45haHXW67C/Cv9L12B/XfVX0VZnu4qWv4b+K1dQf1WUK+H/ARWMSxg=' because it violates the following Content Security Policy directive: "font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net". registration:1 [Report Only] Refused to load the font 'data:font/woff2;base64,d09GMgABAAAAAZ6cAA0AAAAEviAAAZ4+AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGogMG4TcKByBqkoGYACBywoKiKkshuRRC89UAAE2AiQDz04EIAWMWgeByS1bBzO00v8f+3kvvCX68AwGeLZpWWtmP8t9dA6pxNFaAJ3T++3LZBGZxLYX8k19QYuO6hjDCxsOJW9Wv24obphO0QvXbQhktsjavD6L7P///////////////29rWUQ6nd3/9177UqVX6UiogUSTAGMZbONKCIljx04gSjOGaKvgQv8j4WM2QYowUVmcSipESV5wsWJLYSooxTSIwDUREk1rErO1uDxTaBmkzahtS1hEHaJyQQwFlQu6mvT6jiQYkFp3hjSkozGdHE1lFraJDfBkMIAlx4ErCgPn9NwordAX12q0yEIvYVYoYGAM1n0YohkxiRZk2drOYa005CTkjREIMUoJESJEi4JqKGghSlijFLRA...vay8QjyHmPXrvooxrlrCsmgybai2SogmeU1gYh1ADJke/kwbtOhcSI2c/sdVhuKtkmbpmxgbXcmN0jvxufbxeOaJMAEieHMgUh68yLOxOarPbEVK0UzazLfABCMIJiOEFSNMNy87uYGEFSNMNyeqkIwQiK4QRJ0QzL6aURjKAYTpAUzbCcXjqAEIzTjDbm5TqhzKcYTtAsN7+z62Y4QWq4uM1nODe9C1/nACIMJ0gGk8XmcHmUOhcQYThBMpgsNofLo9R5gAjDCZLBZLE5XB6lzgeIMJzg8qi5L5VPnW9zuDxq7ks7MYLB4ui9akqdAzi9HwrdLsXSer2GSvGQ4uY+E9Bn5FEVI+z3CJXlVF8KIrpUACEYQTGCpGiGrRIkkdINwHA1GztxcXxwprz0YTm9DAAhGEExfI7TuHd877xzxejfLt1DlwARhhMkg8lic7jTu4eitv3qRd6oNQiJC7g3a4bRZj8br/3lZf6xnOhYuCyVOfdxdO7H0Cv3XgAA' because it violates the following Content Security Policy directive: "font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net". registration:1 [Report Only] Refused to load the font 'data:font/woff;base64,d09GRgABAAAAAi9oAA0AAAAEs/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHREVGAAGucAAAApIAAAQMLxsv50dQT1MAAbEEAABW7wABLijDHQkvR1NVQgACB/QAACd0AABVSptVOxRPUy8yAAABoAAAAFUAAABgK+OxuGNtYXAAABLYAAAubAAAZYo1eewlZ2x5ZgAAUmwAATOXAAILDJHyiHloZWFkAAABMAAAADQAAAA2LDXMIWhoZWEAAAFkAAAAIAAAACQdyRlbaG10eAAAAfgAABDfAAAnzjw8I4Zsb2NhAABBRAAAESUAACfUCSJfmG1heHAAAAGEAAAAHAAAACAKCwDubmFtZQABhgQAAAKBAAAF1qWJ4Alwb3N0AAGIiAAAJeUAAGSt3IG0A3jaY2BkYGBgZrk77YvK1nh+m68M0twMIHDn025bKB3wbcufGpksHnugWjYGJpAOAKUIDqp42mNgZGDgWvE3g...iGo5AydCcu5vX2AEboQHfB9bzKeuJRXmU/wbJEYaIQv020SFyCdYleiRR2JGYm7sYHiYcSi/GHxJLE0/gk8VziOfw98XbibZxKfJz4GN/x0wWHFBKfJj5Tyv1ErrSJTKQSnJOGygTvBLuVDeuG+SoMS8NSlQzHhuNUjvt9UeW53w5VpfCxcIWq7H4vVNXD98N9qkbULGquakWtolaqrvutTuVHM6M71TnR7Gi2qh9tiZ5XDdxvbKog+io6ohqdfj3+n8wfCPZfyVuFNV/PFAVtguugmKuYxkHT4KoKPRqYNjxvx0qPGqYZz8/saU9OhtJS27h5O/K0Fist+Tyri06byQXIO/tIQNDiO8T3hPhuTtc++8oHgWQFQ1aw9nr+KWeOgbeFYhT8uzkBgcrHvUL06pvWvO7HSCSqm6a8vq8+zackiEvZl07369/cNQtA0BVWW100LHeOYWh11uuwvwr/S9dgf131V9FWZ7uKlr+G/itXUH9VlCvh/wEVjEsY' because it violates the following Content Security Policy directive: "font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net". registration:1 [Report Only] Refused to load the font 'data:font/woff2;base64,d09GMgABAAAAAZ1MAA0AAAAEvUAAAZzvAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGogMG4TcQByBqkoGYACBywoKiKZshuQfC89UAAE2AiQDz04EIAWNJAeByS1bNzO03k/xt/e6st5xlhatijhWZwJB0IDI/MfoXyO+mXoANtXuv5unEZWMuYf5ACEygrTqGMMDGw6lMnvVEVSv/X6Q0JvVx4kf2fTZ////////////////////39fyI/zN3kySzuxuEpKQEEDCRwiIiKKAFX+1rbWfq62tbe+uvYMohBghFk41jBAzqg1OlRiqyiWpslnGJBGaaS4o0NJW2W4QRzpMorRLCmVFfNvrIaloRvoVNBkEaMg2Rp39RMZoIEuhdlJPMWNUPYv4duDmhwta2WyvOcx7R3NWxP8l23CcQLihHksq6UlLXORWYok9tWe5I25tlhuHZp4xWaZtIR2Mo60NTQajJ6JQyLl2TSK2...H21VqNZ2hKHm60hmqsxj6dWNReJh5B1l28puijGuWsKyaDCs0hGargGaUNgxBqgGThWz146lRIjKi+sddh2VWyTdxScQBrOZndhd/J55vCEW0SQOLkUKYgZJ158ciEJqs9Ma3W6+JKm3vZBxBhQhkXUmlj3XzbxIRU2liXV0WECWVcSKWNdXk1woQyLqTSxrq8OkCEuTbZNa/rItmnjAtt3Xxv1224kBm3bvuMu/E2b3eAxLiQytI+23FNXhdIjAupLO2zHdfk9YDEuJDK0j7bcU1eH5AYF45r5tuTN6/vsx3XzLefkwml7dyzTV4HOO5/8No11rNCb6yhpXjIcPVtBIwzcpkWIxzvEmqWpV4LIrpUACEYQTGCpGiGbSVIImU0AMPVbOzExfJgo7z0YTm9DAAhGEExvMZa95b7ti876n3buv2NABGGEyTFovnYnHJvUFTfr17kHbUuxAO4N8fMEr2y32q8q798MP9YDs3KwzIyz330y30Z+o77dQEA' because it violates the following Content Security Policy directive: "font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net". registration:1 [Report Only] Refused to load the font 'data:font/woff;base64,d09GRgABAAAAAi5UAA0AAAAEswAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHREVGAAGtTAAAApIAAAQMLxsv50dQT1MAAa/gAABW/gABLkBwQqjBR1NVQgACBuAAACd0AABVSptVOxRPUy8yAAABoAAAAFYAAABgK3+woGNtYXAAABLQAAAubAAAZYo1eewlZ2x5ZgAAUmAAATJmAAIJv6Y1plxoZWFkAAABMAAAADQAAAA2LMjMCWhoZWEAAAFkAAAAIAAAACQeXRl9aG10eAAAAfgAABDVAAAnzihtiS1sb2NhAABBPAAAESQAACfUCSECn21heHAAAAGEAAAAHAAAACAKCwDlbmFtZQABhMgAAAKaAAAGDq236pRwb3N0AAGHZAAAJeUAAGSt3Hez6XjaY2BkYGBgZrmr+aXHJp7f5iuDNDcDCNz5tNsGSvt/9/hTI5PFownksjEwgXQAAIa8Dah42mNgZGDgWvE3g...iGo5AydCcu5vX2AEboQHfB9bzKeuJRXmU/wbJEYaIQv020SFyCdYleiRR2JGYm7sYHiYcSi/GHxJLE0/gk8VziOfw98XbibZxKfJz4GN/x0wWHFBKfJj5Tyv1ErrSJTKQSnJOGygTvBLuVDeuG+SoMS8NSlQzHhuNUjvt9UeW53w5VpfCxcIWq7H4vVNXD98N9qkbULGquakWtolaqrvutTuVHM6M71TnR7Gi2qh9tiZ5XDdxvbKog+io6ohqdfj3+n8wfCPZfyVuFNV/PFAVtguugmKuYxkHT4KoKPRqYNjxvx0qPGqYZz8/saU9OhtJS27h5O/K0Fist+Tyri06byQXIO/tIQNDiO8T3hPhuTtc++8oHgWQFQ1aw9nr+KWeOgbeFYhT8uzkBgcrHvUL06pvWvO7HSCSqm6a8vq8+zackiEvZl07369/cNQtA0BVWW100LHeOYWh11uuwvwr/S9dgf131V9FWZ7uKlr+G/itXUH9VlCvh/wEVjEsY' because it violates the following Content Security Policy directive: "font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net". registration:1 [Report Only] Refused to load the font 'data:font/woff2;base64,d09GMgABAAAAAQ9AABEAAAADHJgAAQ7aAAI99AAAAAAAAAAAAAAAAAAAAAAAAAAAGoNsG7l0HIG3FgZgAPR8CIEoCZwMEQgKh+94hu1bATYCJAOqPAuVIAAEIAWJZgfyWgyBVlu0z5KoNW6bfpX07oBDtyGA9WZq1fS/hyuRDtl92jGxKDaP1rmIQJ59/RqL3e4TAd0GCKhS0frTS/b///////+25IvsZ0mgL0lb/o6pIDJxnLrtfgyCgozAPEajhFzBa2/aOucu9XnIlFLVHDyyGNv2rdI0vo+UYqTjaWTn42QUR0o0yhEiZECcIap8WTALSteOrRTtQ53H20wHXlvl251DHPibXPHLqceCluwMFHyHZuXWeyd96iA5NnGR2pW31EC8wsEggIODBqiClHYoAeBZdTACaDS/RaWJzAFaOzrmCKFXUraT9b6tNfvVxzrdZ+4bjcUe9GQvQq4I7G4BurHUU/elVVTyMYuA...qkZ1z3mcLaeoTE364XEhvyLpcyHJEyZcr76wo25UfrlpeM7Avp+h7UhqQXUjtNgK6ybGsVilGsYyazkDrP1FLWRm7gaeEJfQNijGQFw57iGi4g8WIW89ganSFjLn6Hb1qD30wlLOX6zjwn/WbMYb/wSlgg/0jLZEx+h2aek9+eJWzJ/vbLSMhlhmX8D8SSIAO/F0j+qzAsiaZ8NeSCZ/P//Hjs+BBfRmE0FIeUd5RGqUfBm3LGReXIWBgbMW6l8uBMmklnqUSPYi0ADhqFEEDoX/Mgod++9csssmUQ/4DzJwnaEWo4sw1RAaKIRW5ohzbevwEc2MmAguTQ/RzADMv8M8cW3BbaFUo+Ok+UguRetirPLf1xan/v/9RuqXRTvLXRrbInWoEfo5A4sH8xtuif0ALgkB+NUf2JgjbPeoGIekpmLZfocJkFh5yp22563esKvQoYWdOcFiOFP8/akxmhQDmVzcrsEqVk/geS/faGtOnHuMr+mUovQ+khAAA=' because it violates the following Content Security Policy directive: "font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net". registration:1 [Report Only] Refused to load the font 'data:font/woff2;base64,d09GMgABAAAAARYMABEAAAADNLgAARWlAAI99AAAAAAAAAAAAAAAAAAAAAAAAAAAGoNsG7pOHIG3FgZgAPR8CIEoCZwMEQgKiIo0h4Y9ATYCJAOqPAuqQAAEIAWJTgfyWgyBVltY3LIGqsXYjK2qvWpyhESQEET+OZ62TSCvJL+7TK+gNYZJj7eKm9g1ogdsSVfuZt5+gG1L+Wa3A/zxue9msv///////////39j8iPW9jNv4c0erICo4Jla4lHWN+0LTbz4EH2QlLyk6FqxuOwwqVEjrZrqrnSzBlVsOwILVDbvJAQJcwtSmQoU700WdfA6VhTvgdF6G3zslrJac22OGy9bwCXcUb+pxd51PZea04McQ0tZKuIjPYHN4jv6SIaCmeN8Qu68nLykChxsyEGP0kujGx/T+aJm5rTAA7rim1qqt8Zvkiy0d/7kF6TzPcv3VX2F9f7n320lCZKQhFYAXU7qKpdBdRe3b5wK...OS+W95/JTc9ghd/kthfxPhrAbS/t26aRD+B8P+BkaQAobbSGx4jGvSHEoTjsZjUNd8pvCXUjSXjeKa8Kw0RNRkkS+l2Cd0iGliyE791jyASBTVqZzKFP4hkcjIaQzXEeOYW/ERPkUGQ6Y81Bzv6OD5RuERA7w8RHIvLcL0w9Do4IeeeajYoV9k4URG0PA9xPymoYYL+J0SMm+s+eM4EbxWPPPp8RTHqB59E5oB/5JIm9SkHfrFg7deRN80zIW5McO/Sd7n0lw6TtvM6+ksYQ8apQIQqmbaHAipWctqFQ6ropHwJ758ufUSgQ+eG+ifiqPOu0tQR90NzL7lp4D6afYzS08Ki8A3QjWCVCfwGGtcs42JFUIlL/8ZXx/1sNuHvb44ftm/LEYocCDUqxsRH0Goex8EMO97IPU/kWsiMjL1aD4f0v+WBpCzMO+V6VLwXPeOJkEkS3rmOVpK764j9FdsZZ1KYSUzTxDA3EvsrTXy/ieOZf+i+FaAmAcAAAA=' because it violates the following Content Security Policy directive: "font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net".
Right now, the home-page has been the thing we focused on, but we basically want the same behavior that we have on the home page on reader pages.
Handle SSR on the reader pages on par with the home-page and handle indexing on the reader pages on par with the home-page.
- State "DONE" from "TODO" [2022-10-06 Thu 08:01] \\ Siarhei provided updated sketches at https://www.figma.com/file/DenroEWfValwUxKZJdtLW7/formation.tools-(Copy)?node-id=343%3A598&viewport=-1853%2C254%2C0.14
Remove emphasis from Visit Source button and add a clear Follow URL CTA (Call-to-Action).
As a user, I press the Visit Source button expecting to be lead to the URL entered inside of the URL input box. This is a usability problem. We need a strong CTA near the URL input box that does exactly what a typical end-user would expect: lead one to the page that matches the entered URL.
💡 Try to enter https://gitlab.com/formation.tools/eng/proto-01/-/raw/hack/README.org
into the URL input control to see what we mean. When pressing the blue button it only navigates to the source of the current page. The lack of a connection between that prominent button and the input text is a bit confusing. Following the entered text is only possibly by pressing Enter
.
comments
💡 The URL input textbox will very likely disappear once we allow people to login with their own GitHub and GitLab accounts. At that point, users will just navigate to the files they want through the sidebar menu. Since we're not there yet, the URL input box is an interim solution for now so it doesn't have to be perfect but should be at least "usable enough" -- as in, not confusing even the people who made it. We improvised input bar without the guidance from design so this explains the poor UX. Now we're trying to improve since we're hitting a point where the previous solution is no longer good enough.
Users can currently press enter to continue upon entering some text into the URL text input. This isn't always very intuitive since there is a big blue button begging to be clicked right next to it. 😅 This button doesn't quite do what one is expecting, so we want there to be a button for the folks who wouldn't intuitively go for the enter key that does exactly what 80%+ would expect from such a a button without reading its label.
I'm thinking that a good starting point would be to move the Visit Source button to an icon-only source button. Perhaps we can add it as an option to the perspective-selection button groun (where we can switch between Prose and Kanban views). But we're also open to a different design since I would understand if you argue it doesn't belong there since Kanban and Prose are both in-app perspectives and this Visit Source action would lead one out of the app and perhaps deserved a different level of emphasis for that reason.
For reference, you can see how GitHub approached a similar problem on page https://github.com/rasendubi/uniorg/blob/master/README.md?plain=1 in the bar with the Raw and Blame buttons.
This bar has Display the source blob and Display the rendered blob buttons as well. The Raw button does what we previously did with the Visit Source button: it leads one to a different place on the internet that serves as the source of content. The Display the source blob button just displays the source of the file in-app (never leaving the main app).
I'm not sure on whether we eventually 1) lead people out of our app to the source or if we 2) offer them a source-only representation in-app, but since we already handled option 1 with the Visit Source button, we can just model the behavior of GitHub's Raw button which does more or less the same thing. This approach keeps the implementation step easier as we already have the Visit Source bit implemented and would simply need to restyle it. Rendering the source in-app, however; may require us to design another perspective for that behavior that we should schedule as a follow-up task and complete at a later point in time.
In Add a clear Follow URL CTA, we touched upon eventually providing an in-app source-only view which would allow folks to see and copy the source code conveniently.
As a user, I want to be able to quickly copy the source code for a given page.
We currently enable this by allowing users to follow a link to the Raw representation (the exact version of the file that we read for parsing but this is technical detail so just ignore this). Going to the raw page, leads a user out of the app and the user has to copy the content by selecting it all (which is easy enough with keybindings) and copying it (also easy enough with keybindings).
We want there to be a perspective where the end-user can:
In the spirit of keeping things simple and not trusting any input, the user should expect some basic checks to prevent them from doing things that must not work.
:ID: 8f0aaa91-7845-4dee-bb3e-7f256d7d13fe
Loading of files such as https://raw.githubusercontent.com/sachac/emacs-news/master/index.org currently times out with status 413 and the following logging output. 😰
[GET] /r/aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3NhY2hhYy9lbWFjcy1uZXdzL21hc3Rlci9pbmRleC5vcmc 18:53:40:72 Function Status: None Edge Status: 500 Duration: 29697.85 ms Init Duration: N/A Memory Used: 741 MB ID: fra1:fra1::4v64n-1663095220604-c6e5ca4e426f User Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 url aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3NhY2hhYy9lbWFjcy1uZXdzL21hc3Rlci9pbmRleC5vcmc 2022-09-13T18:54:05.666Z f70e20ab-d399-4148-8d00-5a652bd01208 WARN Warning: data for page "/r/[url]" is 7.16 MB which exceeds the threshold of 128 kB, this amount of data can reduce performance. See more info here: https://nextjs.org/docs/messages/large-page-data [ERROR] [1663095250473] LAMBDA_RUNTIME Failed to post handler success response. Http response code: 413.
This problem may be solveable by incrementally parsing larger pages and gradually streaming chunks of parsed content back to the client but I'm not sure how easy it would be to make this happen.
Avoid people from shooting themselves in the foot by implementing basic heuristics to check that GitHub and GitLab links are raw links:
In case the links are not raw, present an alert for now with the error. We'll show something nicer in the future when we have time.
Right now we run HTML input through the Org parser if the URL was invalid and pointing towards a regular webpage. We should at the very least check if we can discern plaintext input from HTML input by checking the mimetypes in the response.
- State "DONE" from "TODO" [2022-09-04 Sun 16:22] \\ Implemented and merged to mainline
In order to populate the Kanban view (where the first TODO may not be part of a first-level heading), we want to be able to extract all top-level tasks from a document.
For the following snippet snippet we can list 3 top-level tasks, being:
* Background * Tasks ** TODO Think about what to do ** Something in the future, not sure yet *** DONE Procrastinate *** TODO Figure out what is in the future **** TODO Collect underpants **** TODO ... **** TODO Profit * Conclusion
Any of the subtasks should only be visible when navigating into that level by clicking on the parent card.
- State "DONE" from "TODO" [2022-10-15 Sat 15:37] \\ This has been solved with a mode selection component, named ModeSwitch
For user to switch between the different modes/perspectives/views, let's just call it perspective, of a document we need something better than the toggle so let's define a button group that looks a bit like the Preview/Code tablist on pages such like https://tailwindui.com/components/application-ui/navigation/tabs to make it much clearer which view is currently active.
comments
I guess perspective is clear enough as it provides a perspective on a document. Modes may introduce confusion if we were to support vim-style modal navigating/editing at some point and views already has a meaning with regards to interface development that could confuse us, as a team, or readers. Perspective seems like a word that isn't used too often within the realm of text-editing and interface development to allow for easy misunderstanding -- I hope 😅.
- State "DONE" from "TODO" [2022-09-25 Sun 22:12] \\ Top bar is the place for now, but we will revisit this at a later point to localize the perspective-switch in the perspective view itself.
Reach out to @siarhei and team to figure out where to best place the perspective control.
- State "DONE" from "TODO" [2022-09-25 Sun 22:13] \\ Not very clear. We need to implement images to clarify this even more.
The opening section of this document points people towards the Toggle to switch views. Once's we've been able to rip that out, we can update the text to point viewers towards the new controls.
- State "DONE" from "TODO" [2022-08-31 Wed 15:51] \\ Merged into mainline a few days ago
- State "DONE" from "TODO" [2022-08-31 Wed 15:51] \\ Merged into mainline along with the Link component
- State "DONE" from "TODO" [2022-09-07 Wed 21:53] \\ It's GPL3 now. It's a prototype build so we'll have to get over it. The idea of keeping this GPL3 is a fun challenge as well.
At the moment, the front-end will have to be released under GPL3 as far as we understand. This represents a risk for the commercial interests of the project and thus it is important to consider how we design the system going forward. Some approaches would be to:
The uniorg is licensed under GPL3 and possible must be because it represents a translation (see GPL3 text for the definition of a translated program) of the org-element.el implementation in Emacs also under a GPL license.
The alternative org-parser is also licensed as GPL3 so, apart from the effort to integrate, this would not be a valid option either.
- State "DONE" from "TODO" [2023-02-05 Sun 17:00] \\ Already licensed under GPL3 so this topic is covered.
If we cannot find a permissive parser, we will have to license the front-end project under GPL3.
- State "DONE" from "TODO" [2023-02-05 Sun 17:08] \\ We already know how to tackle this issue so let's just mention the solution.
In order to avoid having to bundle everything into a GPL3 app, we can provide all base front-end logic in the app and then take the marketplace approach and offer integrations with services (our own or third-party).
We can ship back-end logic into a separate app and then use the NextJS Rewrites facility to points to the necessary endpoints, wherever they are.
All data is contained in files which we'll call Documents. These Documents are currently retrieved directly from a third-party HTTP server that serves us the raw plaintext, but we want to hit our internal documents endpoint instead that will provide us optimized content to work with.
Problems with the current design:
We eventually don't want to show the content for every raw/plaintext page on the web but only for the pages that we have some write-access to git repos.
We will therefore need a data pipeline that can ingest data from repos (on a schedule and triggered by hooks), and populate our internal docs structure such that we can provide fast retrieval.
- State "DONE" from "TODO" [2022-11-12 Sat 15:32] \\ Stub basic properties of inbox
An inbox entry needs to have the following properties:
As a knowledge worker, I want to be able to quickly record web pages/resources and catalog them for future reference within a single interaction.
Can we allow a user to SSO-login through a regular tab and then use that session data for the extension? AFAIK there is some scoping between tabs and extension and we don't want to complicate the sign-in experience in the extension.
Ideally, we can facilitate SSO from a (Google, GitHub) session that is already live within the browser.
See what Quatro has done https://quarto.org/docs/get-started/hello/vscode.html.
:COOKIE_DATA: todo recursive
- State "CANCELLED" from "IN_SCOPING" [2023-01-06 Fri 14:47] \\ This was a gimmicky thing that popped up in conversations between David and Alex. No headspace to focus on play with this since we have to clarify focus on the story and ship v0.1.0 so I'm dropping this from the board.
- State "DONE" from "TODO" [2022-12-09 Fri 10:14] \\ Done on-stream
👋🏿 This is an Org file, that markup format associated to Org-mode the so-called killer feature of Emacs 🐃🙊 but that's not the point. I've hated Confluence, Notion and consorts 😠 with a passion for a while and was keen to have a way to collaborate through plaintext with folks. It's obviously unfair to ask folks to learn Emacs, so I figured building a tool that allows folks to read Org files is at least something I can do to stop my whining about Confluence, Notion, et. al in the "shut up, fix it" spirit. This is the scrappiest prototype that I could whip up to start flirting with the idea. 🌱
The cool thing is that such Org files can be presented differently to provide a better overview to you, the reader. We can look at them as prose, as a kanban board (which displays the entries in this document that are tasks), as an calendar (for a view of everything that has dates/times associated to them) and more.
The toggle top-right can be flipped to switch between prose and Kanban view on this doc. Yeah, it's a horrible UX but it was the quick-and-dirty thing that we could built in there. 🙈
The "big idea" 💡 here is that most of this is kept as plaintext and that a git repo is the conduit to keep track of your content and changes. For most users, this is not interesting so we can provide them a web tool that abstrats all this git and Org stuff away from them but will allow the other weirdo hackers like myself to just live in their editor of choice and not worry about context-switching between their editor and slow web-tools.
Many of us probably agree that git is a great way to collaborate and track versions of ever-changing text. We don't need our data stored somewhere in someone else's database and request permissions through clunky APIs to access it when you can just have it on a repo on your machine. We want to offer a an easy API and good utils to just do whetever the heck you want with the files on your box. Honestly, with the development in tooling recently, decent search or other capabilities should be easily serviceable by your own machine through different utils (some perhaps built in Rust). Our computers are definitely packing enough heat to handle all the compute needed to for a local knowledge base.
Depending on how poorly (or warmly) this is received, I will decide how to move forward, so that feedback is needed for me to know if this is a moderately sensible idea or totally ludicrous. 😅 We're thinking about supporting collaborative editing in the future. I'm already setting up a coffee meet with the author of ProseMirror to explore this. Imagine being able to look at a prose, kanban or calendar diffs on the state of an Org-file when you come back from vacay 🏖️ to get a quick sense of all the things that have changed. This is also on the wishlist.
So in the spirit of scrappy prototypes, the repo is online and GPL3 (because I'm using @rasendubi's uniorg)... Roast away! 🔥
digraph G { compound=true; //bgcolor="transparent" <<infra-domains>> <<infra-vercel>> <<infra-plausible>> <<infra-associations>> }
file:gen/infra.overview.png
comments
🧭️ Everything that is dotted and in red is on the way out! Things that are gray and dashed are ideas to likely be introduced Everything green and filled is live at the moment and everything else is just there but not highlighed to keep the diagram from being too cluttered.
file:gen/infra.overview.png
We use Vercel as our compute provider because it is easy.
subgraph cluster_scope_vercel { label = "Vercel scope" <<infra-vercel-apps>> }
The prototype app is our current drop-in point -- the one place where you end up for everything. Since we're in an early testing/validation stage, we are keeping this scrappy for now until we know what to really build.
node_vercel_proto [shape=box,label="proto-01",style=filled,color=lightgreen];
We anticipate needing different APIs at a later stage for different functionality, e.g.:
All these APIs and integrations can be separate Vercel apps just to keep things more cleanly isolated. That way we can decouple deployments of our different services.
💡 This is just a concept section of APIs that we can implement at some point. As this section is commented, the dot diagram code below is not rendered into the infra overview at the top of this section.
node_api_v1 [shape=box,style=dashed,color=lightgrey,label="API v1"]; node_api_v1_data_git [shape=box,style=dashed,color=lightgrey,label="API v1 git data svc"]; node_api_v2 [shape=box,style=dashed,color=lightgrey,label="API v2"];
We started off by directing all of our domains to a rewrite/proxy service where we resolved paths. This allowed us to gradually build out our stack with different services that we can mount under arbitrary paths on our target domains but was challenging to develop with. On , I had a call with Hassan about DX and we decided to move all rewrites to the prototype project directly.
By using the rewrites facility in next.config.js, we should be able to reference environment variables which can be defined using .env.local files during development (and pointed towards any arbitrary destination) and through the Environment Variables platform facility on a per-branch level in the Vercel platform. This should provide us the needed control and a tenable DX.
subgraph cluster_vercel_conf { style=dotted; color=red; label = "vercel-config"; node_vercel_conf [shape=box,label="vercel.json"]; }
Some of the rewrites that we conduct are rewrites to external services. The last defined rewrite and therefore our fallback path is the prototype app for now.
node_vercel_conf -> node_api_v1_data_git [label="/api/v1/data/git",style=dashed,color=lightgrey,ltail=cluster_vercel_conf]; node_vercel_conf -> node_api_v1 [label="/api/v1/*",style=dashed,color=lightgrey,ltail=cluster_vercel_conf]; node_vercel_conf -> node_api_v2 [label="/api/v2/*",style=dashed,color=lightgrey,ltail=cluster_vercel_conf]; node_vercel_conf -> node_vercel_proto [label="/*",ltail=cluster_vercel_conf];
Eventually, we can use the rewrites/proxy mechanism to mount all of our own services and 3rd-party services under friendlier paths, for example (and bear with me, this is just me musing):
We have the following domain names:
subgraph cluster_domains { label = "Domains" node_subdomain_prod [shape=box,label="proto.formation.tools"]; node_subdomain_hack [shape=box,style=dashed,color=lightgrey,label="hack.formation.tools"]; node_subdomain_staging [shape=box,style=dotted,color=red,label="staging.formation.tools"]; node_domain_a [shape=box,label="formation.tools"]; node_domain_b [shape=box,label="forto.dev"]; node_domain_c [shape=box,label="f7n.io"]; }
We use the Rewrite (Proxy) facility from Vercel to manage these domains through a single Vercel project -- a convenient drop-in point to serve as our nice traffic-direction point.
node_subdomain_prod -> node_vercel_proto [label="Production"]; node_subdomain_hack -> node_vercel_proto [style=dashed,color=lightgrey,label="branch: hack",label="branch: hack"]; node_subdomain_staging -> node_vercel_conf [lhead=cluster_vercel_conf,style=dotted,color=red,label="branch: staging"]; node_domain_a -> node_vercel_conf [lhead=cluster_vercel_conf,style=dotted,color=red,]; node_domain_b -> node_vercel_conf [lhead=cluster_vercel_conf,style=dotted,color=red,]; node_domain_c -> node_vercel_conf [lhead=cluster_vercel_conf,style=dotted,color=red,];
We use Plausible for analytics. On the Plausible end, only two artifacts are relevant:
subgraph cluster_scope_plausible { label = "Plausible" node_plausible_v1_api_event [style=dashed,color=lightgrey,shape=box,label="/api/event"]; node_plausible_v1_script [style=dashed,color=lightgrey,shape=box,label="/js/script.js"]; }
In order to minimize the potential for loosing data due to ad-blockers, we proxy our own paths onwards to Plausible as suggested in the Plausible documentation on dealing with missing data (link captured on ).
node_vercel_proto -> node_plausible_v1_script [style=dashed,color=lightgrey,label="/js/script.js",ltail=cluster_vercel_conf]; node_vercel_proto -> node_plausible_v1_api_event [style=dashed,color=lightgrey,label="/api/v1/event",ltail=cluster_vercel_conf];
- State "DONE" from "TODO" [2022-09-14 Wed 11:19] \\ Resolved with the help of Sebastian from Vercel. Core point was that we only defined a non-root rewrite =/:match*= instead of defining a root rewrite =/=.
Stubbed vercel-config but it is broken. We debugged this together to no avail and I just filed Case #00097879 with Vercel Customer Support.
In order to ensure that we hit our analytics back-end, we can define some rewrite rules towards Plausible.
The Plausible config is hard-coded in the codebase and pollutes our analytics because it also records localhost or staging traffic.
- State "TODO" from "TODO" [2022-09-14 Wed 11:20] \\ Not actively tackling this since we are too early stage to worry about payments at the moment. We're focusing on the big picture bits for now.
Wants:
This is a product concern so I'm signing this over to the product scope which is in the shared intel repo.
Use these as tools to guide you through every engineering choice.
We are a small team and probably always will be (kind of like a small formation of synchronized dancers 🩰 or a well-tuned squad of special forces operatives). Because of our size, we have to build solutions that minimize manual effort on remedial tasks.
Refer to [[https://sre.google/sre-book/eliminating-toil/][Eliminating Toil [Google SRE Handbook]]].
For anything we attempt to engineer, we make it a habit to design first by drafting a document that outlines the problem, potential solution candidates with reasons for why the candidates are preferred or not. This document is already an example of such a text because we have been "thinking out" ideas in this doc. 😅
💡 Really try to not initiate implementation work without first going through a design effort!
comments
👨🏿💻 License to Hack: Sometimes a quick and dirty prototype is arguably part of the "design effort" and maybe even a required step in order to understand a problem and related constraints (for example tooling constraints) better. Somethings are simply hard to sketch out in advance or phrase in clear terms, so, in the spirit of show/hack-and-tell, feel free to whip up the occasional prototype to serve as "documentation" to communicate a design.
When engineering software, start with a README in a repository and document the problem and some solution candidates.
The following is an example of a minimal README for an imaginary project. #+begin_src org ,#+TITLE: Org API ,* Problem - Org is a rather extensive (complex) standard - Changing source on a per-character basis is leaves room for "breaking the format" (e.g.: when necessary punctionation is altered - From a user-perspective, Org operations happen on an Org-primitives (e.g.: we add text to a heading, we remove a link, we boldface a phrase of text) which a renderer or editor will need to understand ,* Solution Options - find Org parser that is usable with JavaScript (because we're building something for the browser) - org-parser (ClojureScript) based on instaparse (ClojureScript) - built with instaparse (seems like a mature parsing toolbox) - written in ClojureScript (should be compileable to JavaScript and reuseable in other apps, need to explore this) - implemented through expression of *BNF grammar* (a practical abstraction for the parsing problem) - developed by 200ok.ch (respected consulting firm with plenty of Org and ClojureScript experience) - implement own Org parser - from scratch (too expensive, not core business, little value to be added to project) - in unified ecosystem tooling (implementation of large Org syntax would introduce a lot of work that doesn't directly add value to the project) #+end_src
When any topic needs to be documented, find the one place where most will think to look for this information.
For a software project, this will be the git repositories or a more general architecture document in a dedicated knowledge base or another repository.
For a product idea, this will be the place were such ideation typically happens like a product management or project management tool.
Try to avoid replicating information because synchronizing multiple sources is a hard chore. Think: DRY!
We don't all process information the same way. When documenting information consider the following modalities:
Write in a manner that feels natural to hear such that folks using TTS (text-to-speech) tools can follow along.
Since search mostly functions through text, try to accompany every non-text artefact (e.g.: video, diagram, photograph, audio snippet, etc.) with human-readable description.
Since we don't all parse information best through text alone, try to provide diagrams when possible for those who need visual cues. Complement your diagram entries with sufficiently descriptive alt-texts or summaries such that the idea within the diagram is communicated even if the summary is read without awareness of the image. Do the same for any non-text artefact that you may add to a document.
comments
With complex diagrams, it may be easier to define high level