diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..ecac4f9 Binary files /dev/null and b/bun.lockb differ diff --git a/content/.gitkeep b/content/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/content/Notes/CP/T - Aula 2 - 19 Setembro.md b/content/Notes/CP/T - Aula 2 - 19 Setembro.md new file mode 100644 index 0000000..307e4e0 --- /dev/null +++ b/content/Notes/CP/T - Aula 2 - 19 Setembro.md @@ -0,0 +1,7 @@ +## Conteúdo +- cache em arquiteturas multicore +- code profiling +- instruction-level paralelism +- Data dependency +- branch prediction +- \ No newline at end of file diff --git a/content/Notes/MFES/MFES - UC Details.md b/content/Notes/MFES/MFES - UC Details.md new file mode 100644 index 0000000..2f9a300 --- /dev/null +++ b/content/Notes/MFES/MFES - UC Details.md @@ -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. \ No newline at end of file diff --git a/content/Notes/MFES/T - Aula 2 - 18 Setembro 2023.md b/content/Notes/MFES/T - Aula 2 - 18 Setembro 2023.md new file mode 100644 index 0000000..96bc7f1 --- /dev/null +++ b/content/Notes/MFES/T - Aula 2 - 18 Setembro 2023.md @@ -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 + + +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. diff --git a/content/Notes/MFES/TP - Aula 1 - 18 Setembro.md b/content/Notes/MFES/TP - Aula 1 - 18 Setembro.md new file mode 100644 index 0000000..05cdc0e --- /dev/null +++ b/content/Notes/MFES/TP - Aula 1 - 18 Setembro.md @@ -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. diff --git a/content/Notes/RAS/T - Aula 2 - 19 Setembro 2023.md b/content/Notes/RAS/T - Aula 2 - 19 Setembro 2023.md new file mode 100644 index 0000000..27517ac --- /dev/null +++ b/content/Notes/RAS/T - Aula 2 - 19 Setembro 2023.md @@ -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. diff --git a/content/README.md b/content/README.md new file mode 100644 index 0000000..af937a3 --- /dev/null +++ b/content/README.md @@ -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 diff --git a/docs/features/explorer.md b/docs/features/explorer.md index 17647de..6f941b8 100644 --- a/docs/features/explorer.md +++ b/docs/features/explorer.md @@ -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, +> }) +> ``` diff --git a/package.json b/package.json index 0a2085c..2c4edfb 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/quartz.config.ts b/quartz.config.ts index f677a18..5d8a1f9 100644 --- a/quartz.config.ts +++ b/quartz.config.ts @@ -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: { diff --git a/quartz/components/Explorer.tsx b/quartz/components/Explorer.tsx index ce69491..8597075 100644 --- a/quartz/components/Explorer.tsx +++ b/quartz/components/Explorer.tsx @@ -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) => { 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) => {
-
    +
      +
diff --git a/quartz/components/ExplorerNode.tsx b/quartz/components/ExplorerNode.tsx index 6718ec9..fd0c082 100644 --- a/quartz/components/ExplorerNode.tsx +++ b/quartz/components/ExplorerNode.tsx @@ -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
  • - {node.file.frontmatter?.title} + {node.name}
  • ) : ( diff --git a/quartz/components/scripts/darkmode.inline.ts b/quartz/components/scripts/darkmode.inline.ts index e16f4f8..c42a367 100644 --- a/quartz/components/scripts/darkmode.inline.ts +++ b/quartz/components/scripts/darkmode.inline.ts @@ -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 + }) }) diff --git a/quartz/components/scripts/explorer.inline.ts b/quartz/components/scripts/explorer.inline.ts index 8073979..2b7df7d 100644 --- a/quartz/components/scripts/explorer.inline.ts +++ b/quartz/components/scripts/explorer.inline.ts @@ -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