mirror of
https://github.com/ttusk/leif.git
synced 2026-07-22 08:32:51 +00:00
fix(release): address automated review findings
This commit is contained in:
parent
38e4641250
commit
f9bdfc042d
12 changed files with 73 additions and 38 deletions
13
README.md
13
README.md
|
|
@ -8,6 +8,19 @@ Leif é um plugin para [Obsidian](https://obsidian.md/) que organiza estudos par
|
|||
|
||||
Ele reúne concursos, matérias, tópicos do edital, materiais e sessões de estudo para mostrar o que estudar agora e acompanhar o progresso sem sair do Obsidian.
|
||||
|
||||
## Instalação
|
||||
|
||||
### Pelo Obsidian
|
||||
|
||||
Quando o Leif estiver disponível no diretório oficial, abra **Configurações → Plugins da comunidade → Explorar**, procure por **Leif**, selecione **Instalar** e depois **Ativar**.
|
||||
|
||||
### Instalação manual
|
||||
|
||||
1. Baixe `main.js`, `manifest.json` e `styles.css` da mesma versão na página de releases.
|
||||
2. Crie a pasta `<seu-vault>/.obsidian/plugins/leif` e coloque os três arquivos nela.
|
||||
3. Reinicie o Obsidian ou recarregue os plugins em **Configurações → Plugins da comunidade**.
|
||||
4. Ative o **Leif**.
|
||||
|
||||
## Como usar
|
||||
|
||||
Ative o Leif em **Plugins da comunidade** e abra o painel pelo ícone da faixa lateral ou pelo comando **Abrir painel**. Crie um concurso, organize suas matérias e recursos, registre cada sessão e consulte **Hoje** para seguir o ciclo.
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const ptBR = {
|
|||
|
||||
export type TranslationKey = keyof typeof ptBR;
|
||||
|
||||
const bundle: Record<string, string> = ptBR as unknown as Record<string, string>;
|
||||
const bundle: Record<string, string> = ptBR;
|
||||
|
||||
export function t(key: TranslationKey): string {
|
||||
return bundle[key] ?? key;
|
||||
|
|
|
|||
|
|
@ -133,8 +133,8 @@ export class LeifView extends ItemView {
|
|||
label.textContent = tab.label;
|
||||
button.append(icon, label);
|
||||
button.dataset.tab = tab.id;
|
||||
button.addEventListener("click", async () => {
|
||||
await this.selectTab(tab.id);
|
||||
button.addEventListener("click", () => {
|
||||
void this.selectTab(tab.id);
|
||||
});
|
||||
button.setAttribute("role", "tab");
|
||||
button.id = `leif-tab-${tab.id}`;
|
||||
|
|
|
|||
|
|
@ -209,21 +209,21 @@ export class SessionsTab {
|
|||
fromInput.dataset.sessionFilter = "from";
|
||||
toInput.dataset.sessionFilter = "to";
|
||||
|
||||
subjectSelect.addEventListener("change", async () => {
|
||||
subjectSelect.addEventListener("change", () => {
|
||||
this.historySubjectFilter = subjectSelect.value;
|
||||
await this.onUpdate();
|
||||
void this.onUpdate();
|
||||
});
|
||||
typeSelect.addEventListener("change", async () => {
|
||||
typeSelect.addEventListener("change", () => {
|
||||
this.historyTypeFilter = typeSelect.value;
|
||||
await this.onUpdate();
|
||||
void this.onUpdate();
|
||||
});
|
||||
fromInput.addEventListener("change", async () => {
|
||||
fromInput.addEventListener("change", () => {
|
||||
this.historyFromFilter = fromInput.value;
|
||||
await this.onUpdate();
|
||||
void this.onUpdate();
|
||||
});
|
||||
toInput.addEventListener("change", async () => {
|
||||
toInput.addEventListener("change", () => {
|
||||
this.historyToFilter = toInput.value;
|
||||
await this.onUpdate();
|
||||
void this.onUpdate();
|
||||
});
|
||||
|
||||
const filters = DomHelpers.createElement("div", "leif-session-filters");
|
||||
|
|
|
|||
|
|
@ -10,9 +10,11 @@ export const LEIF_ICON = "compass";
|
|||
export function registerLeifView(plugin: Obsidian.Plugin, dataStore: PluginDataStore): void {
|
||||
plugin.registerView(LEIF_VIEW_TYPE, (leaf) => new LeifView(leaf, dataStore));
|
||||
|
||||
plugin.addRibbonIcon(LEIF_ICON, "Abrir Leif", () => openLeifView(plugin));
|
||||
plugin.addRibbonIcon(LEIF_ICON, "Abrir Leif", () => {
|
||||
void openLeifView(plugin);
|
||||
});
|
||||
plugin.addCommand({
|
||||
id: "leif-open-view",
|
||||
id: "open-view",
|
||||
name: t("command.openView"),
|
||||
callback: async () => {
|
||||
await openLeifView(plugin);
|
||||
|
|
@ -29,5 +31,5 @@ export async function openLeifView(plugin: Obsidian.Plugin): Promise<void> {
|
|||
active: true
|
||||
});
|
||||
|
||||
await plugin.app.workspace.revealLeaf(leaf);
|
||||
plugin.app.workspace.setActiveLeaf(leaf, { focus: true });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,11 +15,7 @@ export class DomHelpers {
|
|||
tagName: K,
|
||||
className?: string
|
||||
): HTMLElementTagNameMap[K] {
|
||||
const element = document.createElement(tagName);
|
||||
if (className) {
|
||||
element.className = className;
|
||||
}
|
||||
return element;
|
||||
return createEl(tagName, className ? { cls: className } : undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -98,7 +94,7 @@ export class DomHelpers {
|
|||
* Creates a strong (bold) text element.
|
||||
*/
|
||||
static createStrong(text: string): HTMLElement {
|
||||
const strong = document.createElement("strong");
|
||||
const strong = this.createElement("strong");
|
||||
strong.textContent = text;
|
||||
return strong;
|
||||
}
|
||||
|
|
@ -134,7 +130,7 @@ export class DomHelpers {
|
|||
* Creates an input element.
|
||||
*/
|
||||
static createInput(type: string, placeholder: string, value = ""): HTMLInputElement {
|
||||
const input = document.createElement("input");
|
||||
const input = this.createElement("input");
|
||||
input.type = type;
|
||||
input.placeholder = placeholder;
|
||||
input.value = value;
|
||||
|
|
@ -147,10 +143,10 @@ export class DomHelpers {
|
|||
* @param selectedValue - Optional value to pre-select
|
||||
*/
|
||||
static createSelect(options: Array<[string, string]>, selectedValue?: string): HTMLSelectElement {
|
||||
const select = document.createElement("select");
|
||||
const select = this.createElement("select");
|
||||
|
||||
options.forEach(([value, label]) => {
|
||||
const option = document.createElement("option");
|
||||
const option = this.createElement("option");
|
||||
option.value = value;
|
||||
option.textContent = label;
|
||||
if (value === selectedValue) {
|
||||
|
|
@ -166,7 +162,7 @@ export class DomHelpers {
|
|||
* Creates a textarea element.
|
||||
*/
|
||||
static createTextarea(placeholder: string, value = ""): HTMLTextAreaElement {
|
||||
const textarea = document.createElement("textarea");
|
||||
const textarea = this.createElement("textarea");
|
||||
textarea.placeholder = placeholder;
|
||||
textarea.value = value;
|
||||
return textarea;
|
||||
|
|
@ -302,7 +298,7 @@ export class DomHelpers {
|
|||
type?: "button" | "submit" | "reset";
|
||||
} = {}
|
||||
): HTMLButtonElement {
|
||||
const button = document.createElement("button");
|
||||
const button = this.createElement("button");
|
||||
button.type = options.type || "button";
|
||||
button.className = options.className || "";
|
||||
|
||||
|
|
@ -319,7 +315,9 @@ export class DomHelpers {
|
|||
}
|
||||
|
||||
if (options.onClick) {
|
||||
button.addEventListener("click", options.onClick);
|
||||
button.addEventListener("click", (event) => {
|
||||
void options.onClick?.(event);
|
||||
});
|
||||
}
|
||||
|
||||
return button;
|
||||
|
|
@ -341,7 +339,7 @@ export class DomHelpers {
|
|||
onClick?: (event: MouseEvent) => void | Promise<void>;
|
||||
} = {}
|
||||
): HTMLButtonElement {
|
||||
const button = document.createElement("button");
|
||||
const button = this.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = options.className || "clickable-icon";
|
||||
button.setAttribute("aria-label", title);
|
||||
|
|
@ -354,7 +352,9 @@ export class DomHelpers {
|
|||
}
|
||||
|
||||
if (options.onClick) {
|
||||
button.addEventListener("click", options.onClick);
|
||||
button.addEventListener("click", (event) => {
|
||||
void options.onClick?.(event);
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof setTooltip === "function") {
|
||||
|
|
@ -370,11 +370,11 @@ export class DomHelpers {
|
|||
* Creates a form element.
|
||||
*/
|
||||
static createForm(onSubmit?: (event: Event) => void | Promise<void>): HTMLFormElement {
|
||||
const form = document.createElement("form");
|
||||
const form = this.createElement("form");
|
||||
if (onSubmit) {
|
||||
form.addEventListener("submit", (event) => {
|
||||
event.preventDefault();
|
||||
onSubmit(event);
|
||||
void onSubmit(event);
|
||||
});
|
||||
}
|
||||
return form;
|
||||
|
|
@ -393,7 +393,7 @@ export class DomHelpers {
|
|||
): void {
|
||||
select.innerHTML = "";
|
||||
options.forEach(([value, label]) => {
|
||||
const option = document.createElement("option");
|
||||
const option = this.createElement("option");
|
||||
option.value = value;
|
||||
option.textContent = label;
|
||||
if (selectedValue !== undefined && value === selectedValue) {
|
||||
|
|
|
|||
|
|
@ -1123,7 +1123,6 @@ form.leif-card,
|
|||
gap: 4px;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 3px;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.leif-view.is-narrow .leif-tab-button {
|
||||
|
|
@ -1277,7 +1276,6 @@ form.leif-card,
|
|||
gap: 4px;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 3px;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.leif-tab-button {
|
||||
|
|
|
|||
|
|
@ -16,9 +16,19 @@ describe("community release readiness", () => {
|
|||
expect(versions[version]).toBe(manifest.minAppVersion);
|
||||
expect(manifest.description).not.toBe("A bússola do seu estudo.");
|
||||
expect(manifest.authorUrl).toBe("https://github.com/ttusk");
|
||||
expect(read("README.md")).toContain("## Instalação");
|
||||
expect(read("README.md")).toContain("## Como usar");
|
||||
});
|
||||
|
||||
it("uses APIs and DOM patterns supported by the declared Obsidian version", () => {
|
||||
const viewRegistration = read("src/ui/view/registerLeifView.ts");
|
||||
const domHelpers = read("src/ui/view/shared/DomHelpers.ts");
|
||||
|
||||
expect(viewRegistration).not.toContain("workspace.revealLeaf");
|
||||
expect(domHelpers).not.toContain("document.createElement");
|
||||
expect(domHelpers).not.toMatch(/addEventListener\([^,]+,\s*options\.onClick\)/);
|
||||
});
|
||||
|
||||
it("keeps demo and compiled artifacts out of the source repository", () => {
|
||||
expect(existsSync(resolve(root, "sample-vault/.obsidian/plugins/leif/main.js"))).toBe(false);
|
||||
expect(existsSync(resolve(root, "docs/improvements-audit.md"))).toBe(false);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,17 @@
|
|||
const notices: string[] = [];
|
||||
const registeredIcons = new Map<string, string>();
|
||||
|
||||
globalThis.createEl = ((tagName, options) => {
|
||||
const element = document.createElement(tagName);
|
||||
const className = typeof options === "string" ? options : options?.cls;
|
||||
|
||||
if (className) {
|
||||
element.className = Array.isArray(className) ? className.join(" ") : className;
|
||||
}
|
||||
|
||||
return element;
|
||||
}) as typeof createEl;
|
||||
|
||||
type ViewCreator = (leaf: WorkspaceLeaf) => ItemView;
|
||||
|
||||
export class Notice {
|
||||
|
|
@ -72,7 +83,7 @@ export class Workspace {
|
|||
return this.getLeaf();
|
||||
}
|
||||
|
||||
async revealLeaf(_leaf: WorkspaceLeaf): Promise<void> {
|
||||
setActiveLeaf(_leaf: WorkspaceLeaf, _params?: { focus?: boolean }): void {
|
||||
// Test stub.
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ describe("LeifPlugin", () => {
|
|||
|
||||
const registeredPlugin = plugin as unknown as Plugin;
|
||||
expect(registeredPlugin.settingTabs).toHaveLength(0);
|
||||
expect(registeredPlugin.commands.map((command) => command.id)).toEqual(["leif-open-view"]);
|
||||
expect(registeredPlugin.commands.map((command) => command.id)).toEqual(["open-view"]);
|
||||
expect(registeredPlugin.commands[0]?.name).toBe("Abrir painel");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ async function openLeifView(dataStore: PluginDataStore): Promise<{
|
|||
const plugin = new Plugin(app);
|
||||
registerLeifView(plugin as never, dataStore);
|
||||
|
||||
const openCommand = plugin.commands.find((command) => command.id === "leif-open-view");
|
||||
const openCommand = plugin.commands.find((command) => command.id === "open-view");
|
||||
|
||||
if (!openCommand) {
|
||||
throw new Error("Open view command was not registered.");
|
||||
|
|
@ -155,8 +155,8 @@ describe("LeifView", () => {
|
|||
|
||||
expect(plugin.ribbonIcons).toHaveLength(1);
|
||||
expect(plugin.ribbonIcons[0]?.title).toBe("Abrir Leif");
|
||||
expect(plugin.commands.map((command) => command.id)).toContain("leif-open-view");
|
||||
expect(plugin.commands.find((command) => command.id === "leif-open-view")?.name).toBe(
|
||||
expect(plugin.commands.map((command) => command.id)).toContain("open-view");
|
||||
expect(plugin.commands.find((command) => command.id === "open-view")?.name).toBe(
|
||||
"Abrir painel"
|
||||
);
|
||||
expect(leaf.containerEl.textContent).toContain("Hoje");
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ describe("Leif visual system", () => {
|
|||
expect(styles).toMatch(
|
||||
/\.leif-view\.is-narrow\s+\.leif-tab-bar\s*{[^}]*flex-direction:\s*row;[^}]*overflow-x:\s*auto;/s
|
||||
);
|
||||
expect(styles).not.toContain("scrollbar-width");
|
||||
});
|
||||
|
||||
it("gives semantic tabs a native, visible active state", () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue