Compare commits

...

20 commits

Author SHA1 Message Date
362f7d9b4f
Quartz sync: Sep 19, 2023, 7:39 PM 2023-09-19 19:39:11 +01:00
d676a6a37e
Quartz sync: Sep 19, 2023, 7:37 PM 2023-09-19 19:37:06 +01:00
Jacky Zhao
1bf7e3d8b3 fix(nit): make defaultOptions on explorer not a function 2023-09-19 10:22:39 -07:00
David Fischer
cc31a40b0c
feat: support changes in system theme (#484)
* feat: support changes in system theme

* fix: run prettier

* fix: add content/.gitkeep
2023-09-19 09:25:51 -07:00
Ben Schlegel
0d3cf29226
docs: fix explorer example (#483) 2023-09-18 14:32:00 -07:00
Ben Schlegel
6a2e0b3ad3
fix: bad visibility for last explorer item (#478)
* fix: bad visibility for last explorer item

* feat(explorer): add pseudo element for observer
2023-09-17 22:04:44 +02:00
Ben Schlegel
e67f409ec1
Merge pull request #479 from benschlegel/explorer-config
feat(explorer): add config for custom sort/map/filter functions
2023-09-17 21:36:04 +02:00
Ben Schlegel
4afb099bf3
docs: fix examples 2023-09-17 21:32:23 +02:00
Ben Schlegel
6914d4b40c
docs: fix intra page links 2023-09-17 21:20:09 +02:00
Christian Gill
af41f34bfd
fix(slug): Handle question mark (#481) 2023-09-17 11:02:00 -07:00
Ben Schlegel
5cc9253c41
docs(explorer): write docs for new features 2023-09-17 16:41:23 +02:00
Ben Schlegel
94a04ab1c9
fix(explorer): filter function in ExplorerNode 2023-09-17 15:51:08 +02:00
Ben Schlegel
9358f73f1c
fix: display name for file nodes 2023-09-17 12:41:06 +02:00
Ben Schlegel
f7029012df
feat: black magic
add config for `order` array, which determines the order in which all passed config functions for explorer will get executed in.

functions will now dynamically be called on `fileTree` via array accessor (e.g. fileTree["sort"].call(...)) with corresponding function from options being passed to call)
2023-09-16 21:58:38 +02:00
Ben Schlegel
fea352849c
fix: create deep copy of file passed into tree 2023-09-16 19:45:21 +02:00
Ben Schlegel
3d8c470c0d
feat(explorer): implement map fn argument
Add a function for mapping over all FileNodes as an option for `Explorer`
2023-09-16 19:35:27 +02:00
Ben Schlegel
31d16fbd2c
feat(explorer): integrate filter option 2023-09-16 19:18:59 +02:00
Ben Schlegel
036a33f70b
fix: use correct import for QuartzPluginData 2023-09-16 17:47:44 +02:00
Ben Schlegel
58aea1cb07
feat: implement filter function for explorer 2023-09-16 17:28:58 +02:00
Ben Schlegel
c7d3474ba8
feat(explorer): add config to support custom sort fn 2023-09-16 12:40:19 +02:00
17 changed files with 736 additions and 29 deletions

BIN
bun.lockb Executable file

Binary file not shown.

View file

View file

@ -0,0 +1,7 @@
## Conteúdo
- cache em arquiteturas multicore
- code profiling
- instruction-level paralelism
- Data dependency
- branch prediction
-

View file

@ -0,0 +1,37 @@
## Programa
1. Lógicas para especificação formal
1. [[T - Aula 2 - 18 Setembro 2023#Proposicional Logic (PL)| Lógica Proposicional (PL)]]
2. Lógica de Primeira Ordem (FOL)
3. Lógica relacional (RL)
4. Lógica temporal linear (LTL)
2. Técnicas de prova automática
1. SAT solving para lógica proposicional
2. SMT solving para lógica e teorias de primeira ordem
3. Model-finding via SAT para lógica relacional
4. Bounded model-checking via SAT para lógica temporal
3. Concepção formal de software com Alloy
1. Modelação estrutural
2. Modelação comportamental
4. Especificação e verificação de programas com Why3
1. Linguagem lógica, teorias (biblioteca) e prova
2. Linguagem de programas, especificação comportamental, e verificação de correção
3. Desenvolvimento de sistemas com estado
## Docentes
- Maria João Frade - [mjf@di.uminho.pt](mailto:mjf@di.uminho.pt)
- Alcino Cunha - [alcino@di.uminho.pt](mailto:alcino@di.uminho.pt)
- Jorge Sousa Pinto - [jsp@di.uminho.pt](mailto:jsp@di.uminho.pt)
## Avaliação
- 20% - Exercícios de avaliação
- 80% - Teste individual
Nota: As notas acima de 18 valores (quer na avaliação contínua quer no exame) terão que ser defendidas através da realização de um pequeno projecto.
## Planificação
- 6 Outubro - Entrega do 1º exercício de avaliação. Modelação em PL e FOL.
- 27 Outubro - Entrega do 2º exercício de avaliação. Modelação estrutural em Alloy.
- 10 Novembro - Entrega do 3º exercício de avaliação. Modelação comportamental em Alloy.
- 8 Dezembro - Entrega do 1º exercício de avaliação. Desenvolvimento e verificação de sistemas com estado em Why3.

View file

@ -0,0 +1,121 @@
# Conteúdo
1. [[T - Aula 2 - 18 Setembro 2023#1. Intro|Intro]]
1. [[T - Aula 2 - 18 Setembro 2023#1.1 SAT|SAT]]
2. [[T - Aula 2 - 18 Setembro 2023#1.2 Proposicional Logic (PL)|Lógica Proposicional]]
3. [[T - Aula 2 - 18 Setembro 2023#SAT Solvers|SAT Solvers]]
# 1. Intro
*Formal modeling* - formally represent the system and its properties in the syntactic conventions that the tool understands and can process.
Formal Logic = logical language (logical symbols + non-logical symbols) + semantics +proof system
### 1.1 SAT
<iframe title="Boolean Satisfiability Problem - Intro to Theoretical Computer Science" src="https://www.youtube.com/embed/uAdVzz1hKYY?feature=oembed" height="113" width="200" allowfullscreen="" allow="fullscreen" style="aspect-ratio: 1.76991 / 1; width: 100%; height: 100%;"></iframe>
The Boolean satisfiability (SAT) problem:
Find an assignment to the propositional variables of the formula such that the formula evaluates to TRUE, or prove that no such assignment exists.
- SAT is an NP-complete decision problem.
- SAT was the first problem to be shown NP-complete.
- There are no known polynomial time algorithms for SAT.
Usually SAT solvers deal with formulas in conjunctive normal form (CNF)
- **literal**: propositional variable or its negation A, ¬A, B, ¬B, C, ¬C
- **clause**: disjuntion of literals. (A _ ¬B _ C)
- **conjunctive normal form**: conjuction of clauses. (A _ ¬B _ C) ^ (B _ ¬A) ^ ¬C
> [!info]+ Cook's theorem(1971)
> SAT is NP-complete
## 1.2 Proposicional Logic (PL)
>[!note] Nota
>Esta secção basicamente só contém revisão de conceitos. Aconselha-se a ver a coisa rapidamente, porque é só a formalidade de lógica escrita por extenso.
Let $A$ be an assignment and let $F$ be a formula. If $A(F) = 1$, then we say **$F$ holds under assignment**, or **$A$ models $F$.**
We write A $\models F$ iff $A(F)=1$, and $A \not \models F$ iff $A(F) = 0$.
An assignment is a function $A$ : $V_{prop} \implies {0,1}$ , that assigns to every
propositional variable a truth value. An assignment $A$ naturally extends to all formulas, $A$ : **Form** $\implies {0,1}$. The truth value of a formula is computed using **truth tables**:
| F | $A$ | $B$ | $\neg A$ | $A \land B$ | $A \lor B$ | $A \implies B$ | $A \iff B$ | $\bot$ | $\top$ |
| --------- | --- | --- | -------- | ----------- | ---------- | -------------- | ---------- | ------ | ------ |
| $A_1 (F)$ | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 1 |
| $A_2 (F)$ | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
| $A_3 (F)$ | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 |
| $A_4 (F)$ | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 |
A formula $F$ is:
1. **valid** iff it holds under every assignment. We write $\models F$. A valid formula is called a *tautology*.
2. **satisfiable** iff it folds (true) under some assignment.
3. **unsatisfiable** iff it holds under no assignment. An unsatisfiable formula is called a *contradiction*.
4. **refutable** iff it is not valid.
> [!tip]+ Proposition
> $F$ is **valid** iff $\neg F$ is **unsatisfiable**.
- $F \models G$ iff for every assignment $A$, if $A \models F$ then $A \models G$. We say $G$ is a **consequence** of $F$.
- $F \equiv G$ iff $F \models G$ and $G \models F$. We say $F$ and $G$ are **equivalent**.
- Let $\Gamma = { F_1, F_2, F_3,... }$ be a set of formulas.
- $A \models \Gamma$ iff $A \models F_i$ for each formula $F_i$ in $\Gamma$. We say $A$ models $\Gamma$.
- $\Gamma \models G$ iff $A \models \Gamma$ implies $A \models G$ for every assignment $A$. We say $G$ is a **consequence** of $\Gamma$.
> [!tip]+ Proposition
> - $F \models G$ iff $\models F \implies G$.
> - $\Gamma \models G$ and $\Gamma$ finite iff $\models \land \Gamma \implies G$.
>
- $\Gamma$ is *consistent* or *satisfiable* iff there is an assignment that models $\Gamma$.
- We say that $\Gamma$ is inconsistent or unsatisfiable iff there is not consistent and denote this by $\Gamma \models \bot$.
> [!tip]+ Proposition
> - {$F, \neg F$} $\models \bot$
> - If $\Gamma \models \bot$ and $\Gamma \subseteq \Gamma '$, then $\Gamma ' \models \bot$
> - $\Gamma \models F$ iff $\Gamma, \neg F \models \bot$
- Formula $G$ is a subformula of formula F if it occurs syntactically within F
- Formula G is a strict subformula of F if G is a subformula of $F$ and $G \neg = F$
**Basic Equivalences:**
1. $\neg \neg A \equiv A$
2. $A \lor A \equiv A$
3. $A \land A \equiv A$
4. $A \land \neg A \equiv \bot$
5. $A \lor \neg A \equiv \top$
6. $A \lor B \equiv B \lor A$
7. $A \land B \equiv B \land A$
8. $A \land \top \equiv A$
9. $A \lor \top \equiv \top$
10. $A \land \bot \equiv \bot$
11. $A \lor \bot \equiv A$
12. $A \land (B \lor A) \equiv A$
13. $A \land (B \lor C) \equiv (A \land B) \lor (A \land C)$
14. $A \lor (B \land C) \equiv (A \lor B) \land (A \lor C)$
15. $\neg (A \lor B) \equiv \neg A \land \neg B$
16. $\neg (A \land B) \equiv \neg A \lor \neg B$
17. $A \implies B \equiv \neg A \lor B$
18. $A \iff B \equiv (A \implies B) \land (B \implies A)$
# 2. SAT Solvers
- There are several techniques and algorithms for SAT solving.
- Usually SAT solvers receive as input a formula in a specific syntatical format.
- SAT solvers deal with formulas in **conjunctive normal form (CNF)**.
- Most current state-of-the-art SAT solvers are based on the **Davis-Putnam-Logemann-Loveland (DPLL) framework**.
## 2.1 DPLL Framework
The idea is to **incrementally construct an assignment compatible with a CNF**, propagating the implications of the decisions made that are easy to detect and simplifying the clauses.
A CNF is satisfied by an assignment if all its clauses are satisfied. And a clause is satisfied if at least one of its literals is satisfied.

View file

@ -0,0 +1,88 @@
## Ficha 1
> [!help]+ Ex 1 (Configuração de produtos)
> Certos produtos, como é o caso dos automóveis, são altamente personalizáveis, mas podem haver dependências entre as configurações. Os clientes podem não estar cientes de todas essas dependências, e poderão escolher opções de configuração inconsistentes.
> Como são muitas configurações e muitas dependências, podemos usar um SAT solver para verificar se o cliente escolhe opções de configuração consistentes. Para isso, podemos seguir os seguintes passos:
>
> 1. Codificar as dependências entre configurações como uma formula proposicional $\psi$
> 2. Codificar as opções selecionadas pelo cliente como uma fórmula proposicional $\phi$.
> 3. Usar o SAT solver para verificar se $\psi \land \phi$ não é contraditório.
>
> Considere agora a seguinte dependência entre as configurações disponíveis para a personalização de um automóvel:
>
> *"O ar condicionado Thermotronic comfort requer uma bateria de alta capacidade, exceto quando combinado com motores a gasolina de 3,2 litros."*
>
> Será que um cliente pode escolher o ar condicionado Thermotronic comfort, uma bateria de pequena capacidade e não escolher o motor de 3,2 litros? Como poderia responder a esta pergunta com a ajuda de um SAT solver?
>
> > [!tip]- Resolução
> > Legenda de variáveis utilizadas:
> > A - ar condicionado
> > B - bateria alta capacidade
> > C - motor 3,2 L
> >
> >$A \land \neg B \implies G \equiv \neg (A \land \neg B) \lor G \equiv \neg A \lor B \lor G$
> >
> >$(\neg A \lor B \lor G) \land A \land \neg B \land \neg G$ ----- SAT?
> >
>
> [!help]+ Ex 3 (Configuração de computadores)
> Uma loja de eletrónica permite aos seus clientes personalizar o seu computador, escolhendo entre dois modelos de motherboards e dois modelos de monitor. Cada computador tem que ter obrigatoriamente uma única motherboard, um único CPU, uma única placa gráfica e uma única memória RAM. O computador poderá ou não ter um monitor. A personalização do computador deverá obedecer às regras:
> 1. A motherboard MB1 quando combinada com a placa gráfica PG1, obriga à utilização da RAM1;
> 2. A placa gráfica PG1 precisa do CPU1, exceto quando combinada com a memória RAM2;
> 3. O CPU2 só pode estar instalado na motherboard MB2;
> 4. O monitor MON1 para poder funcionar precisa da placa gráfca PG1 e da memória RAM2.
>
>Codifique este problema em lógica proposicional. Assinale claramente o que denota cada variável proposicional que introduzir e escreva um conjunto de fórmulas proposicionais adequado à sua modelação.
>
> > [!hint]- Resolução
> > Legenda de variáveis utilizadas:
> > $CPU_1; CPU_2; RAM_1; RAM_2; MB_1; MB_2; PG_1; PG_2; MON_1; MON_2$
> >
> > Problema modelado:
> > 1. $CPU_1 \lor CPU_2$
> > 2. $CPU_1 \implies \neg CPU_2$
> > 3. $RAM_1 \lor RAM_2$
> > 4. $RAM_1 \implies \neg RAM_2$
> > 5. $MB_1 \lor MB_2$
> > 6. $MB_1 \implies \neg MB_2$
> > 7. $PG_1 \lor PG_2$
> > 8. $PG_1 \implies \neg PG_2$
> >
> > 9. $MB_1 \land PG_1 \implies RAM_1$
> > 10. $PG_1 \implies CPU_1 \lor RAM_2 \equiv PG_1 \land \neg RAM_2 \implies CPU_1$
> > 11. $CPU_2 \implies MB_2$
> > 12. $MON_1 \implies PG_1 \land RAM_2$
>
> Indique, apenas por palavras, como poderia usar um SAT solver para testar as consistências deste conjunto de regras.
>
> Indique, apenas por palavras, como usaria um SAT solver para se pronuncias quanto à velocidade das seguintes afirmações:
> (a) O monitor MON1 só poderá ser usado com uma motherboard MB1.
> > [!hint]- Resolução
> > ??? $T \models MON_1 \implies MB_1$
> > $T, \neg (MON_1 \implies MB_1) UNSAT$
> > > [!info] $T \models F$ sse $T, \neg F UNSAT$
>
> (b) Um cliente pode personalizar o seu computador da seguinte forma: uma motherboard MB2, o CPU1, a placa gráfica PG2 e a memória RAM1.
> >[!hint]- Resolução
> >$T, MB_2 \land CPU_1 \land PG_2 \land RAM_1$ ---- SAT?
> [!help]+ Ex 2 (Distribuição de Gabinetes)
> Considere que temos 3 gabinetes e queremos distribuir 4 pessoas (Ana=1, Nuno=2, Pedro=3 e Maria=4) por esses gabinetes. Para codificar este problema em lógica proposicional, , vamos ter um conjunto de variáveis proposicionais $x_{p,g}$ com a seguinte semântica:
> $x_{p,g}$ é verdade sse a pessoa $p$ ocupa o gabinete $g$, com $p=1..4$ e $g=1..3$
>
> Considere que forem estipuladas as seguintes regras de ocupação de gabinetes:
> 1. Cada pessoa ocupa um único gabinete.
> 2. O Nuno e o Pedro não podem partilhar gabinete.
> 3. Se a Ana ficar sozinha num gabinete, então o Pedro também terá que ficar sozinho num gabinete.
> 4. Cada gabinete só pode acomodar, no máximo, 2 pessoas.
>
> Escreve um conjunto de fórumlas proposicionais adequado à modelação destas regras.
>
> >[!tip]- Resolução (por passos)
> >**Cada pessoa ocupa só um único gabinete.**
> > 1. para cada pessoa p = 1..4
> > 2. pelo menos um gabinete: $x_{p1} \lor x_{p2} \lor x_{p3}$
> > 3. no máximo 1 gabinete: $x_{p1} \implies \neg x_{p2} \land x_{p3} \equiv x_{p1} \implies \neg (x_{p2} \lor x_{p3} ) \equiv x_{p2} \implies \neg x_{p3}$
> > 4.

View file

@ -0,0 +1,159 @@
## Contents
1. [[T - Aula 2 - 19 Setembro 2023#Definition of Requirement|Definition of requirement]]
2. [[T - Aula 2 - 19 Setembro 2023#Functional requirements|Functional requirements]]
3. [[T - Aula 2 - 19 Setembro 2023#Non-functional requirements|Non-functional requirements]]
4. [[T - Aula 2 - 19 Setembro 2023#4. User and system requirements|User and system requirements]]
5. [[T - Aula 2 - 19 Setembro 2023#Recap|Recap]]
## 1. Definition of Requirement
The requirements express the users necessities and restrictions that are
placed on the system.
A **requirement** is a capacity that a system must possess, to satisfy the
users necessities.
The IEEE 610.12-1990 standard definition divides requirements into
two alternatives:
1. user needs
2. system capacities
Requirements can be divided into:
1. functional requirements
2. non-functional requirements
A r**equirement that for a stakeholder** can be perceived
as being **functional**, can be considered as **non-functional**
for **a different one**.
> [!info] A candidate requirement is a requirement that was identified by some elicitation technique.
## 2. Functional requirements
> [!tip] Definition
> A functional requirement describes a functionality to be made
available to the users of the system.
- This type of requirements should not mention any technological issue.
- Ideally, functional requirements must be independent of design and
implementation aspects.
> [!note]- Note: Cohesion and Completion
> Collectively, the set of functional requirements must be complete and
coherent. The set is:
> - *coherent*, if there are no contradictions among its elements;
> - *complete*, if it considers all the necessities that the client wishes to see
satisfied.
>
> Defining complete requirements is the most difficult attribute to
achieve or evaluate.
> [!note]- Note: Implicit and Explicit Requirements
> Implicit requirements are referred like that because the client may not be aware about these requirements.
>
> An **implicit requirement** is a requirement included by the development team, based on the domain knowledge that it possesses, in spite of not having been explicitly requested by the stakeholders.
>
> An **explicit requirement** refers to a requirement that was requested by the clients and that is represented in the documentation.
## 3. Non-functional requirements
> [!tip] Definition
> A non-functional requirement corresponds to a set of restrictions
imposed on the system to be developed.
- Alternative terms: quality requirement or quality attribute
- It establishes how attractive, fast, or reliable the system is.
- It includes time constraints, restrictions in the development process, or
adoption of standards.
> [!example]- Example: Technological restriction
> The product must be developed in C++.
> [!info] **A non-functional requirement does not change the essence of the system functionalities.**
- Non-functional requirements are applicable to the system as a whole
and not just to some of its parts, as, generally, non-functional requirements **cannot be modularized**.
- The non-functional requirements are frequently **emergent properties** of the system at hand. *An emergent property of a system is a property that can be associated with the system as a whole, but not individually to each of its components.*
- *Reliability* is a good example of an emergent property.
> [!info] **Non-functional requirements are crucial to decide the system architecture.**
### 3.1 Classification of non-functional requirements
> [!tip]- Classification: Sommerville (2010)
> - **product requirements**: characterise aspects of the behaviour of the system itself (*reliability, performance, efficiency, portability, usability, testability, and readability*).
> - **organisational requirements**: come from strategies and procedures established in the context of the manufacturing process of the system or the client organisation (process standards and implementation requirements).
> - **external requirements**: have origin in external factors to the system and the development process (interoperability, legal, and ethical requirements).
> [!tip]- Classification: Roberson and Roberson (2006)
> - **appearance**: the aspect and the aesthetics of the system;
> - **usability**: the easiness of utilization of the system and everything that permits a more friendly user experience;
> - **performance**: aspects of speed, real-time, storage capacity, and execution correction;
> - **operational**: characteristics about what the system must do to work correctly in the environment where it is inserted;
> - **maintenance and support**: attributes that allow the system to be repaired or improved and new functionalities to be added;
> - **security**: issues related to access, confidentiality, protection, and integrity of the data;
> - **cultural and political**: factors related to the stakeholders culture and habits;
> - **legal**: laws that apply to the system so that it can operate.
#### 3.1.1 Usability
- **Ease of use** is related to the efficiency of utilizing a system and with the mechanisms that reduce the errors made by the users.
- **Personalization** is associated with the capacity of adapting the system to the tastes of the users.
- **Ease of learning** is concerned with the way users are trained to use the system.
- **Accessibility** indicates how easy it is to use the system, for people who
are somehow physically or mentally disabled.
> [!example]- Example: Usability requirements
> 1. The product shall be easy to use for illiterate persons.
> 2. The product shall be intuitive to use for children with 4 years old.
> 3. The product shall be usable by visually impaired persons.
#### 3.1.2 Maintenance and support
- System maintenance is divided into four types: preventive, corrective, perfective, and adaptive.
- **Modifiability** is related to maintenance and is dependent on how easy it is to locate the components that must be changed.
> [!example]- Example: Maintenance and support requirements
> 1. The source code of the product programs should contain comments.
> 2. The product must be prepared to be translated to any language.
#### 3.1.3 Legal
> [!example]- Example: Legal requirements
> 1. The product shall be aligned with the PMBoK guide.
> 2. The product shall be certified by the Taxing Authority.
> 3. The product shall fulfil with the copyright.
## 4. User and system requirements
A **user requirement** represents:
1. a functionality that the system is expected to provide to its users;
2. a restriction that is applicable to the operation of that system.
- These requirements are related to the problem domain.
- They are normally expressed without great mathematical rigour, using natural language and informal diagrams.
A **system requirement** constitutes a more detailed specification of a requirement, being generally a formal model of the system. These requirements are oriented towards the solution domain, and are documented in a more technical language than the one
adopted for the user requirements.
### 4.1 Problem domain
- The requirements result from necessities that exist in the problem domain to be addressed.
- User requirements should be described with the problem domain terminology.
# Recap
1. Requirements represent the necessities of the users and the constraints that are applied to a system and that must be considered throughout the development.
2. The requirements can be classified, according to a first criterion, as either functional or non-functional.
3. Non-functional requirements are divided in 8 different types: appearance, usability, performance, operational, maintenance and support, security, cultural and political, and legal.
4. The requirements can be designated as either user or system requirements.
5. User requirements are related to the problem domain and are usually expressed in a natural language.
6. A system requirement is oriented towards the solution domain and is a detailed specification of a requirement, generally in the form of a formal model of the system.

10
content/README.md Normal file
View file

@ -0,0 +1,10 @@
Basicamente, isto é uma sopa de letras com alguma lógica por detrás (mas não muita). Abandonem as expectativas. :)
## Conteúdo
### Ano 1 (Mestrado)
1. [[MFES - UC Details| (MFES) Métodos Formais de Engenharia de Software]]
2. (ASCN) Aplicações e Serviços de COmpitação em Nuvem
3. (RAS) Requisitos e Arquiteturas de Software
4. (CP) Computação Paralela
5. (DAA) Dados e Aprendizagem Automática
6. (ESR) Engenharia de Serviços em Rede

View file

@ -4,9 +4,9 @@ tags:
- component
---
Quartz features an explorer that allows you to navigate all files and folders on your site. It supports nested folders and has options for customization.
Quartz features an explorer that allows you to navigate all files and folders on your site. It supports nested folders and is highly customizable.
By default, it will show all folders and files on your page. To display the explorer in a different spot, you can edit the [[layout]].
By default, it shows all folders and files on your page. To display the explorer in a different spot, you can edit the [[layout]].
> [!info]
> The explorer uses local storage by default to save the state of your explorer. This is done to ensure a smooth experience when navigating to different pages.
@ -25,6 +25,14 @@ Component.Explorer({
folderClickBehavior: "collapse", // what happens when you click a folder ("link" to navigate to folder page on click or "collapse" to collapse folder on click)
folderDefaultState: "collapsed", // default state of folders ("collapsed" or "open")
useSavedState: true, // wether to use local storage to save "state" (which folders are opened) of explorer
// Sort order: folders first, then files. Sort folders and files alphabetically
sortFn: (a, b) => {
... // default implementation shown later
},
filterFn: undefined,
mapFn: undefined,
// what order to apply functions in
order: ["filter", "map", "sort"],
})
```
@ -33,9 +41,187 @@ When passing in your own options, you can omit any or all of these fields if you
Want to customize it even more?
- Removing table of contents: remove `Component.Explorer()` from `quartz.layout.ts`
- (optional): After removing the explorer component, you can move the [[table of contents]] component back to the `left` part of the layout
- (optional): After removing the explorer component, you can move the [[table of contents | Table of Contents]] component back to the `left` part of the layout
- Changing `sort`, `filter` and `map` behavior: explained in [[#Advanced customization]]
- Component:
- Wrapper (Outer component, generates file tree, etc): `quartz/components/Explorer.tsx`
- Explorer node (recursive, either a folder or a file): `quartz/components/ExplorerNode.tsx`
- Style: `quartz/components/styles/explorer.scss`
- Script: `quartz/components/scripts/explorer.inline.ts`
## Advanced customization
This component allows you to fully customize all of its behavior. You can pass a custom `sort`, `filter` and `map` function.
All functions you can pass work with the `FileNode` class, which has the following properties:
```ts title="quartz/components/ExplorerNode.tsx" {2-5}
export class FileNode {
children: FileNode[] // children of current node
name: string // name of node (only useful for folders)
file: QuartzPluginData | null // set if node is a file, see `QuartzPluginData` for more detail
depth: number // depth of current node
... // rest of implementation
}
```
Every function you can pass is optional. By default, only a `sort` function will be used:
```ts title="Default sort function"
// Sort order: folders first, then files. Sort folders and files alphabetically
Component.Explorer({
sortFn: (a, b) => {
if ((!a.file && !b.file) || (a.file && b.file)) {
return a.name.localeCompare(b.name)
}
if (a.file && !b.file) {
return 1
} else {
return -1
}
},
})
```
---
You can pass your own functions for `sortFn`, `filterFn` and `mapFn`. All functions will be executed in the order provided by the `order` option (see [[#Customization]]). These functions behave similarly to their `Array.prototype` counterpart, except they modify the entire `FileNode` tree in place instead of returning a new one.
For more information on how to use `sort`, `filter` and `map`, you can check [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort), [Array.prototype.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) and [Array.prototype.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
Type definitions look like this:
```ts
sortFn: (a: FileNode, b: FileNode) => number
filterFn: (node: FileNode) => boolean
mapFn: (node: FileNode) => void
```
> [!tip]
> You can check if a `FileNode` is a folder or a file like this:
>
> ```ts
> if (node.file) {
> // node is a file
> } else {
> // node is a folder
> }
> ```
## Basic examples
These examples show the basic usage of `sort`, `map` and `filter`.
### Use `sort` to put files first
Using this example, the explorer will alphabetically sort everything, but put all **files** above all **folders**.
```ts title="quartz.layout.ts"
Component.Explorer({
sortFn: (a, b) => {
if ((!a.file && !b.file) || (a.file && b.file)) {
return a.name.localeCompare(b.name)
}
if (a.file && !b.file) {
return -1
} else {
return 1
}
},
})
```
### Change display names (`map`)
Using this example, the display names of all `FileNodes` (folders + files) will be converted to full upper case.
```ts title="quartz.layout.ts"
Component.Explorer({
mapFn: (node) => {
node.name = node.name.toUpperCase()
},
})
```
### Remove list of elements (`filter`)
Using this example, you can remove elements from your explorer by providing an array of folders/files using the `omit` set.
```ts title="quartz.layout.ts"
Component.Explorer({
filterFn: (node) => {
// set containing names of everything you want to filter out
const omit = new Set(["authoring content", "tags", "hosting"])
return !omit.has(node.name.toLowerCase())
},
})
```
You can customize this by changing the entries of the `omit` set. Simply add all folder or file names you want to remove.
## Advanced examples
### Add emoji prefix
To add emoji prefixes (📁 for folders, 📄 for files), you could use a map function like this:
```ts title="quartz.layout.ts"
Component.Explorer({
mapFn: (node) => {
// dont change name of root node
if (node.depth > 0) {
// set emoji for file/folder
if (node.file) {
node.name = "📄 " + node.name
} else {
node.name = "📁 " + node.name
}
}
},
}})
```
### Putting it all together
In this example, we're going to customize the explorer by using functions from examples above to [[#Add emoji prefix | add emoji prefixes]], [[#remove-list-of-elements-filter| filter out some folders]] and [[#use-sort-to-put-files-first | sort with files above folders]].
```ts title="quartz.layout.ts"
Component.Explorer({
filterFn: sampleFilterFn,
mapFn: sampleMapFn,
sortFn: sampleSortFn,
order: ["filter", "sort", "map"],
})
```
Notice how we customized the `order` array here. This is done because the default order applies the `sort` function last. While this normally works well, it would cause unintended behavior here, since we changed the first characters of all display names. In our example, `sort` would be applied based off the emoji prefix instead of the first _real_ character.
To fix this, we just changed around the order and apply the `sort` function before changing the display names in the `map` function.
> [!tip]
> When writing more complicated functions, the `layout` file can start to look very cramped.
> You can fix this by defining your functions in another file.
>
> ```ts title="functions.ts"
> import { Options } from "./quartz/components/ExplorerNode"
> export const mapFn: Options["mapFn"] = (node) => {
> // implement your function here
> }
> export const filterFn: Options["filterFn"] = (node) => {
> // implement your function here
> }
> export const sortFn: Options["sortFn"] = (a, b) => {
> // implement your function here
> }
> ```
>
> You can then import them like this:
>
> ```ts title="quartz.layout.ts"
> import { mapFn, filterFn, sortFn } from "./functions.ts"
> Component.Explorer({
> mapFn: mapFn,
> filterFn: filterFn,
> sortFn: sortFn,
> })
> ```

View file

@ -78,6 +78,7 @@
"remark-smartypants": "^2.0.0",
"rimraf": "^5.0.1",
"serve-handler": "^6.1.5",
"shiki": "^0.14.4",
"source-map-support": "^0.5.21",
"to-vfile": "^7.2.4",
"toml": "^3.0.0",

View file

@ -9,7 +9,7 @@ const config: QuartzConfig = {
analytics: {
provider: "plausible",
},
baseUrl: "quartz.jzhao.xyz",
baseUrl: "notes.flup.dev",
ignorePatterns: ["private", "templates", ".obsidian"],
defaultDateType: "created",
theme: {

View file

@ -6,23 +6,62 @@ import script from "./scripts/explorer.inline"
import { ExplorerNode, FileNode, Options } from "./ExplorerNode"
// Options interface defined in `ExplorerNode` to avoid circular dependency
const defaultOptions = (): Options => ({
const defaultOptions = {
title: "Explorer",
folderClickBehavior: "collapse",
folderDefaultState: "collapsed",
useSavedState: true,
})
// Sort order: folders first, then files. Sort folders and files alphabetically
sortFn: (a, b) => {
if ((!a.file && !b.file) || (a.file && b.file)) {
return a.name.localeCompare(b.name)
}
if (a.file && !b.file) {
return 1
} else {
return -1
}
},
order: ["filter", "map", "sort"],
} satisfies Options
export default ((userOpts?: Partial<Options>) => {
function Explorer({ allFiles, displayClass, fileData }: QuartzComponentProps) {
// Parse config
const opts: Options = { ...defaultOptions(), ...userOpts }
const opts: Options = { ...defaultOptions, ...userOpts }
// Construct tree from allFiles
const fileTree = new FileNode("")
allFiles.forEach((file) => fileTree.add(file, 1))
// Sort tree (folders first, then files (alphabetic))
fileTree.sort()
/**
* Keys of this object must match corresponding function name of `FileNode`,
* while values must be the argument that will be passed to the function.
*
* e.g. entry for FileNode.sort: `sort: opts.sortFn` (value is sort function from options)
*/
const functions = {
map: opts.mapFn,
sort: opts.sortFn,
filter: opts.filterFn,
}
// Execute all functions (sort, filter, map) that were provided (if none were provided, only default "sort" is applied)
if (opts.order) {
// Order is important, use loop with index instead of order.map()
for (let i = 0; i < opts.order.length; i++) {
const functionName = opts.order[i]
if (functions[functionName]) {
// for every entry in order, call matching function in FileNode and pass matching argument
// e.g. i = 0; functionName = "filter"
// converted to: (if opts.filterFn) => fileTree.filter(opts.filterFn)
// @ts-ignore
// typescript cant statically check these dynamic references, so manually make sure reference is valid and ignore warning
fileTree[functionName].call(fileTree, functions[functionName])
}
}
}
// Get all folders of tree. Initialize with collapsed state
const folders = fileTree.getFolderPaths(opts.folderDefaultState === "collapsed")
@ -57,8 +96,9 @@ export default ((userOpts?: Partial<Options>) => {
</svg>
</button>
<div id="explorer-content">
<ul class="overflow">
<ul class="overflow" id="explorer-ul">
<ExplorerNode node={fileTree} opts={opts} fileData={fileData} />
<div id="explorer-end" />
</ul>
</div>
</div>

View file

@ -1,12 +1,18 @@
// @ts-ignore
import { QuartzPluginData } from "vfile"
import { QuartzPluginData } from "../plugins/vfile"
import { resolveRelative } from "../util/path"
type OrderEntries = "sort" | "filter" | "map"
export interface Options {
title: string
folderDefaultState: "collapsed" | "open"
folderClickBehavior: "collapse" | "link"
useSavedState: boolean
sortFn: (a: FileNode, b: FileNode) => number
filterFn?: (node: FileNode) => boolean
mapFn?: (node: FileNode) => void
order?: OrderEntries[]
}
type DataWrapper = {
@ -29,7 +35,7 @@ export class FileNode {
constructor(name: string, file?: QuartzPluginData, depth?: number) {
this.children = []
this.name = name
this.file = file ?? null
this.file = file ? structuredClone(file) : null
this.depth = depth ?? 0
}
@ -65,6 +71,25 @@ export class FileNode {
this.children.forEach((e) => e.print(depth + 1))
}
/**
* Filter FileNode tree. Behaves similar to `Array.prototype.filter()`, but modifies tree in place
* @param filterFn function to filter tree with
*/
filter(filterFn: (node: FileNode) => boolean) {
this.children = this.children.filter(filterFn)
this.children.forEach((child) => child.filter(filterFn))
}
/**
* Filter FileNode tree. Behaves similar to `Array.prototype.map()`, but modifies tree in place
* @param mapFn function to use for mapping over tree
*/
map(mapFn: (node: FileNode) => void) {
mapFn(this)
this.children.forEach((child) => child.map(mapFn))
}
/**
* Get folder representation with state of tree.
* Intended to only be called on root node before changes to the tree are made
@ -90,19 +115,13 @@ export class FileNode {
}
// Sort order: folders first, then files. Sort folders and files alphabetically
sort() {
this.children = this.children.sort((a, b) => {
if ((!a.file && !b.file) || (a.file && b.file)) {
return a.name.localeCompare(b.name)
}
if (a.file && !b.file) {
return 1
} else {
return -1
}
})
this.children.forEach((e) => e.sort())
/**
* Sorts tree according to sort/compare function
* @param sortFn compare function used for `.sort()`, also used recursively for children
*/
sort(sortFn: (a: FileNode, b: FileNode) => number) {
this.children = this.children.sort(sortFn)
this.children.forEach((e) => e.sort(sortFn))
}
}
@ -131,7 +150,7 @@ export function ExplorerNode({ node, opts, fullPath, fileData }: ExplorerNodePro
// Single file node
<li key={node.file.slug}>
<a href={resolveRelative(fileData.slug!, node.file.slug!)} data-for={node.file.slug}>
{node.file.frontmatter?.title}
{node.name}
</a>
</li>
) : (

View file

@ -20,4 +20,13 @@ document.addEventListener("nav", () => {
if (currentTheme === "dark") {
toggleSwitch.checked = true
}
// Listen for changes in prefers-color-scheme
const colorSchemeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)")
colorSchemeMediaQuery.addEventListener("change", (e) => {
const newTheme = e.matches ? "dark" : "light"
document.documentElement.setAttribute("saved-theme", newTheme)
localStorage.setItem("theme", newTheme)
toggleSwitch.checked = e.matches
})
})

View file

@ -3,6 +3,18 @@ import { FolderState } from "../ExplorerNode"
// Current state of folders
let explorerState: FolderState[]
const observer = new IntersectionObserver((entries) => {
// If last element is observed, remove gradient of "overflow" class so element is visible
const explorer = document.getElementById("explorer-ul")
for (const entry of entries) {
if (entry.isIntersecting) {
explorer?.classList.add("no-background")
} else {
explorer?.classList.remove("no-background")
}
}
})
function toggleExplorer(this: HTMLElement) {
// Toggle collapsed state of entire explorer
this.classList.toggle("collapsed")
@ -101,8 +113,10 @@ function setupExplorer() {
) as HTMLElement
// Get corresponding content <ul> tag and set state
const folderUL = folderLi.parentElement?.nextElementSibling as HTMLElement
setFolderState(folderUL, folderUl.collapsed)
const folderUL = folderLi.parentElement?.nextElementSibling
if (folderUL) {
setFolderState(folderUL as HTMLElement, folderUl.collapsed)
}
})
} else {
// If tree is not in localStorage or config is disabled, use tree passed from Explorer as dataset
@ -113,6 +127,13 @@ function setupExplorer() {
window.addEventListener("resize", setupExplorer)
document.addEventListener("nav", () => {
setupExplorer()
const explorerContent = document.getElementById("explorer-ul")
// select pseudo element at end of list
const lastItem = document.getElementById("explorer-end")
observer.disconnect()
observer.observe(lastItem as Element)
})
/**

View file

@ -131,3 +131,12 @@ div:has(> .folder-outer:not(.open)) > .folder-container > svg {
.folder-icon:hover {
color: var(--tertiary);
}
.no-background::after {
background: none !important;
}
#explorer-end {
// needs height so IntersectionObserver gets triggered
height: 1px;
}

View file

@ -52,7 +52,7 @@ export function slugifyFilePath(fp: FilePath, excludeExt?: boolean): FullSlug {
let slug = withoutFileExt
.split("/")
.map((segment) => segment.replace(/\s/g, "-").replace(/%/g, "-percent")) // slugify all segments
.map((segment) => segment.replace(/\s/g, "-").replace(/%/g, "-percent").replace(/\?/g, "-q")) // slugify all segments
.join("/") // always use / as sep
.replace(/\/$/, "") // remove trailing slash