cobalt.rs
cobalt.rs copied to clipboard
[RFC] Pagination feature
Feature to paginate a collection of documents so we can avoid a landing page which is thousands of kilometer long :p
Requirements
- [x] Documentation
- [ ] Audit
- [ ] #680
- [ ] #679
- [ ] #681
Initial
Index pages should be able to index by
- [x] all content
- [x] A single tag
- [x] Categories
- [x] published_date year, year and month or year and month and day
Index pages should be able to sort by multiple factors at once:
- [x] published_date
- [x] weight (helpful for "pinning" some content to top)
- [x] title
Index page behavior
- Access to:
- Ability to create back, forward, first, last, and index-based links
- Not part of
collections.<name>.pages
Index page permalinks
- Able to define permalink with current pagination index
- Should we ensure there is a way to avoid a
0when that is the index?
- Should we ensure there is a way to avoid a
- Content we are indexing by is available for permalink
- categories list is only as long as where we are currently indexing
- Make date variables that aren't part of the index
nil? Like day variables when indexing by month. - Can you define category or publish_date for a index? How do these interact?
- Internal links (#440) would map to
index=0
Potential
Narrowing and nesting
- Should we support narrowing by additional tags?
- Nested indexing
- tags within a category
- published_date within category
Unplanned
Index pages should be able to index by
- Should we support hierarchical tags (like
parent::child)?
Proposal
Activation
This feature is activated in the frontmatter.
Default values and use shortcut include to activate indexing:
pagination:
include: Categories // default: `None`, can also be `All`, `Tags` `Dates`, etc
per_page: 10
permalink_suffix: "./{{index}}/{{ num }}/"
order: Desc
sort_by: ["weight", "published_date"]
trails:
before: 0
after: 0
Also, we can make the schema auto-adapt
pagination: truemaps topagination: Allpagination: categoriespagination: categories, tags
User-defined defaults can be set in _cobalt.yml by filling in the default,
pages.default, or posts.default pagination field, just leaving include as None
to avoid activating it for all pages.
Permalink
This permalink attribute is not to be confused with the one outside pagination section.
It defines the location of the generated indexes.
New permalink attributes specific to this context
include: a built-in representation of the paginated conceptnum: the index number
Paginator
Pagination would be accessible through a paginator object (total ripoff from jekyll's
pagination v2):
pages: The list of posts objects that belong to this pagination page.
total_pages: Total number of pages contained in this paginator.
per_page: Maximum number of posts or documents on each pagination page.
index: Current index.
indexes: All paginators available, one per index (used in cases like Tags, nil otherwise).
index_permalink: The relative Url path of the current pagination page.
total_indexes: Total number of pagination pages created.
previous_index_permalink: The relative Url of the previous page. Nil if no previous page is available.
next_index_permalink: The relative Url of the next page in the pagination. Nil if there is no next page available.
first_index_permalink: The relative Url of the first page in the pagination.
last_index_permalink: The relative Url of the last page in the pagination.
trails: The pagination trail structure
before: 0
after: 0
Once activated, a paginator object replace collection object in liquid template.
Tags
Moved to RFC https://github.com/cobalt-org/cobalt.rs/issues/549
Publication Date
Open Question
- How do we let users select how much of the date to index by?
- Have a field that is a list of either strftime or permalinks attributes (e.g.
["%Y', "%m"]would have year/month hierarchical indexes)
- Have a field that is a list of either strftime or permalinks attributes (e.g.
Prior Art
Jekyll
Deprecated method
https://jekyllrb.com/docs/pagination/
Activation in _config.yml:
paginate: 5
paginate_path: "/blog/page:num/"
Quoting the page:
This will read in blog/index.html, send it each pagination page in Liquid as paginator and write the output to blog/page:num/, where :num is the pagination page number, starting with 2. If a site has 12 posts and specifies paginate: 5, Jekyll will write blog/index.html with the first 5 posts, blog/page2/index.html with the next 5 posts and blog/page3/index.html with the last 2 posts into the destination directory.
Pagination V2
https://github.com/sverrirs/jekyll-paginate-v2/
- site configuration
- Sounds like most of this is actually defaults and can instead be set on the page:
- "All of the configuration elements from the _config.yml file can be overwritten in the pagination pages. E.g. if you want one category page to have different permalink structure simply override the item like so"
- Sounds like most of this is actually defaults and can instead be set on the page:
- page configuration
Advanced sorting:
sort_field: 'author:born'
pagination trails:
pagination:
trail:
before: 2 # The number of links before the current page
after: 2 # The number of links after the current page
Gutenberg
Paginator object: https://www.getgutenberg.io/documentation/templates/pagination/
A paginated section gets the same section variable as a normal section page. In addition, a paginated section gets a paginator variable of the Pager type:
// How many items per page
paginate_by: Number;
// Permalink to the first page
first: String;
// Permalink to the last page
last: String;
// Permalink to the previous page, if there is one
previous: String?;
// Permalink to the next page, if there is one
next: String?;
// All pages for the current page
pages: Array<Page>;
// All pagers for this section, but with their `pages` attribute set to an empty array
pagers: Array<Pagers>;
// Which page are we on
current_index: Number;
To activate pagination on a page: https://www.getgutenberg.io/documentation/content/section/
Tags and categories management: https://www.getgutenberg.io/documentation/content/tags-categories/
Hugo
https://gohugo.io/templates/pagination/
In configuration:
Paginate=10
PaginatePath=page
Activation in page:
{{ $paginator := .Paginate (where .Data.Pages "Type" "post") }}
{{ template "_internal/pagination.html" . }}
{{ range $paginator.Pages }}
{{ .Title }}
{{ end }}
or
{{ template "_internal/pagination.html" . }}
{{ range .Paginator.Pages }}
{{ .Title }}
{{ end }}
I've added some sections we'll want to fill out to help guide the conversation
Activation of pagination feature will also say on which index pagination will be enabled In _cobalt.yml:
So the things you can paginate on and their permalink are being defined in _cobalt.yml but how do I associate my index.md with one of these?
permalink: "/{{ year }}/{{ month }}/p{{ num }}"
When we crate an index by published_date, are they all only available by year/month or are we parsing the permalink to see what variables are being used?
added prior art, still thinking about your questions :)
Thanks!
Some feedback
- For jekyll, that form of pagination is deprecated. Seems like https://github.com/sverrirs/jekyll-paginate-v2 might be better to compare
- For gutenberg, that only talks about what variables are available but not how you set it up.
- https://www.getgutenberg.io/documentation/content/section/
- https://www.getgutenberg.io/documentation/content/tags-categories/
- https://www.getgutenberg.io/documentation/getting-started/configuration/
For gutenberg, that only talks about what variables are available but not how you set it up.
It's in https://www.getgutenberg.io/documentation/content/section/
In short you just set paginate_by to a positive integer in any section front-matter and it will paginate this section by this much. I'll add some docs on that I guess
updated proposal :)
I just update the jekyll v2 section with some interesting highlights.
In particular, I want to call out the fact thatthe global settings are defaults and not the main way to configure. I think its important that we allow pagination configuration to happen in the frontmatter. I then assume we should just rely on the frontmatter default system we have in the config file for pagination rather than creating another way of creating unique instances of pagination.
btw for Hugo, I think _index.md is what they use but this is one area where I feel there docs are lacking
https://gohugo.io/content-management/organization/#index-pages-index-md
Thank you :) What are trails?
I agree with the fact that _cobalt.yml should hold only defaults and same values can be override in fronts
What are trails?
https://github.com/sverrirs/jekyll-paginate-v2/blob/master/README-GENERATOR.md#creating-pagination-trails
Nice, I'll add this to our version :)
updated the config to pagination instead of index, more intuitive.
add the info that we can override the config values in frontmatter
Added trails, total ripoff of Jekyll pagination v2
I agree with the fact that _cobalt.yml should hold only defaults and same values can be override in fronts
I think it'd be a good exercise to document the frontmatter changes first and then to see how that plays out with the _cobalt.yml configuration.
Currently, the way to default frontmatter is to set is by filling in the default sections in the configuration. Maybe some of that will work with pagination defaults? Maybe some will still need a unique way to default in the config. Maybe some won't need defaults because they are too unique.
And if we do maintain defaults in the config, we should also consider whether the defaults are set globally and/or per-collection.
What do you mean? That I write the documentation first?
I'm not expecting documentation, just a description, like you did for _cobalt.yml, of what will be supported in the frontmatter after this.
Sorry for this huge delay, a video game sucked me out real life x)
I've updated the proposal with more paginator object description.
All the values in _cobalt.yml are available on each page front as well if needed to be override.
Understandable. I've had health issues in the family plus the CLI-WG sucking me away.
Not sure if its lack of clarity on my part or if it fell through the cracks since its been so long since we've talked, but I feel like some of my feedback hasn't been applied.
I think it'd be a good exercise to document the frontmatter changes first and then to see how that plays out with the _cobalt.yml configuration.
Maybe I'm misunderstanding something but this part:
all the values in config can be overriden in frontmatter of each page
Means all the values I defined in the _cobalt.yml are valid for the frontmatter as well. They will override the value set in _cobalt.yml
I hope the health issues are not too serious and that everybody will get better soon :-/
I'm trying to understand how Cobalt is working to identify where I will put the pagination code. Correct me if I'm wrong: I'll need to work on liquid.rs project as well in order to make this work, won't I? If I got it properly, Cobalt is only responsible for:
- opening the files
- parsing the front and the config and determine from where the files are read, and where they are written
- convert markdown to html if needed
But the rendering is done by Liquid.rs, isn't it?
So, for the pagination, I'll need to enrich Liquid to understand what to do with a paginator, and Cobalt will generate Collections for each needed index and determine their destination on the hardrive. Am I correct?
EDIT: this is totally wrong, no need to add anything to Liquid at least for the for block part
Also, I'm studying Liquid, and especially the for loop block. But I can't understand how collections.posts.pages is resolve in Liquid, can you give me some insight on this please? :)
Got it! It's in generate_doc in cobalt.rs when we put "collections" into globals :D
After studying the code and made the refactoring I understand a bit more the implication of pagination. I'm rewriting the proposal
Rewritten proposal posted! :D
I've made an attempt at closing our any of the comments that have been resolved to make it easier to browse the discussion. I feel like github needs a batch hide option :).
We've been going back and forth on this several times. I'm wondering if scheduling a discussion on gitter would be helpful. I'll go ahead and give this another shot.
How appropriate is it for pagination information to only be configured globally? Should it instead be configured on the actual page?
I suspect that it should be on the page.
The current pattern in cobalt for this is to define this configuration on the Frontmatter. In going this route, I'd expect a proposal to document these new Frontmatter fields. The only reason to discuss _cobalt.yml is to discuss how defaulting works which is of particular note here.
The way to globally set defaults for the frontmatter is then to use default, posts.default, and pages.default. The challenge is we don't want to globally enable pagination. A limited way of handling this is the user sets all of the default fields they want except an enable flag. They just then need to enable it and get the rest.
This makes defaulting limited to one kind of index, whether by year, category, etc. Its probably common enough for users to configure multiple, that we should consider our options. One option is to adopt jekyll's defaulting system which is based on global patterns, like "if the file uses this path, set these defaults".
An alternative system is to provide named defaults that a page opts-in to.
Here is a rough sketch of how named defaults might work
_cobalt.yml:
pages:
default:
data:
foo: bar
posts:
default:
- name: ""
permalink: "/something"
- name: "category_index"
pagination:
...
- name: "date_index"
pagination:
...
index.md:
default: "category_index"
---
Hello world!@
(field names subject to change, this is just for illustrative purposes)
With serde, we can make a fields support multiple types of values. I chose to make default be a Either<FrontMatter, Vec<KeyedFrontMatter>> (where KeyedFrontmatter adds name) rather than using a HashMap to avoid ambiguous situations where it might not be clear whether is is a Frontmatter or a HashMap.
Of course this is me exploring one line of solving these problems based on the current design of cobalt, allowing for consistent patterns for the user to be aware of. There are of course other possible solutions and it'd be great for us to consider and evaluate them. I think this gets down to recording requirements which should be the first role of a proposal which we don't have written down and agreed to yet.
I think this gets down to recording requirements which should be the first role of a proposal which we don't have written down and agreed to yet.
I've seen you have filled the section :)
My first need is to paginate collections.posts.pages and then in a second time, I'll need tag and year/month as well. I don't know if my first need is covered by one of your proposals.
How appropriate is it for pagination information to only be configured globally? Should it instead be configured on the actual page?
This is a good idea, at least for my use case, it's true that having global default value then only having a switch is not really logic. I was following Jekyll's scheme here. So I'm all for "only in the front configuration" idea.
- Should we support narrowing by additional tags?
In a second step, I think yes but maybe having a first version working then enrich it?
- Should we support hierarchical tags (like
parent::child)?
For me, tags have no hierarchy, categories are here for that.
- Should we support nested indexing, like tags within a category?
Hm… not sure, but even if yes, let's not do a mega big feature in one shot, let's paginate collections.posts.pages first and then move forward?
Otherwise the feature will take years to get out.
For me, tags have no hierarchy, categories are here for that.
Oh the joy of complex data models to map :)
Hm… not sure, but even if yes, let's not do a mega big feature in one shot, let's paginate collections.posts.pages first and then move forward?
Otherwise the feature will take years to get out.
I support that as long as we don't feel we have designed ourselves out of doing more.
I've seen you have filled the section :)
Well, started it. There are probably more for us to consider.
I've read what you wrote as requirements and I quite agree with all. I just added published_date with no year nor month in order to paginate collections.posts.pages.
I've read what you wrote as requirements and I quite agree with all.
Feel free to speak up if you do have a concern over an edit I make. This is a proposal, even my contributions, and all is fair game.
I just added published_date with no year nor month in order to paginate collections.posts.pages.
I redid that to just include all content. I assume your intent was sorting. That is orthogonal, so I created a separate item for it.
Updated the proposal with no global config and activation from frontmatter