WIP: table of contents sticky on articles

This commit is contained in:
a 2024-10-05 23:56:30 -05:00
parent 33b1bb942a
commit 4629fad054
7 changed files with 108 additions and 13 deletions

View file

@ -1,4 +1,7 @@
.hugo-content { .hugo-content {
*:first-child {
margin-block-start: 0;
}
/* text */ /* text */
p, li {max-inline-size: 80ch;} p, li {max-inline-size: 80ch;}
h1 {font-size: 1.8em} h1 {font-size: 1.8em}

View file

@ -1,6 +1,5 @@
.layout-_default-single { .layout-_default-single {
.page-header { .page-header {
padding: 2em 1em;
hr {display: none;} hr {display: none;}
.container { .container {
max-width: 80ch; max-width: 80ch;
@ -34,8 +33,56 @@
margin-block-end: 0; margin-block-end: 0;
align-self: end; align-self: end;
} }
.page-toc {
display: none;
}
.page-content { .page-content {
max-width: 80ch; max-width: 80ch;
margin-inline: auto; margin-inline: auto;
} }
@media (min-width: 60rem) {
.container {padding: 0;}
.page[has-toc] {
display: grid;
grid-template-columns: 1fr minmax(37em, 80ch) minmax(17em, 20em) 1fr;
grid-template-rows: auto 1fr;
gap: 2em;
.page-header {
grid-column: 1 / span 4;
grid-row: 1;
display: grid;
grid-template-columns: 1fr minmax(37em, 80ch) minmax(17em, 20em) 1fr;
gap: 2em;
.container {
max-width: unset;
grid-column: 2 / span 2;
}
}
.page-toc {
display: block;
grid-column: 3;
grid-row: 2;
position: sticky;
top: 2em;
align-self: start;
justify-self: start;
max-width: unset;
details {
padding: 0;
background: none;
#TableOfContents {
padding-left: 0em;
}
}
}
.page-content {
grid-column: 2;
grid-row: 2;
max-width: unset;
.toc {
display: none;
}
}
}
}
} }

View file

@ -3,14 +3,20 @@
flex-flow: row wrap; flex-flow: row wrap;
padding: 0; padding: 0;
li:not(:first-child) { li:not(:first-child) {
margin-inline-start: 1.5rem; margin-inline-start: 1rem;
margin-block-end: 1rem; margin-block-end: 1rem;
} }
li::marker { li {
content: "/ "; list-style: none;
} }
li:first-child::marker { li:not(:first-child) {
content: ""; &:before {
content: " / ";
}
a {
display: inline-block;
margin-inline-start: 0.5rem;
}
} }
li:first-child { li:first-child {
margin-inline-start: 0; margin-inline-start: 0;

View file

@ -7,12 +7,15 @@ syndication = [
inReplyTo = [ inReplyTo = [
"https://github.com/w3c/activitypub/issues/439" "https://github.com/w3c/activitypub/issues/439"
] ]
toc = true
+++ +++
Continuing from (and expanding on) this GitHub issue: https://github.com/w3c/activitypub/issues/439 Continuing from (and expanding on) this GitHub issue: https://github.com/w3c/activitypub/issues/439
Recently I've been thinking about OrderedCollection again and how the way it's specified doesn't actually match up with the way it's defined, or with what you'd need for it to be a useful data type. Some issues I could identify are laid out below. Recently I've been thinking about OrderedCollection again and how the way it's specified doesn't actually match up with the way it's defined, or with what you'd need for it to be a useful data type. Some issues I could identify are laid out below.
{{<toc>}}
## Q1: Is it a List or a Set? ## Q1: Is it a List or a Set?
Put another way: can you Add an item into an OrderedCollection more than once? Put another way: can you Add an item into an OrderedCollection more than once?
@ -183,11 +186,13 @@ Now finally, we can send an Add and know for sure that it will be inserted in th
We now expect that the result will be `[4, 3, 2, 1]` instead of `[3,4,2,1]`. We now expect that the result will be `[4, 3, 2, 1]` instead of `[3,4,2,1]`.
We might also need a comparison function, but this is currently left unconsidered.
## Q3: Are there other ways to approach insertion into an OrderedCollection? ## Q3: Are there other ways to approach insertion into an OrderedCollection?
We might not *want* our OrderedSet to be a SortedSet specifically. We might want some specific ordering that is also specifically not sorted according to any criteria or property. We might not *want* our OrderedSet to be a SortedSet specifically. We might want some specific ordering that is also specifically not sorted according to any criteria or property.
### Adding a new item at a specific position ### Inserting a new item at a specific position
In the most basic case, this might be doable with a property like `insertAfter`? This would allow us to specify where in the `@list` to insert the new item. In the most basic case, this might be doable with a property like `insertAfter`? This would allow us to specify where in the `@list` to insert the new item.
@ -238,9 +243,13 @@ This can be fixed by changing the `Add` to an `Insert`. Now, the server has to s
We could also define an `insertBefore` property, but I think this isn't necessary and overall just complicates things. Generally when updating a linked list, you traverse it going forwards; the `@list` of `orderedItems` is also presented in forward ordering. It therefore makes the implementation simpler at a data structure level to only define `insertAfter` and no `insertBefore`. We could also define an `insertBefore` property, but I think this isn't necessary and overall just complicates things. Generally when updating a linked list, you traverse it going forwards; the `@list` of `orderedItems` is also presented in forward ordering. It therefore makes the implementation simpler at a data structure level to only define `insertAfter` and no `insertBefore`.
One remaining caveat has to do with ActivityPub delivery. In the case that we send an activity to our local server via C2S, we are not guaranteed that it will have any side effects processed; the activity may be malformed or otherwise not processable. But the presence of addressing properties like `to`, `cc` and `audience` will cause the activity to be delivered to the recipients regardless of whether it had any local side effects -- perhaps the remote server will know what to do with the activity. If the remote server also doesn't know how to process the activity, then you end up with a no-op on both your local server and on the remote server. Perhaps some human reading their `inbox` might be able to derive some meaning from it...? One remaining caveat has to do with ActivityPub delivery. In the case that we send an activity to our local server via C2S, we are not guaranteed that it will have any side effects processed; the activity may be malformed or otherwise not processable. But the presence of addressing properties like `to`, `cc` and `audience` will cause the activity to be delivered to the recipients regardless of whether it had any local side effects -- perhaps the remote server will know what to do with the activity. If the remote server also doesn't know how to process the activity, then you end up with a no-op on both your local server and on the remote server. Perhaps some client reading their `inbox` might be able to derive some meaning from it...?
### Moving an item to a new position in the list? #### Inserting multiple items? (WIP)
Maybe we could do `"object": {"@list": [...]}` but this might not be friendly to LD-unaware processors. Also this doesn't allow for inserting multiple items at different locations, but maybe those should be separate Insert activities/operations anyway.
### Moving an item to a new position in the list? (WIP)
Just like we defined an Insert activity earlier, we might want to use something like a Move activity to reorder our OrderedCollection. Say we have a list in the form `orderedItems: [3, 4, 2, 1]`, perhaps by some permutation of aware or unaware servers leading to the error case from before. Well, how do we fix this? Just like we defined an Insert activity earlier, we might want to use something like a Move activity to reorder our OrderedCollection. Say we have a list in the form `orderedItems: [3, 4, 2, 1]`, perhaps by some permutation of aware or unaware servers leading to the error case from before. Well, how do we fix this?
@ -264,12 +273,18 @@ We might formulate "move `3` to be after `4`" with the following Move activity:
"actor": "https://domain.example/some-actor", "actor": "https://domain.example/some-actor",
"type": "Move", "type": "Move",
"object": "https://domain.example/objects/3", "object": "https://domain.example/objects/3",
"origin": "https://domain.example/some-collection",
"target": "https://domain.example/some-collection",
"insertAfter": "https://domain.example/objects/4" "insertAfter": "https://domain.example/objects/4"
} }
``` ```
This `Move` activity is equivalent to doing a `Remove` first, followed by a new `Insert`; compare to a `Move` activity normally being equivalent to doing a `Remove` first and then following up with an `Add`. This `Move` activity is equivalent to doing a `Remove` first, followed by a new `Insert`; compare to a `Move` activity normally being equivalent to doing a `Remove` first and then following up with an `Add`.
But we run into the same problem as before -- namely, it's not clear whether `Move` in this case means "Remove from `origin` and Add to `target`", or if it means "Remove from `origin` and Insert into `target`". So we probably need a new activity type for this kind of operation. I have no idea what to call it.
For now, the safest thing is probably to fall back to doing this in 2 activities: first, send a `Remove`, then send an `Insert`.
### Completely reordering the items ### Completely reordering the items
Given a complex enough OrderedCollection, you might find it easier to just create a new collection and Add or Insert items as appropriate... but this is not an option if you want to preserve the collection's id. (Well, it could be an option, but you'd have to define a mechanism to reassign identifiers first. Perhaps a `Migrate` activity could be defined in some other FEP? Alternatively, `Move` might work, but I'd be wary of overloading the semantics of "moving" an object. Of course, Mastodon's "account migration" feature already uses `Move` in exactly this overloading way, so...) Given a complex enough OrderedCollection, you might find it easier to just create a new collection and Add or Insert items as appropriate... but this is not an option if you want to preserve the collection's id. (Well, it could be an option, but you'd have to define a mechanism to reassign identifiers first. Perhaps a `Migrate` activity could be defined in some other FEP? Alternatively, `Move` might work, but I'd be wary of overloading the semantics of "moving" an object. Of course, Mastodon's "account migration" feature already uses `Move` in exactly this overloading way, so...)
@ -294,4 +309,15 @@ So given the same OrderedCollection, we might consider an `Update` that changes
} }
``` ```
In the case where an OrderedCollection is *unpaged* but just very very large, then the answer is to simply construct a similarly large Update activity. But the larger the collection grows, the more likely it is to be paged. So if the OrderedCollection is paged and the pages are reified, you might only be able to Update one page at a time. You might even find yourself needing to Move items between one reified page and another! So maybe you can construct a new OrderedCollectionPage and carefully update the `next` and `prev` links of what is effectively a linked list. The exact algorithm for doing this is left as an exercise for the reader, because this is already getting to be a very long post, and adding more examples for this increasingly niche and situation-dependent use case is going to make it even longer... eh, perhaps in a follow-up. In the case where an OrderedCollection is *unpaged* but just very very large, then the answer is to simply construct a similarly large Update activity. But the larger the collection grows, the more likely it is to be paged. So if the OrderedCollection is paged and the pages are reified, you might only be able to Update one page at a time. You might even find yourself needing to Move items between one reified page and another! So maybe you can construct a new OrderedCollectionPage and carefully update the `next` and `prev` links of what is effectively a linked list. The exact algorithm for doing this is left as an exercise for the reader, because this is already getting to be a very long post, and adding more examples for this increasingly niche and situation-dependent use case is going to make it even longer... eh, perhaps in a follow-up.
## Current summary and conclusions
- An OrderedCollection's items are expressed in JSON-LD as a `@list` but the data type is more correctly an ordered set, not an ordered list. This is because of the AS2-Vocab definitions. This means that items can only be included in a Collection at most once.
- Ordering is determined by time-of-insertion and can be signalled by something like a property `orderType` and some classes like `ForwardChronological` and `ReverseChronological` to indicate if the latest appended item will be the first item (LIFO/FILO) or if the latest appended item will be the last item (LILO/FIFO).
- We could define a new type `SortedCollection` specifically for collections that are not only ordered, but also sorted according to some criteria. Time-of-insertion doesn't matter here, as any newly inserted item will take its place within the sorting. We might define the `sortType` somehow using vocab terms again, but it is more useful to use `sortedBy` pointing to a property name, and `sortOrder` can be one of two classes, either `Ascending` (smallest first) or `Descending` (largest first). We might also need a comparison function, but this is currently left unconsidered.
- For cases where the collection is ordered but not sorted, we could define an `Insert` activity and an `insertAfter` property to point to the object after which the Insert's object will be inserted. Currently left unconsidered is the possibility of inserting at a specific index numerically, as well as the ability to `insertBefore`.
- Inserting multiple objects into an OrderedCollection also needs to be thought about.
- Moving items typically is equivalent to Remove then Add. We might need an activity that is equivalent to Remove then Insert, but I have no idea what to call it. For now, the safest thing is probably to fall back to doing this in 2 activities: first, send a `Remove`, then send an `Insert`.
- Items might be completely reordered with an Update targeting the orderedItems property but this can be kinda complicated when dealing with paged collections, especially if pages are reified. You will possibly have to construct new pages and manipulate the `next`/`prev` links between them.
- For now, we neglect to consider or define non-set types, such as List which is explicitly allowed to contain the same item multiple times.

View file

@ -1,4 +1,4 @@
<h{{.Level}} class="heading" id="{{.Anchor | safeURL}}"> <h{{.Level}} class="heading" id="{{.Anchor | safeURL}}"></h>
<span class="heading__text">{{.Text | safeHTML}}</span> <span class="heading__text">{{.Text | safeHTML}}</span>
<a class="heading__anchor-link" href="#{{.Anchor | safeURL}}"> <a class="heading__anchor-link" href="#{{.Anchor | safeURL}}">
[link] [link]

View file

@ -1,8 +1,11 @@
{{ define "head" }}
{{ end }}
{{ define "body" }} {{ define "body" }}
<body class="layout-_default-single"> <body class="layout-_default-single">
<main> <main>
<article class="page h-entry hentry" {{- if .Param "autonumbering" }} autonumbering {{- end }}> <article class="page h-entry hentry" {{- if .Param "autonumbering" }} autonumbering {{- end }}{{- if .Param "toc" }} has-toc {{- end }}>
<header class="section page-header"> <header class="page-header section">
<div class="container"> <div class="container">
{{ with .Title | safeHTML }} {{ with .Title | safeHTML }}
<h1 class="page-title p-name entry-title">{{.}}</h1> <h1 class="page-title p-name entry-title">{{.}}</h1>
@ -38,6 +41,16 @@
</div> </div>
<hr> <hr>
</header> </header>
{{ if .Params.toc }}
<aside class="page-toc toc section">
<div class="container">
<details open>
<summary class="toc-title">Contents</summary>
{{ .TableOfContents }}
</details>
</div>
</aside>
{{ end }}
<section class="page-content section"> <section class="page-content section">
<div class="container e-content entry-content hugo-content"> <div class="container e-content entry-content hugo-content">
{{ .Content }} {{ .Content }}