mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
feat(onboarding): refactor onboarding flow with new placement configuration
- Add new PlacementStep for configuring Fluent layout placement - Consolidate onboarding steps into unified step-based architecture - Remove obsolete components (ConfigPreview, FluentPlacement, OnboardingComplete, UserLevelSelector) - Enhance step navigation with improved back/skip logic - Add internationalization support to changelog view - Update changelog branding (icon, URL to taskgenius.md) - Add experimental TaskViewV2 with enhanced Fluent interface - Improve onboarding UI with better visual hierarchy and styling - Update translations across en, zh-cn, zh-tw locales
This commit is contained in:
parent
2323767b32
commit
1b9768f5af
32 changed files with 4458 additions and 1095 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import { ItemView, MarkdownRenderer, WorkspaceLeaf } from "obsidian";
|
||||
import type TaskProgressBarPlugin from "@/index";
|
||||
import { getCachedChangelog } from "@/utils/changelog-cache";
|
||||
import { t } from "@/translations/helper";
|
||||
|
||||
export const CHANGELOG_VIEW_TYPE = "task-genius-changelog";
|
||||
|
||||
|
|
@ -26,11 +27,11 @@ export class ChangelogView extends ItemView {
|
|||
}
|
||||
|
||||
getDisplayText(): string {
|
||||
return "Task Genius Changelog";
|
||||
return t("Changelog");
|
||||
}
|
||||
|
||||
getIcon(): string {
|
||||
return "list";
|
||||
return "task-genius";
|
||||
}
|
||||
|
||||
async onOpen() {
|
||||
|
|
@ -97,7 +98,7 @@ export class ChangelogView extends ItemView {
|
|||
});
|
||||
|
||||
headerEl.createEl("h2", {
|
||||
text: "Task Genius Changelog",
|
||||
text: t("Task Genius Changelog"),
|
||||
});
|
||||
|
||||
if (this.content?.version) {
|
||||
|
|
@ -113,7 +114,7 @@ export class ChangelogView extends ItemView {
|
|||
metaEl.createSpan({ text: " • " });
|
||||
metaEl.createEl("a", {
|
||||
text: "View full changelog",
|
||||
href: this.content.sourceUrl,
|
||||
href: "https://taskgenius.md/changelog",
|
||||
attr: {
|
||||
target: "_blank",
|
||||
rel: "noopener noreferrer",
|
||||
|
|
|
|||
|
|
@ -1,320 +0,0 @@
|
|||
import {
|
||||
OnboardingConfig,
|
||||
OnboardingConfigManager,
|
||||
} from "@/managers/onboarding-manager";
|
||||
import { t } from "@/translations/helper";
|
||||
import { setIcon } from "obsidian";
|
||||
|
||||
export class ConfigPreview {
|
||||
private configManager: OnboardingConfigManager;
|
||||
|
||||
constructor(configManager: OnboardingConfigManager) {
|
||||
this.configManager = configManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render configuration preview
|
||||
*/
|
||||
render(containerEl: HTMLElement, config: OnboardingConfig) {
|
||||
containerEl.empty();
|
||||
|
||||
// Configuration overview
|
||||
const overviewSection = containerEl.createDiv("config-overview");
|
||||
|
||||
const selectedModeEl = overviewSection.createDiv("selected-mode");
|
||||
selectedModeEl.createEl("h3", { text: t("Selected Mode") });
|
||||
|
||||
const modeCard = selectedModeEl.createDiv("mode-card");
|
||||
const modeIcon = modeCard.createDiv("mode-icon");
|
||||
setIcon(modeIcon, this.getConfigIcon(config.mode));
|
||||
|
||||
const modeContent = modeCard.createDiv("mode-content");
|
||||
modeContent.createEl("h4", { text: config.name });
|
||||
modeContent.createEl("p", { text: config.description });
|
||||
|
||||
// Features that will be enabled
|
||||
const featuresSection = containerEl.createDiv("config-features");
|
||||
featuresSection.createEl("h3", {
|
||||
text: t("Features that will be enabled"),
|
||||
});
|
||||
|
||||
const featuresList = featuresSection.createEl("ul", {
|
||||
cls: "enabled-features-list",
|
||||
});
|
||||
config.features.forEach((feature) => {
|
||||
const featureItem = featuresList.createEl("li");
|
||||
const checkIcon = featureItem.createSpan("feature-check");
|
||||
setIcon(checkIcon, "check");
|
||||
featureItem.createSpan("feature-text").setText(feature);
|
||||
});
|
||||
|
||||
// Views that will be available
|
||||
this.renderViewsPreview(containerEl, config);
|
||||
|
||||
// Settings summary
|
||||
this.renderSettingsSummary(containerEl, config);
|
||||
|
||||
// Note about customization
|
||||
const customizationNote = containerEl.createDiv("customization-note");
|
||||
customizationNote.createEl("p", {
|
||||
text: t(
|
||||
"Don't worry! You can customize any of these settings later in the plugin settings."
|
||||
),
|
||||
cls: "note-text",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Render views preview
|
||||
*/
|
||||
private renderViewsPreview(
|
||||
containerEl: HTMLElement,
|
||||
config: OnboardingConfig
|
||||
) {
|
||||
if (!config.settings.viewConfiguration) return;
|
||||
|
||||
const viewsSection = containerEl.createDiv("config-views");
|
||||
viewsSection.createEl("h3", { text: t("Available views") });
|
||||
|
||||
const viewsGrid = viewsSection.createDiv("views-grid");
|
||||
|
||||
config.settings.viewConfiguration.forEach((view) => {
|
||||
const viewItem = viewsGrid.createDiv("view-item");
|
||||
|
||||
const viewIcon = viewItem.createDiv("view-icon");
|
||||
// Use native Obsidian icon from view.icon
|
||||
setIcon(viewIcon, view.icon || "list");
|
||||
|
||||
const viewName = viewItem.createDiv("view-name");
|
||||
viewName.setText(view.name);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Render settings summary
|
||||
*/
|
||||
private renderSettingsSummary(
|
||||
containerEl: HTMLElement,
|
||||
config: OnboardingConfig
|
||||
) {
|
||||
const settingsSection = containerEl.createDiv("config-settings");
|
||||
settingsSection.createEl("h3", { text: t("Key settings") });
|
||||
|
||||
const settingsList = settingsSection.createEl("ul", {
|
||||
cls: "settings-summary-list",
|
||||
});
|
||||
|
||||
// Progress bars
|
||||
if (config.settings.progressBarDisplayMode) {
|
||||
const item = settingsList.createEl("li");
|
||||
item.createSpan("setting-label").setText(t("Progress bars") + ":");
|
||||
item.createSpan("setting-value").setText(
|
||||
config.settings.progressBarDisplayMode === "both"
|
||||
? t("Enabled (both graphical and text)")
|
||||
: config.settings.progressBarDisplayMode
|
||||
);
|
||||
}
|
||||
|
||||
// Task status switching
|
||||
if (config.settings.enableTaskStatusSwitcher !== undefined) {
|
||||
const item = settingsList.createEl("li");
|
||||
item.createSpan("setting-label").setText(
|
||||
t("Task status switching") + ":"
|
||||
);
|
||||
item.createSpan("setting-value").setText(
|
||||
config.settings.enableTaskStatusSwitcher
|
||||
? t("Enabled")
|
||||
: t("Disabled")
|
||||
);
|
||||
}
|
||||
|
||||
// Quick capture
|
||||
if (config.settings.quickCapture?.enableQuickCapture !== undefined) {
|
||||
const item = settingsList.createEl("li");
|
||||
item.createSpan("setting-label").setText(t("Quick capture") + ":");
|
||||
item.createSpan("setting-value").setText(
|
||||
config.settings.quickCapture.enableQuickCapture
|
||||
? t("Enabled")
|
||||
: t("Disabled")
|
||||
);
|
||||
}
|
||||
|
||||
// Workflow
|
||||
if (config.settings.workflow?.enableWorkflow !== undefined) {
|
||||
const item = settingsList.createEl("li");
|
||||
item.createSpan("setting-label").setText(
|
||||
t("Workflow management") + ":"
|
||||
);
|
||||
item.createSpan("setting-value").setText(
|
||||
config.settings.workflow.enableWorkflow
|
||||
? t("Enabled")
|
||||
: t("Disabled")
|
||||
);
|
||||
}
|
||||
|
||||
// Rewards
|
||||
if (config.settings.rewards?.enableRewards !== undefined) {
|
||||
const item = settingsList.createEl("li");
|
||||
item.createSpan("setting-label").setText(t("Reward system") + ":");
|
||||
item.createSpan("setting-value").setText(
|
||||
config.settings.rewards.enableRewards
|
||||
? t("Enabled")
|
||||
: t("Disabled")
|
||||
);
|
||||
}
|
||||
|
||||
// Habits
|
||||
if (config.settings.habit?.enableHabits !== undefined) {
|
||||
const item = settingsList.createEl("li");
|
||||
item.createSpan("setting-label").setText(t("Habit tracking") + ":");
|
||||
item.createSpan("setting-value").setText(
|
||||
config.settings.habit.enableHabits
|
||||
? t("Enabled")
|
||||
: t("Disabled")
|
||||
);
|
||||
}
|
||||
|
||||
// Performance features
|
||||
if (
|
||||
config.settings.fileParsingConfig?.enableWorkerProcessing !==
|
||||
undefined
|
||||
) {
|
||||
const item = settingsList.createEl("li");
|
||||
item.createSpan("setting-label").setText(
|
||||
t("Performance optimization") + ":"
|
||||
);
|
||||
item.createSpan("setting-value").setText(
|
||||
config.settings.fileParsingConfig.enableWorkerProcessing
|
||||
? t("Enabled")
|
||||
: t("Disabled")
|
||||
);
|
||||
}
|
||||
|
||||
// Show configuration change preview
|
||||
this.renderConfigurationChanges(containerEl, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render configuration changes preview
|
||||
*/
|
||||
private renderConfigurationChanges(
|
||||
containerEl: HTMLElement,
|
||||
config: OnboardingConfig
|
||||
) {
|
||||
try {
|
||||
const preview = this.configManager.getConfigurationPreview(
|
||||
config.mode
|
||||
);
|
||||
|
||||
// Show change summary section
|
||||
const changesSection = containerEl.createDiv(
|
||||
"config-changes-summary"
|
||||
);
|
||||
changesSection.createEl("h3", { text: t("Configuration Changes") });
|
||||
|
||||
// User custom views preserved
|
||||
if (preview.userCustomViewsPreserved.length > 0) {
|
||||
const preservedSection =
|
||||
changesSection.createDiv("preserved-views");
|
||||
const preservedHeader =
|
||||
preservedSection.createDiv("preserved-header");
|
||||
const preservedIcon =
|
||||
preservedHeader.createSpan("preserved-icon");
|
||||
setIcon(preservedIcon, "shield-check");
|
||||
preservedHeader
|
||||
.createSpan("preserved-text")
|
||||
.setText(
|
||||
t("Your custom views will be preserved") +
|
||||
` (${preview.userCustomViewsPreserved.length})`
|
||||
);
|
||||
|
||||
const preservedList = preservedSection.createEl("ul", {
|
||||
cls: "preserved-views-list",
|
||||
});
|
||||
preview.userCustomViewsPreserved.forEach((view) => {
|
||||
const item = preservedList.createEl("li");
|
||||
const viewIcon = item.createSpan();
|
||||
setIcon(viewIcon, view.icon || "list");
|
||||
item.createSpan().setText(" " + view.name);
|
||||
});
|
||||
}
|
||||
|
||||
// Views to be added
|
||||
if (preview.viewsToAdd.length > 0) {
|
||||
const addedSection = changesSection.createDiv("added-views");
|
||||
const addedIcon = addedSection.createSpan("change-icon");
|
||||
setIcon(addedIcon, "plus-circle");
|
||||
addedSection
|
||||
.createSpan("change-text")
|
||||
.setText(
|
||||
t("New views to be added") +
|
||||
` (${preview.viewsToAdd.length})`
|
||||
);
|
||||
}
|
||||
|
||||
// Views to be updated
|
||||
if (preview.viewsToUpdate.length > 0) {
|
||||
const updatedSection =
|
||||
changesSection.createDiv("updated-views");
|
||||
const updatedIcon = updatedSection.createSpan("change-icon");
|
||||
setIcon(updatedIcon, "refresh-cw");
|
||||
updatedSection
|
||||
.createSpan("change-text")
|
||||
.setText(
|
||||
t("Existing views to be updated") +
|
||||
` (${preview.viewsToUpdate.length})`
|
||||
);
|
||||
}
|
||||
|
||||
// Settings changes
|
||||
if (preview.settingsChanges.length > 0) {
|
||||
const settingsChangesSection =
|
||||
changesSection.createDiv("settings-changes");
|
||||
const settingsIcon =
|
||||
settingsChangesSection.createSpan("change-icon");
|
||||
setIcon(settingsIcon, "settings");
|
||||
settingsChangesSection
|
||||
.createSpan("change-text")
|
||||
.setText(t("Feature changes"));
|
||||
|
||||
const changesList = settingsChangesSection.createEl("ul", {
|
||||
cls: "settings-changes-list",
|
||||
});
|
||||
preview.settingsChanges.forEach((change) => {
|
||||
const item = changesList.createEl("li");
|
||||
item.setText(change);
|
||||
});
|
||||
}
|
||||
|
||||
// Safety note
|
||||
const safetyNote = changesSection.createDiv("safety-note");
|
||||
const safetyIcon = safetyNote.createSpan("safety-icon");
|
||||
setIcon(safetyIcon, "info");
|
||||
safetyNote
|
||||
.createSpan("safety-text")
|
||||
.setText(
|
||||
t(
|
||||
"Only template settings will be applied. Your existing custom configurations will be preserved."
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn("Could not generate configuration preview:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get icon for configuration mode
|
||||
*/
|
||||
private getConfigIcon(mode: string): string {
|
||||
switch (mode) {
|
||||
case "beginner":
|
||||
return "edit-3"; // Lucide edit icon
|
||||
case "advanced":
|
||||
return "settings"; // Lucide settings icon
|
||||
case "power":
|
||||
return "zap"; // Lucide lightning bolt icon
|
||||
default:
|
||||
return "clipboard-list"; // Lucide clipboard icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
import { t } from "@/translations/helper";
|
||||
|
||||
export type Placement = "sideleaves" | "inline";
|
||||
|
||||
export class FluentPlacement {
|
||||
render(
|
||||
container: HTMLElement,
|
||||
current: Placement | null,
|
||||
onSelect: (p: Placement) => void
|
||||
) {
|
||||
container.empty();
|
||||
|
||||
const section = container.createDiv({ cls: "placement-selection" });
|
||||
section.createEl("h2", { text: t("Fluent Layout"), cls: "section-title" });
|
||||
section.createEl("p", {
|
||||
text: t("Choose how to display Fluent: use Sideleaves for enhanced multi-column collaboration, or Inline for an immersive single-page experience."),
|
||||
cls: "section-desc"
|
||||
});
|
||||
|
||||
const options = section.createDiv({ cls: "placement-options" });
|
||||
|
||||
const makeCard = (
|
||||
id: Placement,
|
||||
titleText: string,
|
||||
descText: string
|
||||
) => {
|
||||
const el = options.createDiv({ cls: `placement-card placement-${id}` });
|
||||
const header = el.createDiv({ cls: "placement-card-header" });
|
||||
header.createDiv({ cls: "placement-card-title", text: titleText });
|
||||
const body = el.createDiv({ cls: "placement-card-body" });
|
||||
const preview = body.createDiv({ cls: "placement-card-preview" });
|
||||
// Visual representation for each placement mode
|
||||
if (id === "sideleaves") {
|
||||
// Three column layout visual
|
||||
const col1 = preview.createDiv({ cls: "placement-preview-col" });
|
||||
const col2 = preview.createDiv({ cls: "placement-preview-col placement-preview-main" });
|
||||
const col3 = preview.createDiv({ cls: "placement-preview-col" });
|
||||
} else {
|
||||
// Single column layout visual
|
||||
const single = preview.createDiv({ cls: "placement-preview-single" });
|
||||
}
|
||||
body.createDiv({ cls: "placement-card-desc", text: descText });
|
||||
if (current === id) el.addClass("is-selected");
|
||||
el.addEventListener("click", () => {
|
||||
// Remove previous selection
|
||||
options.querySelectorAll('.placement-card').forEach(card => {
|
||||
card.removeClass('is-selected');
|
||||
});
|
||||
el.addClass('is-selected');
|
||||
onSelect(id);
|
||||
});
|
||||
el.addClass("clickable-icon");
|
||||
};
|
||||
|
||||
|
||||
makeCard(
|
||||
"inline",
|
||||
t("Inline (Single-Page Immersion)"),
|
||||
t("All content in one page, focusing on the main view and reducing interface distractions.")
|
||||
);
|
||||
|
||||
makeCard(
|
||||
"sideleaves",
|
||||
t("Sideleaves (Multi-Column Collaboration)"),
|
||||
t("Left navigation and right details as separate workspace sidebars, ideal for simultaneous browsing and editing.")
|
||||
);
|
||||
|
||||
|
||||
const tips = section.createDiv({ cls: "placement-tips" });
|
||||
tips.createEl("p", {
|
||||
text: t("You can change this option later in settings. We'll strive to maintain fluidity and elegance."),
|
||||
cls: "text-muted",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,213 +0,0 @@
|
|||
import { OnboardingConfig } from "@/managers/onboarding-manager";
|
||||
import { t } from "@/translations/helper";
|
||||
import { setIcon } from "obsidian";
|
||||
|
||||
export class OnboardingComplete {
|
||||
/**
|
||||
* Render onboarding completion page
|
||||
*/
|
||||
render(containerEl: HTMLElement, config: OnboardingConfig) {
|
||||
containerEl.empty();
|
||||
|
||||
// Success message
|
||||
const successSection = containerEl.createDiv("completion-success");
|
||||
const successIcon = successSection.createDiv("success-icon");
|
||||
successIcon.setText("🎉");
|
||||
|
||||
successSection.createEl("h2", { text: t("Congratulations!") });
|
||||
successSection.createEl("p", {
|
||||
text: t(
|
||||
"Task Genius has been configured with your selected preferences"
|
||||
),
|
||||
cls: "success-message",
|
||||
});
|
||||
|
||||
// Configuration summary
|
||||
const summarySection = containerEl.createDiv("completion-summary");
|
||||
summarySection.createEl("h3", { text: t("Your Configuration") });
|
||||
|
||||
const configCard = summarySection.createDiv("config-summary-card");
|
||||
|
||||
const configHeader = configCard.createDiv("config-header");
|
||||
const iconEl = configHeader.createDiv("config-icon");
|
||||
setIcon(iconEl, this.getConfigIcon(config.mode));
|
||||
configHeader.createDiv("config-name").setText(config.name);
|
||||
|
||||
const configDescription = configCard.createDiv("config-description");
|
||||
configDescription.setText(config.description);
|
||||
|
||||
// Quick start guide
|
||||
const quickStartSection = containerEl.createDiv("quick-start-section");
|
||||
quickStartSection.createEl("h3", { text: t("Quick Start Guide") });
|
||||
|
||||
const stepsContainer = quickStartSection.createDiv("quick-start-steps");
|
||||
|
||||
const quickStartSteps = this.getQuickStartSteps(config.mode);
|
||||
quickStartSteps.forEach((step, index) => {
|
||||
const stepEl = stepsContainer.createDiv("quick-start-step");
|
||||
stepEl.createDiv("step-number").setText((index + 1).toString());
|
||||
stepEl.createDiv("step-content").setText(step);
|
||||
});
|
||||
|
||||
// Next steps
|
||||
const nextStepsSection = containerEl.createDiv("next-steps-section");
|
||||
nextStepsSection.createEl("h3", { text: t("What's next?") });
|
||||
|
||||
const nextStepsList = nextStepsSection.createEl("ul", {
|
||||
cls: "next-steps-list",
|
||||
});
|
||||
const nextSteps = [
|
||||
t("Open Task Genius view from the left ribbon"),
|
||||
t("Create your first task using Quick Capture"),
|
||||
t("Explore different views to organize your tasks"),
|
||||
t("Customize settings anytime in plugin settings"),
|
||||
];
|
||||
|
||||
nextSteps.forEach((step) => {
|
||||
const item = nextStepsList.createEl("li");
|
||||
const checkIcon = item.createSpan("step-check");
|
||||
setIcon(checkIcon, "arrow-right");
|
||||
item.createSpan("step-text").setText(step);
|
||||
});
|
||||
|
||||
// Helpful resources
|
||||
const resourcesSection = containerEl.createDiv("resources-section");
|
||||
resourcesSection.createEl("h3", { text: t("Helpful Resources") });
|
||||
|
||||
const resourcesList = resourcesSection.createDiv("resources-list");
|
||||
|
||||
const resources = [
|
||||
{
|
||||
icon: "book-open", // Lucide book icon
|
||||
title: t("Documentation"),
|
||||
description: t("Complete guide to all features"),
|
||||
url: "https://taskgenius.md",
|
||||
},
|
||||
{
|
||||
icon: "message-circle", // Lucide message circle icon
|
||||
title: t("Community"),
|
||||
description: t("Get help and share tips"),
|
||||
url: "https://discord.gg/ARR2rHHX6b",
|
||||
},
|
||||
{
|
||||
icon: "settings", // Lucide settings icon
|
||||
title: t("Settings"),
|
||||
description: t("Customize Task Genius"),
|
||||
action: "open-settings",
|
||||
},
|
||||
];
|
||||
|
||||
resources.forEach((resource) => {
|
||||
const resourceEl = resourcesList.createDiv("resource-item");
|
||||
|
||||
const resourceContent = resourceEl.createDiv("resource-content");
|
||||
resourceContent.createEl("h4", { text: resource.title });
|
||||
resourceContent.createEl("p", { text: resource.description });
|
||||
|
||||
if (resource.url) {
|
||||
resourceEl.addEventListener("click", () => {
|
||||
window.open(resource.url, "_blank");
|
||||
});
|
||||
resourceEl.addClass("resource-clickable");
|
||||
} else if (resource.action === "open-settings") {
|
||||
resourceEl.addEventListener("click", () => {
|
||||
// Open plugin settings
|
||||
// This will be handled by the main plugin
|
||||
const event = new CustomEvent("task-genius-open-settings");
|
||||
document.dispatchEvent(event);
|
||||
});
|
||||
resourceEl.addClass("resource-clickable");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration icon
|
||||
*/
|
||||
private getConfigIcon(mode: string): string {
|
||||
switch (mode) {
|
||||
case "beginner":
|
||||
return "edit-3"; // Lucide edit icon
|
||||
case "advanced":
|
||||
return "settings"; // Lucide settings icon
|
||||
case "power":
|
||||
return "zap"; // Lucide lightning bolt icon
|
||||
default:
|
||||
return "clipboard-list"; // Lucide clipboard icon
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get quick start steps based on configuration mode
|
||||
*/
|
||||
private getQuickStartSteps(mode: string): string[] {
|
||||
switch (mode) {
|
||||
case "beginner":
|
||||
return [
|
||||
t("Click the Task Genius icon in the left sidebar"),
|
||||
t("Start with the Inbox view to see all your tasks"),
|
||||
t("Use quick capture panel to quickly add your first task"),
|
||||
t("Try the Forecast view to see tasks by date"),
|
||||
];
|
||||
case "advanced":
|
||||
return [
|
||||
t("Open Task Genius and explore the available views"),
|
||||
t("Set up a project using the Projects view"),
|
||||
t("Try the Kanban board for visual task management"),
|
||||
t("Use workflow stages to track task progress"),
|
||||
];
|
||||
case "power":
|
||||
return [
|
||||
t("Explore all available views and their configurations"),
|
||||
t("Set up complex workflows for your projects"),
|
||||
t("Configure habits and rewards to stay motivated"),
|
||||
t("Integrate with external calendars and systems"),
|
||||
];
|
||||
default:
|
||||
return [
|
||||
t("Open Task Genius from the left sidebar"),
|
||||
t("Create your first task"),
|
||||
t("Explore the different views available"),
|
||||
t("Customize settings as needed"),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle feedback submission
|
||||
*/
|
||||
private handleFeedback(
|
||||
type: "positive" | "negative",
|
||||
feedbackSection: HTMLElement
|
||||
) {
|
||||
// Find and remove existing feedback buttons
|
||||
const buttonsEl = feedbackSection.querySelector(".feedback-buttons");
|
||||
if (buttonsEl) {
|
||||
buttonsEl.remove();
|
||||
}
|
||||
|
||||
// Show thank you message
|
||||
const thankYouEl = feedbackSection.createDiv("feedback-thanks");
|
||||
thankYouEl.createEl("p", {
|
||||
text:
|
||||
type === "positive"
|
||||
? t("Thank you for your positive feedback!")
|
||||
: t(
|
||||
"Thank you for your feedback. We'll continue improving the experience."
|
||||
),
|
||||
cls: "feedback-thanks-message",
|
||||
});
|
||||
|
||||
// For negative feedback, could add a link to feedback form
|
||||
if (type === "negative") {
|
||||
const feedbackLink = thankYouEl.createEl("a", {
|
||||
text: t("Share detailed feedback"),
|
||||
href: "https://github.com/obsidian-task-genius/feedback/issues/new",
|
||||
});
|
||||
feedbackLink.setAttribute("target", "_blank");
|
||||
}
|
||||
|
||||
// Log feedback (could be sent to analytics in the future)
|
||||
console.log(`Onboarding feedback: ${type}`);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,13 +14,14 @@ export enum OnboardingStep {
|
|||
FLUENT_PROJECTS = 5,
|
||||
FLUENT_OTHER_VIEWS = 6,
|
||||
FLUENT_TOPNAV = 7,
|
||||
FLUENT_PLACEMENT = 8,
|
||||
// Config & rest
|
||||
SETTINGS_CHECK = 8,
|
||||
USER_LEVEL_SELECT = 9,
|
||||
FILE_FILTER = 10,
|
||||
CONFIG_PREVIEW = 11,
|
||||
TASK_CREATION_GUIDE = 12,
|
||||
COMPLETE = 13,
|
||||
SETTINGS_CHECK = 9,
|
||||
USER_LEVEL_SELECT = 10,
|
||||
FILE_FILTER = 11,
|
||||
CONFIG_PREVIEW = 12,
|
||||
TASK_CREATION_GUIDE = 13,
|
||||
COMPLETE = 14,
|
||||
}
|
||||
|
||||
export interface OnboardingState {
|
||||
|
|
@ -173,6 +174,9 @@ export class OnboardingController {
|
|||
nextStep = OnboardingStep.FLUENT_TOPNAV;
|
||||
break;
|
||||
case OnboardingStep.FLUENT_TOPNAV:
|
||||
nextStep = OnboardingStep.FLUENT_PLACEMENT;
|
||||
break;
|
||||
case OnboardingStep.FLUENT_PLACEMENT:
|
||||
nextStep = OnboardingStep.SETTINGS_CHECK;
|
||||
break;
|
||||
|
||||
|
|
@ -259,11 +263,14 @@ export class OnboardingController {
|
|||
case OnboardingStep.FLUENT_TOPNAV:
|
||||
prevStep = OnboardingStep.FLUENT_OTHER_VIEWS;
|
||||
break;
|
||||
case OnboardingStep.FLUENT_PLACEMENT:
|
||||
prevStep = OnboardingStep.FLUENT_TOPNAV;
|
||||
break;
|
||||
|
||||
case OnboardingStep.SETTINGS_CHECK:
|
||||
// Go back to last fluent step or mode select based on UI mode
|
||||
if (this.state.uiMode === "fluent") {
|
||||
prevStep = OnboardingStep.FLUENT_TOPNAV;
|
||||
prevStep = OnboardingStep.FLUENT_PLACEMENT;
|
||||
} else {
|
||||
prevStep = OnboardingStep.MODE_SELECT;
|
||||
}
|
||||
|
|
@ -277,7 +284,7 @@ export class OnboardingController {
|
|||
) {
|
||||
prevStep = OnboardingStep.SETTINGS_CHECK;
|
||||
} else if (this.state.uiMode === "fluent") {
|
||||
prevStep = OnboardingStep.FLUENT_TOPNAV;
|
||||
prevStep = OnboardingStep.FLUENT_PLACEMENT;
|
||||
} else {
|
||||
prevStep = OnboardingStep.MODE_SELECT;
|
||||
}
|
||||
|
|
@ -383,11 +390,15 @@ export class OnboardingController {
|
|||
* Check if skip button should be shown
|
||||
*/
|
||||
canSkip(): boolean {
|
||||
const step = this.state.currentStep;
|
||||
|
||||
if (step === OnboardingStep.COMPLETE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
this.state.currentStep === OnboardingStep.INTRO ||
|
||||
this.state.currentStep === OnboardingStep.MODE_SELECT ||
|
||||
this.state.currentStep === OnboardingStep.SETTINGS_CHECK ||
|
||||
this.state.currentStep === OnboardingStep.FILE_FILTER
|
||||
step === OnboardingStep.INTRO ||
|
||||
this.canGoBack()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export class OnboardingLayout extends Component {
|
|||
constructor(
|
||||
container: HTMLElement,
|
||||
controller: OnboardingController,
|
||||
callbacks: OnboardingLayoutCallbacks
|
||||
callbacks: OnboardingLayoutCallbacks,
|
||||
) {
|
||||
super();
|
||||
this.container = container;
|
||||
|
|
@ -50,7 +50,7 @@ export class OnboardingLayout extends Component {
|
|||
*/
|
||||
private createLayout() {
|
||||
this.container.empty();
|
||||
this.container.addClass("onboarding-view");
|
||||
this.container.toggleClass(["onboarding-view"], true);
|
||||
|
||||
// Header section
|
||||
this.headerEl = this.container.createDiv("onboarding-header");
|
||||
|
|
@ -66,6 +66,10 @@ export class OnboardingLayout extends Component {
|
|||
this.container.createEl("div", {
|
||||
cls: "onboarding-shadow",
|
||||
});
|
||||
|
||||
this.container.createEl("div", {
|
||||
cls: "tg-noise-layer",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -128,6 +132,7 @@ export class OnboardingLayout extends Component {
|
|||
// Next button
|
||||
const isLastStep = step === OnboardingStep.COMPLETE;
|
||||
const isSettingsCheck = step === OnboardingStep.SETTINGS_CHECK;
|
||||
const isModeSelect = step === OnboardingStep.MODE_SELECT;
|
||||
this.nextButton.buttonEl.toggleVisibility(true);
|
||||
|
||||
// Update button text based on step and selection
|
||||
|
|
@ -139,15 +144,17 @@ export class OnboardingLayout extends Component {
|
|||
} else {
|
||||
this.nextButton.setButtonText(t("Continue"));
|
||||
}
|
||||
} else if (isModeSelect && state.uiMode === "fluent") {
|
||||
this.nextButton.setButtonText(t("Next to Introduction"));
|
||||
} else {
|
||||
this.nextButton.setButtonText(
|
||||
isLastStep ? t("Start Using Task Genius") : t("Next")
|
||||
isLastStep ? t("Start Using Task Genius") : t("Next"),
|
||||
);
|
||||
}
|
||||
|
||||
// Enable/disable next based on validation
|
||||
this.nextButton.setDisabled(
|
||||
!this.controller.canGoNext() || state.isCompleting
|
||||
!this.controller.canGoNext() || state.isCompleting,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import { FluentMainNavigationStep } from "./steps/FluentMainNavigationStep";
|
|||
import { FluentProjectSectionStep } from "./steps/FluentProjectSectionStep";
|
||||
import { FluentOtherViewsStep } from "./steps/FluentOtherViewsStep";
|
||||
import { FluentTopNavigationStep } from "./steps/FluentTopNavigationStep";
|
||||
import { PlacementStep } from "./steps/PlacementStep";
|
||||
import { UserLevelStep } from "./steps/UserLevelStep";
|
||||
import { FileFilterStep } from "./steps/FileFilterStep";
|
||||
import { ConfigPreviewStep } from "./steps/ConfigPreviewStep";
|
||||
|
|
@ -51,7 +52,7 @@ export class OnboardingView extends ItemView {
|
|||
constructor(
|
||||
leaf: WorkspaceLeaf,
|
||||
plugin: TaskProgressBarPlugin,
|
||||
onComplete: () => void
|
||||
onComplete: () => void,
|
||||
) {
|
||||
super(leaf);
|
||||
this.plugin = plugin;
|
||||
|
|
@ -78,7 +79,7 @@ export class OnboardingView extends ItemView {
|
|||
}
|
||||
|
||||
getDisplayText(): string {
|
||||
return t("Task Genius Setup");
|
||||
return t("Setup");
|
||||
}
|
||||
|
||||
getIcon(): string {
|
||||
|
|
@ -140,7 +141,7 @@ export class OnboardingView extends ItemView {
|
|||
headerEl,
|
||||
contentEl,
|
||||
footerEl,
|
||||
this.controller
|
||||
this.controller,
|
||||
);
|
||||
break;
|
||||
|
||||
|
|
@ -148,7 +149,7 @@ export class OnboardingView extends ItemView {
|
|||
ModeSelectionStep.render(
|
||||
headerEl,
|
||||
contentEl,
|
||||
this.controller
|
||||
this.controller,
|
||||
);
|
||||
break;
|
||||
|
||||
|
|
@ -156,50 +157,53 @@ export class OnboardingView extends ItemView {
|
|||
FluentOverviewStep.render(
|
||||
headerEl,
|
||||
contentEl,
|
||||
this.controller
|
||||
this.controller,
|
||||
);
|
||||
break;
|
||||
case OnboardingStep.FLUENT_WS_SELECTOR:
|
||||
FluentWorkspaceSelectorStep.render(
|
||||
headerEl,
|
||||
contentEl,
|
||||
this.controller
|
||||
this.controller,
|
||||
);
|
||||
break;
|
||||
case OnboardingStep.FLUENT_MAIN_NAV:
|
||||
FluentMainNavigationStep.render(
|
||||
headerEl,
|
||||
contentEl,
|
||||
this.controller
|
||||
this.controller,
|
||||
);
|
||||
break;
|
||||
case OnboardingStep.FLUENT_PROJECTS:
|
||||
FluentProjectSectionStep.render(
|
||||
headerEl,
|
||||
contentEl,
|
||||
this.controller
|
||||
this.controller,
|
||||
);
|
||||
break;
|
||||
case OnboardingStep.FLUENT_OTHER_VIEWS:
|
||||
FluentOtherViewsStep.render(
|
||||
headerEl,
|
||||
contentEl,
|
||||
this.controller
|
||||
this.controller,
|
||||
);
|
||||
break;
|
||||
case OnboardingStep.FLUENT_TOPNAV:
|
||||
FluentTopNavigationStep.render(
|
||||
headerEl,
|
||||
contentEl,
|
||||
this.controller
|
||||
this.controller,
|
||||
);
|
||||
break;
|
||||
case OnboardingStep.FLUENT_PLACEMENT:
|
||||
PlacementStep.render(headerEl, contentEl, this.controller);
|
||||
break;
|
||||
|
||||
case OnboardingStep.SETTINGS_CHECK:
|
||||
SettingsCheckStep.render(
|
||||
headerEl,
|
||||
contentEl,
|
||||
this.controller
|
||||
this.controller,
|
||||
);
|
||||
break;
|
||||
|
||||
|
|
@ -208,7 +212,7 @@ export class OnboardingView extends ItemView {
|
|||
headerEl,
|
||||
contentEl,
|
||||
this.controller,
|
||||
this.configManager
|
||||
this.configManager,
|
||||
);
|
||||
break;
|
||||
|
||||
|
|
@ -217,7 +221,7 @@ export class OnboardingView extends ItemView {
|
|||
headerEl,
|
||||
contentEl,
|
||||
this.controller,
|
||||
this.plugin
|
||||
this.plugin,
|
||||
);
|
||||
break;
|
||||
|
||||
|
|
@ -226,7 +230,7 @@ export class OnboardingView extends ItemView {
|
|||
headerEl,
|
||||
contentEl,
|
||||
this.controller,
|
||||
this.configManager
|
||||
this.configManager,
|
||||
);
|
||||
break;
|
||||
|
||||
|
|
@ -235,7 +239,7 @@ export class OnboardingView extends ItemView {
|
|||
headerEl,
|
||||
contentEl,
|
||||
this.controller,
|
||||
this.plugin
|
||||
this.plugin,
|
||||
);
|
||||
break;
|
||||
|
||||
|
|
@ -253,13 +257,9 @@ export class OnboardingView extends ItemView {
|
|||
const step = this.controller.getCurrentStep();
|
||||
const state = this.controller.getState();
|
||||
|
||||
console.log("handleNext - Current step:", OnboardingStep[step]);
|
||||
console.log("handleNext - UI Mode:", state.uiMode);
|
||||
console.log("handleNext - User has changes:", state.userHasChanges);
|
||||
|
||||
// Show config check transition only when entering Settings Check from:
|
||||
// - Mode Select with Legacy
|
||||
// - The last Fluent step (Top Navigation)
|
||||
// - The final Fluent step (Placement selection)
|
||||
if (step === OnboardingStep.MODE_SELECT) {
|
||||
if (state.uiMode === "legacy" && state.userHasChanges) {
|
||||
// Clear header before showing transition to avoid residual UI
|
||||
|
|
@ -267,7 +267,7 @@ export class OnboardingView extends ItemView {
|
|||
await this.showConfigCheckTransition();
|
||||
}
|
||||
}
|
||||
if (step === OnboardingStep.FLUENT_TOPNAV) {
|
||||
if (step === OnboardingStep.FLUENT_PLACEMENT) {
|
||||
if (state.userHasChanges) {
|
||||
// Clear header before showing transition to avoid residual UI
|
||||
this.layout.clearHeader();
|
||||
|
|
@ -309,7 +309,7 @@ export class OnboardingView extends ItemView {
|
|||
console.log("handleNext - Navigation result:", success);
|
||||
console.log(
|
||||
"handleNext - New step:",
|
||||
OnboardingStep[this.controller.getCurrentStep()]
|
||||
OnboardingStep[this.controller.getCurrentStep()],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -330,7 +330,7 @@ export class OnboardingView extends ItemView {
|
|||
() => {
|
||||
resolve();
|
||||
},
|
||||
state.userHasChanges
|
||||
state.userHasChanges,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
@ -381,9 +381,8 @@ export class OnboardingView extends ItemView {
|
|||
}
|
||||
|
||||
if (isFluent && this.plugin.settings.fluentView) {
|
||||
(
|
||||
this.plugin.settings.fluentView
|
||||
).useWorkspaceSideLeaves = !!state.useSideLeaves;
|
||||
this.plugin.settings.fluentView.useWorkspaceSideLeaves =
|
||||
!!state.useSideLeaves;
|
||||
}
|
||||
|
||||
await this.plugin.saveSettings();
|
||||
|
|
@ -398,7 +397,7 @@ export class OnboardingView extends ItemView {
|
|||
|
||||
if (!config || state.isCompleting) return;
|
||||
|
||||
this.controller.updateState({isCompleting: true});
|
||||
this.controller.updateState({ isCompleting: true });
|
||||
|
||||
try {
|
||||
// Mark onboarding as completed
|
||||
|
|
@ -409,7 +408,7 @@ export class OnboardingView extends ItemView {
|
|||
this.leaf.detach();
|
||||
} catch (error) {
|
||||
console.error("Failed to complete onboarding:", error);
|
||||
this.controller.updateState({isCompleting: false});
|
||||
this.controller.updateState({ isCompleting: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export class TaskCreationGuide {
|
|||
const introSection = containerEl.createDiv("task-guide-intro");
|
||||
introSection.createEl("p", {
|
||||
text: t(
|
||||
"Learn the different ways to create and format tasks in Task Genius. You can use either emoji-based or Dataview-style syntax."
|
||||
"Learn the different ways to create and format tasks in Task Genius. You can use either emoji-based or Dataview-style syntax.",
|
||||
),
|
||||
cls: "guide-description",
|
||||
});
|
||||
|
|
@ -37,18 +37,18 @@ export class TaskCreationGuide {
|
|||
*/
|
||||
private renderTaskFormats(containerEl: HTMLElement) {
|
||||
const formatsSection = containerEl.createDiv("task-formats-section");
|
||||
formatsSection.createEl("h3", {text: t("Task Format Examples")});
|
||||
formatsSection.createEl("h3", { text: t("Task Format Examples") });
|
||||
|
||||
// Basic task format
|
||||
const basicFormat = formatsSection.createDiv("format-example");
|
||||
basicFormat.createEl("h4", {text: t("Basic Task")});
|
||||
basicFormat.createEl("h4", { text: t("Basic Task") });
|
||||
basicFormat.createEl("code", {
|
||||
text: "- [ ] Complete project documentation",
|
||||
});
|
||||
|
||||
// Emoji format
|
||||
const emojiFormat = formatsSection.createDiv("format-example");
|
||||
emojiFormat.createEl("h4", {text: t("With Emoji Metadata")});
|
||||
emojiFormat.createEl("h4", { text: t("With Emoji Metadata") });
|
||||
emojiFormat.createEl("code", {
|
||||
text: "- [ ] Complete project documentation 📅 2024-01-15 🔺 #project/docs",
|
||||
});
|
||||
|
|
@ -56,20 +56,20 @@ export class TaskCreationGuide {
|
|||
const emojiLegend = emojiFormat.createDiv("format-legend");
|
||||
emojiLegend.createEl("small", {
|
||||
text: t(
|
||||
"📅 = Due date, 🔺 = High priority, #project/ = Docs project tag"
|
||||
"📅 = Due date, 🔺 = High priority, #project/ = Docs project tag",
|
||||
),
|
||||
});
|
||||
|
||||
// Dataview format
|
||||
const dataviewFormat = formatsSection.createDiv("format-example");
|
||||
dataviewFormat.createEl("h4", {text: t("With Dataview Metadata")});
|
||||
dataviewFormat.createEl("h4", { text: t("With Dataview Metadata") });
|
||||
dataviewFormat.createEl("code", {
|
||||
text: "- [ ] Complete project documentation [due:: 2024-01-15] [priority:: high] [project:: docs]",
|
||||
});
|
||||
|
||||
// Mixed format
|
||||
const mixedFormat = formatsSection.createDiv("format-example");
|
||||
mixedFormat.createEl("h4", {text: t("Mixed Format")});
|
||||
mixedFormat.createEl("h4", { text: t("Mixed Format") });
|
||||
mixedFormat.createEl("code", {
|
||||
text: "- [ ] Complete project documentation 📅 2024-01-15 [priority:: high] @work",
|
||||
});
|
||||
|
|
@ -81,48 +81,48 @@ export class TaskCreationGuide {
|
|||
|
||||
// Status markers
|
||||
const statusSection = formatsSection.createDiv("status-markers");
|
||||
statusSection.createEl("h4", {text: t("Task Status Markers")});
|
||||
statusSection.createEl("h4", { text: t("Task Status Markers") });
|
||||
|
||||
const statusList = statusSection.createEl("ul", {cls: "status-list"});
|
||||
const statusList = statusSection.createEl("ul", { cls: "status-list" });
|
||||
const statusMarkers = [
|
||||
{marker: "[ ]", description: t("Not started")},
|
||||
{marker: "[x]", description: t("Completed")},
|
||||
{marker: "[/]", description: t("In progress")},
|
||||
{marker: "[?]", description: t("Planned")},
|
||||
{marker: "[-]", description: t("Abandoned")},
|
||||
{ marker: "[ ]", description: t("Not started") },
|
||||
{ marker: "[x]", description: t("Completed") },
|
||||
{ marker: "[/]", description: t("In progress") },
|
||||
{ marker: "[?]", description: t("Planned") },
|
||||
{ marker: "[-]", description: t("Abandoned") },
|
||||
];
|
||||
|
||||
statusMarkers.forEach((status) => {
|
||||
const item = statusList.createEl("li");
|
||||
item.createEl("code", {text: status.marker});
|
||||
item.createEl("code", { text: status.marker });
|
||||
item.createSpan().setText(" - " + status.description);
|
||||
});
|
||||
|
||||
// Metadata symbols
|
||||
const metadataSection = formatsSection.createDiv("metadata-symbols");
|
||||
metadataSection.createEl("h4", {text: t("Common Metadata Symbols")});
|
||||
metadataSection.createEl("h4", { text: t("Common Metadata Symbols") });
|
||||
|
||||
const symbolsList = metadataSection.createEl("ul", {
|
||||
cls: "symbols-list",
|
||||
});
|
||||
const symbols = [
|
||||
{symbol: "📅", description: t("Due date")},
|
||||
{symbol: "🛫", description: t("Start date")},
|
||||
{symbol: "⏳", description: t("Scheduled date")},
|
||||
{symbol: "🔺", description: t("High priority")},
|
||||
{symbol: "⏫", description: t("Higher priority")},
|
||||
{symbol: "🔼", description: t("Medium priority")},
|
||||
{symbol: "🔽", description: t("Lower priority")},
|
||||
{symbol: "⏬", description: t("Lowest priority")},
|
||||
{symbol: "🔁", description: t("Recurring task")},
|
||||
{symbol: "#", description: t("Project/tag")},
|
||||
{symbol: "@", description: t("Context")},
|
||||
{ symbol: "📅", description: t("Due date") },
|
||||
{ symbol: "🛫", description: t("Start date") },
|
||||
{ symbol: "⏳", description: t("Scheduled date") },
|
||||
{ symbol: "🔺", description: t("High priority") },
|
||||
{ symbol: "⏫", description: t("Higher priority") },
|
||||
{ symbol: "🔼", description: t("Medium priority") },
|
||||
{ symbol: "🔽", description: t("Lower priority") },
|
||||
{ symbol: "⏬", description: t("Lowest priority") },
|
||||
{ symbol: "🔁", description: t("Recurring task") },
|
||||
{ symbol: "#", description: t("Project/tag") },
|
||||
{ symbol: "@", description: t("Context") },
|
||||
];
|
||||
|
||||
symbols.forEach((symbol) => {
|
||||
const item = symbolsList.createEl("li");
|
||||
item.createSpan().setText(
|
||||
symbol.symbol + " - " + symbol.description
|
||||
symbol.symbol + " - " + symbol.description,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
@ -132,14 +132,14 @@ export class TaskCreationGuide {
|
|||
*/
|
||||
private renderQuickCaptureDemo(containerEl: HTMLElement) {
|
||||
const quickCaptureSection = containerEl.createDiv(
|
||||
"quick-capture-section"
|
||||
"quick-capture-section",
|
||||
);
|
||||
quickCaptureSection.createEl("h3", {text: t("Quick Capture")});
|
||||
quickCaptureSection.createEl("h3", { text: t("Quick Capture") });
|
||||
|
||||
const demoContent = quickCaptureSection.createDiv("demo-content");
|
||||
demoContent.createEl("p", {
|
||||
text: t(
|
||||
"Use quick capture panel to quickly capture tasks from anywhere in Obsidian."
|
||||
"Use quick capture panel to quickly capture tasks from anywhere in Obsidian.",
|
||||
),
|
||||
});
|
||||
|
||||
|
|
@ -159,16 +159,16 @@ export class TaskCreationGuide {
|
|||
// Show info that quick capture will be enabled
|
||||
new Notice(
|
||||
t(
|
||||
"Quick capture is now enabled in your configuration!"
|
||||
"Quick capture is now enabled in your configuration!",
|
||||
),
|
||||
3000
|
||||
3000,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to open quick capture:", error);
|
||||
new Notice(
|
||||
t("Failed to open quick capture. Please try again later."),
|
||||
3000
|
||||
3000,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -179,7 +179,7 @@ export class TaskCreationGuide {
|
|||
*/
|
||||
private renderInteractivePractice(containerEl: HTMLElement) {
|
||||
const practiceSection = containerEl.createDiv("practice-section");
|
||||
practiceSection.createEl("h3", {text: t("Try It Yourself")});
|
||||
practiceSection.createEl("h3", { text: t("Try It Yourself") });
|
||||
|
||||
practiceSection.createEl("p", {
|
||||
text: t("Practice creating a task with the format you prefer:"),
|
||||
|
|
@ -236,7 +236,7 @@ export class TaskCreationGuide {
|
|||
if (!isValidTask) {
|
||||
feedbackEl.createEl("div", {
|
||||
text: t(
|
||||
"This doesn't look like a valid task. Tasks should start with '- [ ]'"
|
||||
"This doesn't look like a valid task. Tasks should start with '- [ ]'",
|
||||
),
|
||||
cls: "validation-message validation-error",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,134 +0,0 @@
|
|||
import {
|
||||
OnboardingConfigManager,
|
||||
OnboardingConfig,
|
||||
} from "@/managers/onboarding-manager";
|
||||
import { setIcon } from "obsidian";
|
||||
|
||||
export class UserLevelSelector {
|
||||
private configManager: OnboardingConfigManager;
|
||||
private selectedConfig: OnboardingConfig | null = null;
|
||||
private onSelectionChange: (config: OnboardingConfig) => void = () => {};
|
||||
|
||||
constructor(configManager: OnboardingConfigManager) {
|
||||
this.configManager = configManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the user level selector
|
||||
*/
|
||||
render(
|
||||
containerEl: HTMLElement,
|
||||
onSelectionChange: (config: OnboardingConfig) => void
|
||||
) {
|
||||
this.onSelectionChange = onSelectionChange;
|
||||
containerEl.empty();
|
||||
|
||||
const configs = this.configManager.getOnboardingConfigs();
|
||||
|
||||
// Create card container
|
||||
const cardsContainer = containerEl.createDiv("user-level-cards");
|
||||
|
||||
// Create cards for each configuration
|
||||
configs.forEach((config) => {
|
||||
this.createConfigCard(cardsContainer, config);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a configuration card
|
||||
*/
|
||||
private createConfigCard(container: HTMLElement, config: OnboardingConfig) {
|
||||
const card = container.createDiv("user-level-card");
|
||||
card.setAttribute("data-mode", config.mode);
|
||||
|
||||
// Card header with icon and title
|
||||
const cardHeader = card.createDiv("card-header");
|
||||
|
||||
const iconEl = cardHeader.createDiv("card-icon");
|
||||
setIcon(iconEl, this.getConfigIcon(config.mode));
|
||||
|
||||
const titleEl = cardHeader.createEl("h3", {
|
||||
text: config.name,
|
||||
cls: "card-title",
|
||||
});
|
||||
|
||||
// Card description
|
||||
const descEl = card.createEl("p", {
|
||||
text: config.description,
|
||||
cls: "card-description",
|
||||
});
|
||||
|
||||
// Features list
|
||||
const featuresEl = card.createDiv("card-features");
|
||||
const featuresList = featuresEl.createEl("ul");
|
||||
|
||||
config.features.forEach((feature) => {
|
||||
const featureItem = featuresList.createEl("li");
|
||||
featureItem.setText(feature);
|
||||
});
|
||||
|
||||
// Recommendation badge for beginner
|
||||
// if (config.mode === 'beginner') {
|
||||
// const badge = card.createDiv("recommendation-badge");
|
||||
// badge.setText(t("Recommended for new users"));
|
||||
// }
|
||||
|
||||
// Click handler
|
||||
card.addEventListener("click", () => {
|
||||
this.selectConfig(config);
|
||||
});
|
||||
|
||||
// Hover effects
|
||||
card.addEventListener("mouseenter", () => {
|
||||
card.addClass("card-hover");
|
||||
});
|
||||
|
||||
card.addEventListener("mouseleave", () => {
|
||||
card.removeClass("card-hover");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get icon for configuration mode
|
||||
*/
|
||||
private getConfigIcon(mode: string): string {
|
||||
switch (mode) {
|
||||
case "beginner":
|
||||
return "edit-3"; // Lucide edit icon
|
||||
case "advanced":
|
||||
return "settings"; // Lucide settings icon
|
||||
case "power":
|
||||
return "zap"; // Lucide lightning bolt icon
|
||||
default:
|
||||
return "clipboard-list"; // Lucide clipboard icon
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a configuration
|
||||
*/
|
||||
private selectConfig(config: OnboardingConfig) {
|
||||
// Remove previous selection
|
||||
if (this.selectedConfig) {
|
||||
const prevCard = document.querySelector(
|
||||
`[data-mode="${this.selectedConfig.mode}"]`
|
||||
);
|
||||
prevCard?.removeClass("selected");
|
||||
}
|
||||
|
||||
// Select new config
|
||||
this.selectedConfig = config;
|
||||
const newCard = document.querySelector(`[data-mode="${config.mode}"]`);
|
||||
newCard?.addClass("selected");
|
||||
|
||||
// Trigger callback
|
||||
this.onSelectionChange(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get selected configuration
|
||||
*/
|
||||
getSelectedConfig(): OnboardingConfig | null {
|
||||
return this.selectedConfig;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,9 +8,9 @@ import { OnboardingConfig } from "@/managers/onboarding-manager";
|
|||
*/
|
||||
export class CompleteStep {
|
||||
private static readonly ICON_MAP: Record<string, string> = {
|
||||
beginner: "seedling",
|
||||
advanced: "zap",
|
||||
power: "rocket",
|
||||
beginner: "edit-3",
|
||||
advanced: "settings",
|
||||
power: "zap",
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -29,20 +29,21 @@ export class CompleteStep {
|
|||
if (!config) return;
|
||||
|
||||
// Header
|
||||
headerEl.createEl("h1", { text: t("Setup Complete! 🎉") });
|
||||
headerEl.createEl("h1", { text: t("Task Genius is ready!") });
|
||||
headerEl.createEl("p", {
|
||||
text: t("Task Genius is ready to use"),
|
||||
text: t("You're all set to start managing your tasks"),
|
||||
cls: "onboarding-subtitle",
|
||||
});
|
||||
|
||||
// Success section
|
||||
const successSection = contentEl.createDiv("completion-success");
|
||||
const successIcon = successSection.createDiv("success-icon");
|
||||
successIcon.setText("✨");
|
||||
successIcon.setText("🎉");
|
||||
|
||||
successSection.createEl("h2", { text: t("Congratulations!") });
|
||||
successSection.createEl("p", {
|
||||
text: t(
|
||||
"Your task management workspace has been configured successfully"
|
||||
"Task Genius has been configured with your selected preferences"
|
||||
),
|
||||
cls: "success-message",
|
||||
});
|
||||
|
|
@ -53,9 +54,6 @@ export class CompleteStep {
|
|||
// Quick start guide
|
||||
this.renderQuickStart(contentEl, config);
|
||||
|
||||
// Next steps
|
||||
this.renderNextSteps(contentEl);
|
||||
|
||||
// Resources
|
||||
this.renderResources(contentEl);
|
||||
}
|
||||
|
|
@ -101,36 +99,12 @@ export class CompleteStep {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Render next steps
|
||||
*/
|
||||
private static renderNextSteps(container: HTMLElement) {
|
||||
const section = container.createDiv("next-steps-section");
|
||||
section.createEl("h3", { text: t("What's next?") });
|
||||
|
||||
const list = section.createEl("ul", { cls: "next-steps-list" });
|
||||
|
||||
const steps = [
|
||||
t("Open Task Genius view from the left sidebar"),
|
||||
t("Create your first task using Quick Capture"),
|
||||
t("Explore different views to organize your tasks"),
|
||||
t("Customize settings anytime in plugin settings"),
|
||||
];
|
||||
|
||||
steps.forEach((step) => {
|
||||
const item = list.createEl("li");
|
||||
const checkIcon = item.createSpan("step-check");
|
||||
setIcon(checkIcon, "arrow-right");
|
||||
item.createSpan("step-text").setText(step);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Render resources
|
||||
*/
|
||||
private static renderResources(container: HTMLElement) {
|
||||
const section = container.createDiv("resources-section");
|
||||
section.createEl("h3", { text: t("Resources") });
|
||||
section.createEl("h3", { text: t("Helpful Resources") });
|
||||
|
||||
const list = section.createDiv("resources-list");
|
||||
|
||||
|
|
@ -138,7 +112,7 @@ export class CompleteStep {
|
|||
{
|
||||
icon: "book-open",
|
||||
title: t("Documentation"),
|
||||
desc: t("Learn all features"),
|
||||
desc: t("Complete guide to all features"),
|
||||
url: "https://taskgenius.md",
|
||||
},
|
||||
{
|
||||
|
|
@ -147,19 +121,35 @@ export class CompleteStep {
|
|||
desc: t("Get help and share tips"),
|
||||
url: "https://discord.gg/ARR2rHHX6b",
|
||||
},
|
||||
{
|
||||
icon: "settings",
|
||||
title: t("Settings"),
|
||||
desc: t("Customize Task Genius"),
|
||||
action: "open-settings",
|
||||
},
|
||||
];
|
||||
|
||||
resources.forEach((r) => {
|
||||
const item = list.createDiv("resource-item resource-clickable");
|
||||
const item = list.createDiv("resource-item");
|
||||
const icon = item.createDiv("resource-icon");
|
||||
setIcon(icon, r.icon);
|
||||
const content = item.createDiv("resource-content");
|
||||
content.createEl("h4", { text: r.title });
|
||||
content.createEl("p", { text: r.desc });
|
||||
|
||||
item.addEventListener("click", () => {
|
||||
window.open(r.url, "_blank");
|
||||
});
|
||||
if (r.url) {
|
||||
item.addEventListener("click", () => {
|
||||
window.open(r.url, "_blank");
|
||||
});
|
||||
item.addClass("resource-clickable");
|
||||
} else if (r.action === "open-settings") {
|
||||
item.addEventListener("click", () => {
|
||||
// Signal main plugin to open settings so we keep UI logic here.
|
||||
const event = new CustomEvent("task-genius-open-settings");
|
||||
document.dispatchEvent(event);
|
||||
});
|
||||
item.addClass("resource-clickable");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +162,7 @@ export class CompleteStep {
|
|||
return [
|
||||
t("Click the Task Genius icon in the left sidebar"),
|
||||
t("Start with the Inbox view to see all your tasks"),
|
||||
t("Use Quick Capture to add your first task"),
|
||||
t("Use quick capture panel to quickly add your first task"),
|
||||
t("Try the Forecast view to see tasks by date"),
|
||||
];
|
||||
case "advanced":
|
||||
|
|
@ -184,7 +174,7 @@ export class CompleteStep {
|
|||
];
|
||||
case "power":
|
||||
return [
|
||||
t("Explore all available views and configurations"),
|
||||
t("Explore all available views and their configurations"),
|
||||
t("Set up complex workflows for your projects"),
|
||||
t("Configure habits and rewards to stay motivated"),
|
||||
t("Integrate with external calendars and systems"),
|
||||
|
|
@ -193,7 +183,7 @@ export class CompleteStep {
|
|||
return [
|
||||
t("Open Task Genius from the left sidebar"),
|
||||
t("Create your first task"),
|
||||
t("Explore different views"),
|
||||
t("Explore the different views available"),
|
||||
t("Customize settings as needed"),
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export class ConfigPreviewStep {
|
|||
headerEl: HTMLElement,
|
||||
contentEl: HTMLElement,
|
||||
controller: OnboardingController,
|
||||
configManager: OnboardingConfigManager
|
||||
configManager: OnboardingConfigManager,
|
||||
) {
|
||||
// Clear
|
||||
headerEl.empty();
|
||||
|
|
@ -38,7 +38,7 @@ export class ConfigPreviewStep {
|
|||
headerEl.createEl("h1", { text: t("Review Your Configuration") });
|
||||
headerEl.createEl("p", {
|
||||
text: t(
|
||||
"Review the settings that will be applied for your selected mode"
|
||||
"Review the settings that will be applied for your selected mode",
|
||||
),
|
||||
cls: "onboarding-subtitle",
|
||||
});
|
||||
|
|
@ -81,23 +81,21 @@ export class ConfigPreviewStep {
|
|||
.setName(t("Include task creation guide"))
|
||||
.setDesc(t("Show a quick tutorial on creating your first task"))
|
||||
.addToggle((toggle) => {
|
||||
toggle
|
||||
.setValue(!state.skipTaskGuide)
|
||||
.onChange((value) => {
|
||||
controller.setSkipTaskGuide(!value);
|
||||
});
|
||||
toggle.setValue(!state.skipTaskGuide).onChange((value) => {
|
||||
controller.setSkipTaskGuide(!value);
|
||||
});
|
||||
});
|
||||
|
||||
// Note about customization
|
||||
Alert.create(
|
||||
contentEl,
|
||||
t(
|
||||
"You can customize any of these settings later in the plugin settings"
|
||||
"You can customize any of these settings later in the plugin settings",
|
||||
),
|
||||
{
|
||||
variant: "info",
|
||||
className: "customization-note",
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export class FileFilterStep {
|
|||
headerEl: HTMLElement,
|
||||
contentEl: HTMLElement,
|
||||
controller: OnboardingController,
|
||||
plugin: TaskProgressBarPlugin
|
||||
plugin: TaskProgressBarPlugin,
|
||||
) {
|
||||
// Clear
|
||||
headerEl.empty();
|
||||
|
|
@ -32,7 +32,7 @@ export class FileFilterStep {
|
|||
headerEl.createEl("h1", { text: t("Optimize Performance") });
|
||||
headerEl.createEl("p", {
|
||||
text: t(
|
||||
"Configure file filtering to improve indexing performance and focus on relevant content"
|
||||
"Configure file filtering to improve indexing performance and focus on relevant content",
|
||||
),
|
||||
cls: "onboarding-subtitle",
|
||||
});
|
||||
|
|
@ -62,15 +62,13 @@ export class FileFilterStep {
|
|||
*/
|
||||
private static renderConfiguration(
|
||||
container: HTMLElement,
|
||||
plugin: TaskProgressBarPlugin
|
||||
plugin: TaskProgressBarPlugin,
|
||||
) {
|
||||
// Enable/Disable toggle
|
||||
new Setting(container)
|
||||
.setName(t("Enable File Filter"))
|
||||
.setDesc(
|
||||
t(
|
||||
"Filter files during task indexing to improve performance"
|
||||
)
|
||||
t("Filter files during task indexing to improve performance"),
|
||||
)
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
|
|
@ -80,12 +78,14 @@ export class FileFilterStep {
|
|||
await plugin.saveSettings();
|
||||
// Re-render to show/hide configuration
|
||||
this.render(
|
||||
container.parentElement?.previousElementSibling as HTMLElement,
|
||||
container.parentElement?.parentElement as HTMLElement,
|
||||
container.parentElement
|
||||
?.previousElementSibling as HTMLElement,
|
||||
container.parentElement
|
||||
?.parentElement as HTMLElement,
|
||||
{} as OnboardingController,
|
||||
plugin
|
||||
plugin,
|
||||
);
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
if (!plugin.settings.fileFilter.enabled) {
|
||||
|
|
@ -97,19 +97,22 @@ export class FileFilterStep {
|
|||
.setName(t("Filter Mode"))
|
||||
.setDesc(
|
||||
t(
|
||||
"Whitelist: Include only specified paths | Blacklist: Exclude specified paths"
|
||||
)
|
||||
"Whitelist: Include only specified paths | Blacklist: Exclude specified paths",
|
||||
),
|
||||
)
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption(FilterMode.WHITELIST, t("Whitelist (Include only)"))
|
||||
.addOption(
|
||||
FilterMode.WHITELIST,
|
||||
t("Whitelist (Include only)"),
|
||||
)
|
||||
.addOption(FilterMode.BLACKLIST, t("Blacklist (Exclude)"))
|
||||
.setValue(plugin.settings.fileFilter.mode)
|
||||
.onChange(async (value: FilterMode) => {
|
||||
plugin.settings.fileFilter.mode = value;
|
||||
await plugin.saveSettings();
|
||||
this.updateStats(container, plugin);
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// Quick add rules section
|
||||
|
|
@ -132,10 +135,10 @@ export class FileFilterStep {
|
|||
autoAddRuleType: "file",
|
||||
onClose: () => {
|
||||
const rulesEl = container.querySelector(
|
||||
".file-filter-rules-container"
|
||||
".file-filter-rules-container",
|
||||
) as HTMLElement | null;
|
||||
const statsEl = container.querySelector(
|
||||
".file-filter-stats-preview"
|
||||
".file-filter-stats-preview",
|
||||
) as HTMLElement | null;
|
||||
if (rulesEl) {
|
||||
this.renderRules(rulesEl, plugin);
|
||||
|
|
@ -157,10 +160,10 @@ export class FileFilterStep {
|
|||
autoAddRuleType: "folder",
|
||||
onClose: () => {
|
||||
const rulesEl = container.querySelector(
|
||||
".file-filter-rules-container"
|
||||
".file-filter-rules-container",
|
||||
) as HTMLElement | null;
|
||||
const statsEl = container.querySelector(
|
||||
".file-filter-stats-preview"
|
||||
".file-filter-stats-preview",
|
||||
) as HTMLElement | null;
|
||||
if (rulesEl) {
|
||||
this.renderRules(rulesEl, plugin);
|
||||
|
|
@ -182,10 +185,10 @@ export class FileFilterStep {
|
|||
autoAddRuleType: "pattern",
|
||||
onClose: () => {
|
||||
const rulesEl = container.querySelector(
|
||||
".file-filter-rules-container"
|
||||
".file-filter-rules-container",
|
||||
) as HTMLElement | null;
|
||||
const statsEl = container.querySelector(
|
||||
".file-filter-stats-preview"
|
||||
".file-filter-stats-preview",
|
||||
) as HTMLElement | null;
|
||||
if (rulesEl) {
|
||||
this.renderRules(rulesEl, plugin);
|
||||
|
|
@ -216,7 +219,7 @@ export class FileFilterStep {
|
|||
*/
|
||||
private static renderRules(
|
||||
container: HTMLElement,
|
||||
plugin: TaskProgressBarPlugin
|
||||
plugin: TaskProgressBarPlugin,
|
||||
) {
|
||||
container.empty();
|
||||
|
||||
|
|
@ -251,9 +254,9 @@ export class FileFilterStep {
|
|||
this.renderRules(container, plugin);
|
||||
this.updateStats(
|
||||
container.parentElement?.querySelector(
|
||||
".file-filter-stats-preview"
|
||||
".file-filter-stats-preview",
|
||||
) as HTMLElement,
|
||||
plugin
|
||||
plugin,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -285,18 +288,13 @@ export class FileFilterStep {
|
|||
rule.type === "pattern"
|
||||
? "*.tmp, temp/*"
|
||||
: rule.type === "folder"
|
||||
? "path/to/folder"
|
||||
: "path/to/file.md",
|
||||
? "path/to/folder"
|
||||
: "path/to/file.md",
|
||||
});
|
||||
|
||||
// Add appropriate autocomplete based on rule type
|
||||
if (rule.type === "folder") {
|
||||
new FolderSuggest(
|
||||
plugin.app,
|
||||
pathInput,
|
||||
plugin,
|
||||
"single"
|
||||
);
|
||||
new FolderSuggest(plugin.app, pathInput, plugin, "single");
|
||||
} else if (rule.type === "file") {
|
||||
new FileSuggest(pathInput, plugin, (file) => {
|
||||
rule.path = file.path;
|
||||
|
|
@ -326,9 +324,9 @@ export class FileFilterStep {
|
|||
await plugin.saveSettings();
|
||||
this.updateStats(
|
||||
container.parentElement?.querySelector(
|
||||
".file-filter-stats-preview"
|
||||
".file-filter-stats-preview",
|
||||
) as HTMLElement,
|
||||
plugin
|
||||
plugin,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -345,9 +343,9 @@ export class FileFilterStep {
|
|||
this.renderRules(container, plugin);
|
||||
this.updateStats(
|
||||
container.parentElement?.querySelector(
|
||||
".file-filter-stats-preview"
|
||||
".file-filter-stats-preview",
|
||||
) as HTMLElement,
|
||||
plugin
|
||||
plugin,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -356,13 +354,16 @@ export class FileFilterStep {
|
|||
/**
|
||||
* Update statistics display
|
||||
*/
|
||||
private static updateStats(container: HTMLElement, plugin: TaskProgressBarPlugin) {
|
||||
private static updateStats(
|
||||
container: HTMLElement,
|
||||
plugin: TaskProgressBarPlugin,
|
||||
) {
|
||||
if (!container) return;
|
||||
|
||||
container.empty();
|
||||
|
||||
const activeRules = plugin.settings.fileFilter.rules.filter(
|
||||
(r) => r.enabled
|
||||
(r) => r.enabled,
|
||||
).length;
|
||||
|
||||
const stats = [
|
||||
|
|
@ -403,29 +404,15 @@ export class FileFilterStep {
|
|||
*/
|
||||
private static renderDescription(
|
||||
container: HTMLElement,
|
||||
plugin: TaskProgressBarPlugin
|
||||
plugin: TaskProgressBarPlugin,
|
||||
) {
|
||||
container.createEl("h3", { text: t("Why File Filtering?") });
|
||||
container.createEl("p", {
|
||||
text: t(
|
||||
"File filtering helps you focus on relevant content while improving performance, especially in large vaults."
|
||||
"File filtering helps you focus on relevant content while improving performance, especially in large vaults.",
|
||||
),
|
||||
});
|
||||
|
||||
const benefits = container.createEl("ul", {
|
||||
cls: "component-feature-list",
|
||||
});
|
||||
|
||||
[
|
||||
t("Faster task indexing in large vaults"),
|
||||
t("Exclude temporary or archive files"),
|
||||
t("Focus on specific project folders"),
|
||||
t("Reduce memory usage"),
|
||||
t("Improve search performance"),
|
||||
].forEach((benefit) => {
|
||||
benefits.createEl("li", { text: benefit });
|
||||
});
|
||||
|
||||
// Recommended configurations
|
||||
const recsContainer = container.createDiv({
|
||||
cls: "recommended-configs",
|
||||
|
|
@ -473,7 +460,7 @@ export class FileFilterStep {
|
|||
rec.rules.forEach((rule) => {
|
||||
// Check if rule already exists
|
||||
const exists = plugin.settings.fileFilter.rules.some(
|
||||
(r) => r.path === rule.path && r.type === rule.type
|
||||
(r) => r.path === rule.path && r.type === rule.type,
|
||||
);
|
||||
if (!exists) {
|
||||
plugin.settings.fileFilter.rules.push({
|
||||
|
|
@ -486,18 +473,17 @@ export class FileFilterStep {
|
|||
await plugin.saveSettings();
|
||||
|
||||
new Notice(
|
||||
t("Applied recommended configuration: ") + rec.title
|
||||
t("Applied recommended configuration: ") + rec.title,
|
||||
);
|
||||
|
||||
// Re-render configuration section
|
||||
const configSection =
|
||||
container.parentElement?.querySelector(
|
||||
".file-filter-preview"
|
||||
);
|
||||
const configSection = container.parentElement?.querySelector(
|
||||
".file-filter-preview",
|
||||
);
|
||||
if (configSection) {
|
||||
this.renderConfiguration(
|
||||
configSection as HTMLElement,
|
||||
plugin
|
||||
plugin,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export class ModeSelectionStep {
|
|||
static render(
|
||||
headerEl: HTMLElement,
|
||||
contentEl: HTMLElement,
|
||||
controller: OnboardingController
|
||||
controller: OnboardingController,
|
||||
) {
|
||||
// Clear
|
||||
headerEl.empty();
|
||||
|
|
@ -31,7 +31,7 @@ export class ModeSelectionStep {
|
|||
headerEl.createEl("p", {
|
||||
cls: "intro-line intro-line-4",
|
||||
text: t(
|
||||
"In the current version, Task Genius provides a brand new visual and interactive experience: Fluent; while also providing the option to return to the previous interface. Which one do you prefer?"
|
||||
"In the current version, Task Genius provides a brand new visual and interactive experience: Fluent; while also providing the option to return to the previous interface. Which one do you prefer?",
|
||||
),
|
||||
});
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ export class ModeSelectionStep {
|
|||
title: t("Fluent"),
|
||||
subtitle: t("Modern & Sleek"),
|
||||
description: t(
|
||||
"New visual design with elegant animations and modern interactions"
|
||||
"New visual design with elegant animations and modern interactions",
|
||||
),
|
||||
preview: this.createFluentPreview(),
|
||||
},
|
||||
|
|
@ -54,7 +54,7 @@ export class ModeSelectionStep {
|
|||
title: t("Legacy"),
|
||||
subtitle: t("Classic & Familiar"),
|
||||
description: t(
|
||||
"Keep the familiar interface and interaction style you know"
|
||||
"Keep the familiar interface and interaction style you know",
|
||||
),
|
||||
preview: this.createLegacyPreview(),
|
||||
},
|
||||
|
|
@ -71,7 +71,7 @@ export class ModeSelectionStep {
|
|||
},
|
||||
(mode) => {
|
||||
controller.setUIMode(mode);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Set initial selection
|
||||
|
|
@ -82,11 +82,11 @@ export class ModeSelectionStep {
|
|||
// Add info alert
|
||||
Alert.create(
|
||||
contentEl,
|
||||
t("You can change this option later in settings"),
|
||||
t("You can change this option later in interface settings"),
|
||||
{
|
||||
variant: "info",
|
||||
className: "mode-selection-tip",
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export class PlacementStep {
|
|||
static render(
|
||||
headerEl: HTMLElement,
|
||||
contentEl: HTMLElement,
|
||||
controller: OnboardingController
|
||||
controller: OnboardingController,
|
||||
) {
|
||||
// Clear
|
||||
headerEl.empty();
|
||||
|
|
@ -25,9 +25,7 @@ export class PlacementStep {
|
|||
// Header
|
||||
headerEl.createEl("h1", { text: t("Fluent Layout") });
|
||||
headerEl.createEl("p", {
|
||||
text: t(
|
||||
"Choose how to display Fluent views in your workspace"
|
||||
),
|
||||
text: t("Choose how to display Fluent views in your workspace"),
|
||||
cls: "onboarding-subtitle",
|
||||
});
|
||||
|
||||
|
|
@ -43,7 +41,7 @@ export class PlacementStep {
|
|||
title: t("Sideleaves"),
|
||||
subtitle: t("Multi-Column Collaboration"),
|
||||
description: t(
|
||||
"Left navigation and right details as separate workspace sidebars, ideal for simultaneous browsing and editing"
|
||||
"Left navigation and right details as separate workspace sidebars, ideal for simultaneous browsing and editing",
|
||||
),
|
||||
preview: this.createSideleavesPreview(),
|
||||
},
|
||||
|
|
@ -52,7 +50,7 @@ export class PlacementStep {
|
|||
title: t("Inline"),
|
||||
subtitle: t("Single-Page Immersion"),
|
||||
description: t(
|
||||
"All content in one page, focusing on the main view and reducing interface distractions"
|
||||
"All content in one page, focusing on the main view and reducing interface distractions",
|
||||
),
|
||||
preview: this.createInlinePreview(),
|
||||
},
|
||||
|
|
@ -68,12 +66,12 @@ export class PlacementStep {
|
|||
showPreview: true,
|
||||
},
|
||||
(placement) => {
|
||||
controller.setUseSideLeaves(placement === "inline");
|
||||
}
|
||||
controller.setUseSideLeaves(placement === "sideleaves");
|
||||
},
|
||||
);
|
||||
|
||||
// Set initial selection
|
||||
card.setSelected(currentPlacement);
|
||||
card.setSelected("inline");
|
||||
|
||||
// Add info alert
|
||||
Alert.create(
|
||||
|
|
@ -82,7 +80,7 @@ export class PlacementStep {
|
|||
{
|
||||
variant: "info",
|
||||
className: "placement-selection-tip",
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { t } from "@/translations/helper";
|
||||
import type TaskProgressBarPlugin from "@/index";
|
||||
import { OnboardingController } from "../OnboardingController";
|
||||
import { FormatExamples } from "./guide/FormatExamples";
|
||||
import { QuickCaptureDemo } from "./guide/QuickCaptureDemo";
|
||||
import type { OnboardingController } from "../OnboardingController";
|
||||
import { TaskCreationGuide } from "../TaskCreationGuide";
|
||||
|
||||
/**
|
||||
* Task Guide Step - Learn how to create tasks
|
||||
|
|
@ -14,7 +13,7 @@ export class TaskGuideStep {
|
|||
static render(
|
||||
headerEl: HTMLElement,
|
||||
contentEl: HTMLElement,
|
||||
controller: OnboardingController,
|
||||
_controller: OnboardingController,
|
||||
plugin: TaskProgressBarPlugin
|
||||
) {
|
||||
// Clear
|
||||
|
|
@ -22,27 +21,16 @@ export class TaskGuideStep {
|
|||
contentEl.empty();
|
||||
|
||||
// Header
|
||||
headerEl.createEl("h1", { text: t("Creating Tasks") });
|
||||
headerEl.createEl("h1", { text: t("Create Your First Task") });
|
||||
headerEl.createEl("p", {
|
||||
text: t(
|
||||
"Learn different ways to create and format tasks in Task Genius"
|
||||
"Learn the fastest ways to capture and format tasks inside Task Genius"
|
||||
),
|
||||
cls: "onboarding-subtitle",
|
||||
});
|
||||
|
||||
// Introduction
|
||||
const intro = contentEl.createDiv("task-guide-intro");
|
||||
intro.createEl("p", {
|
||||
text: t(
|
||||
"You can use either emoji-based or Dataview-style syntax for task metadata"
|
||||
),
|
||||
cls: "guide-description",
|
||||
});
|
||||
|
||||
// Format examples
|
||||
FormatExamples.render(contentEl);
|
||||
|
||||
// Quick capture demo
|
||||
QuickCaptureDemo.render(contentEl, plugin);
|
||||
// Use the shared task creation guide to render examples and demos
|
||||
const guide = new TaskCreationGuide(plugin);
|
||||
guide.render(contentEl);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export class SelectableCard<T = string> {
|
|||
container: HTMLElement,
|
||||
configs: SelectableCardConfig<T>[],
|
||||
options: SelectableCardOptions = {},
|
||||
onSelect: (id: T) => void
|
||||
onSelect: (id: T) => void,
|
||||
) {
|
||||
this.container = container;
|
||||
this.onSelect = onSelect;
|
||||
|
|
@ -44,7 +44,7 @@ export class SelectableCard<T = string> {
|
|||
|
||||
private render(
|
||||
configs: SelectableCardConfig<T>[],
|
||||
options: SelectableCardOptions
|
||||
options: SelectableCardOptions,
|
||||
) {
|
||||
const {
|
||||
containerClass = "selectable-cards-container",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
import { App, Component, setIcon } from "obsidian";
|
||||
import { Task } from "@/types/task";
|
||||
import { TaskListItemComponent } from "./listItem"; // Re-import needed components
|
||||
import {
|
||||
ViewMode,
|
||||
getViewSettingOrDefault,
|
||||
SortCriterion,
|
||||
} from "@/common/setting-definition"; // 导入 SortCriterion
|
||||
import { ViewMode, getViewSettingOrDefault } from "@/common/setting-definition"; // 导入 SortCriterion
|
||||
import { tasksToTree } from "@/utils/ui/tree-view-utils"; // Re-import needed utils
|
||||
import { TaskTreeItemComponent } from "./treeItem"; // Re-import needed components
|
||||
import { t } from "@/translations/helper";
|
||||
|
|
|
|||
3096
src/experimental/v2/TaskViewV2.ts
Normal file
3096
src/experimental/v2/TaskViewV2.ts
Normal file
File diff suppressed because it is too large
Load diff
799
src/experimental/v2/styles/v2-enhanced.css
Normal file
799
src/experimental/v2/styles/v2-enhanced.css
Normal file
|
|
@ -0,0 +1,799 @@
|
|||
/* Task Genius V2 Enhanced Styles - Shadcn-inspired */
|
||||
|
||||
/* ========== Base Variables ========== */
|
||||
:root {
|
||||
/* Shadows */
|
||||
--tg-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--tg-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-md:
|
||||
0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-lg:
|
||||
0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-xl:
|
||||
0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
|
||||
/* Animations */
|
||||
--tg-transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--tg-transition-base: 200ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--tg-transition-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
/* Border radius */
|
||||
--tg-radius-sm: 0.25rem;
|
||||
--tg-radius: 0.375rem;
|
||||
--tg-radius-md: 0.5rem;
|
||||
--tg-radius-lg: 0.75rem;
|
||||
--tg-radius-full: 9999px;
|
||||
}
|
||||
|
||||
/* ========== Task List View Styles ========== */
|
||||
.task-list-container {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 1.5rem;
|
||||
animation: fadeIn var(--tg-transition-base);
|
||||
}
|
||||
|
||||
.task-list-wrapper {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.tg-task-list-item {
|
||||
background: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--tg-radius-md);
|
||||
margin-bottom: 0.75rem;
|
||||
padding: 1rem 1.25rem;
|
||||
transition: all var(--tg-transition-fast);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tg-task-list-item::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 3px;
|
||||
background: transparent;
|
||||
transition: background var(--tg-transition-fast);
|
||||
}
|
||||
|
||||
.tg-task-list-item:hover {
|
||||
box-shadow: var(--tg-shadow);
|
||||
transform: translateY(-1px);
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.tg-task-list-item:hover::before {
|
||||
background: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.tg-task-list-item.is-completed {
|
||||
opacity: 0.6;
|
||||
background: var(--background-secondary);
|
||||
}
|
||||
|
||||
.tg-task-list-item.is-overdue::before {
|
||||
background: var(--text-error);
|
||||
}
|
||||
|
||||
.task-list-empty {
|
||||
text-align: center;
|
||||
padding: 3rem 1.5rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* ========== Task Tree View Styles ========== */
|
||||
.task-tree-container {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 1.5rem;
|
||||
animation: fadeIn var(--tg-transition-base);
|
||||
}
|
||||
|
||||
.task-tree-wrapper {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.task-tree-project {
|
||||
margin-bottom: 1.5rem;
|
||||
background: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--tg-radius-lg);
|
||||
overflow: hidden;
|
||||
transition: all var(--tg-transition-base);
|
||||
}
|
||||
|
||||
.task-tree-project:hover {
|
||||
box-shadow: var(--tg-shadow-md);
|
||||
}
|
||||
|
||||
.task-tree-project-header {
|
||||
padding: 1rem 1.25rem;
|
||||
background: var(--background-secondary);
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-normal);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
transition: background var(--tg-transition-fast);
|
||||
}
|
||||
|
||||
.task-tree-project-header:hover {
|
||||
background: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.task-tree-project-header::before {
|
||||
content: "▶";
|
||||
display: inline-block;
|
||||
margin-right: 0.5rem;
|
||||
transition: transform var(--tg-transition-fast);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.task-tree-project.is-expanded .task-tree-project-header::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.task-tree-tasks {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.task-tree-item {
|
||||
padding: 0.75rem 1rem;
|
||||
margin: 0.25rem;
|
||||
border-radius: var(--tg-radius);
|
||||
transition: all var(--tg-transition-fast);
|
||||
position: relative;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.task-tree-item::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: var(--text-muted);
|
||||
border-radius: var(--tg-radius-full);
|
||||
}
|
||||
|
||||
.task-tree-item:hover {
|
||||
background: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.task-tree-empty {
|
||||
text-align: center;
|
||||
padding: 3rem 1.5rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* ========== Filter Panel Styles ========== */
|
||||
.tg-v2-filter-panel {
|
||||
position: fixed;
|
||||
right: -380px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 380px;
|
||||
background: var(--background-primary);
|
||||
border-left: 1px solid var(--background-modifier-border);
|
||||
box-shadow: var(--tg-shadow-xl);
|
||||
transition: right var(--tg-transition-slow);
|
||||
z-index: 1000;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tg-v2-filter-panel.is-open {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.filter-panel-header {
|
||||
padding: 1.25rem;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.filter-panel-title {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.filter-panel-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--tg-radius);
|
||||
cursor: pointer;
|
||||
transition: background var(--tg-transition-fast);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.filter-panel-close:hover {
|
||||
background: var(--background-modifier-hover);
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.filter-panel-content {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.filter-group-label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 0.5rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
}
|
||||
|
||||
.filter-options {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.filter-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: var(--tg-radius);
|
||||
cursor: pointer;
|
||||
transition: background var(--tg-transition-fast);
|
||||
}
|
||||
|
||||
.filter-option:hover {
|
||||
background: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.filter-option.is-selected {
|
||||
background: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
}
|
||||
|
||||
.filter-checkbox {
|
||||
margin-right: 0.75rem;
|
||||
}
|
||||
|
||||
/* Filter specific styles */
|
||||
.filter-select {
|
||||
width: 100%;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--background-modifier-form-field);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--tg-radius);
|
||||
color: var(--text-normal);
|
||||
font-size: 0.875rem;
|
||||
transition: all var(--tg-transition-fast);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.filter-select:hover {
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.filter-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--interactive-accent);
|
||||
box-shadow: 0 0 0 2px var(--interactive-accent-hover);
|
||||
}
|
||||
|
||||
.filter-tags-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
.filter-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.375rem 0.75rem;
|
||||
background: var(--background-secondary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--tg-radius-full);
|
||||
font-size: 0.875rem;
|
||||
cursor: pointer;
|
||||
transition: all var(--tg-transition-fast);
|
||||
}
|
||||
|
||||
.filter-tag:hover {
|
||||
background: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.filter-tag.is-selected {
|
||||
background: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.filter-date-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.filter-date-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.filter-date-field span {
|
||||
min-width: 50px;
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.filter-date-input {
|
||||
flex: 1;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--background-modifier-form-field);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--tg-radius);
|
||||
color: var(--text-normal);
|
||||
font-size: 0.875rem;
|
||||
transition: all var(--tg-transition-fast);
|
||||
}
|
||||
|
||||
.filter-date-input:hover {
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.filter-date-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.filter-action-buttons {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
margin-top: 1.5rem;
|
||||
padding-top: 1.5rem;
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.filter-action-buttons button {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.priority-color-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: var(--tg-radius-full);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ========== Loading States ========== */
|
||||
.tg-v2-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.tg-v2-spinner {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 3px solid var(--background-modifier-border);
|
||||
border-top-color: var(--interactive-accent);
|
||||
border-radius: var(--tg-radius-full);
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.tg-v2-loading-text {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* ========== Empty States ========== */
|
||||
.tg-v2-empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tg-v2-empty-icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--text-muted);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.tg-v2-empty-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-normal);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.tg-v2-empty-description {
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 1.5rem;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.tg-v2-empty-action {
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
border: none;
|
||||
border-radius: var(--tg-radius-md);
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all var(--tg-transition-fast);
|
||||
}
|
||||
|
||||
.tg-v2-empty-action:hover {
|
||||
background: var(--interactive-accent-hover);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--tg-shadow);
|
||||
}
|
||||
|
||||
/* ========== Tooltips ========== */
|
||||
.tg-v2-tooltip {
|
||||
position: absolute;
|
||||
background: var(--background-secondary);
|
||||
color: var(--text-normal);
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: var(--tg-radius);
|
||||
font-size: 0.875rem;
|
||||
box-shadow: var(--tg-shadow-lg);
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transform: translateY(4px);
|
||||
transition: all var(--tg-transition-fast);
|
||||
}
|
||||
|
||||
.tg-v2-tooltip.is-visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* ========== Cards ========== */
|
||||
.tg-v2-card {
|
||||
background: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--tg-radius-lg);
|
||||
padding: 1.25rem;
|
||||
margin-bottom: 1rem;
|
||||
transition: all var(--tg-transition-base);
|
||||
}
|
||||
|
||||
.tg-v2-card:hover {
|
||||
box-shadow: var(--tg-shadow-md);
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.tg-v2-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.tg-v2-card-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.tg-v2-card-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.tg-v2-card-action {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: var(--tg-radius);
|
||||
cursor: pointer;
|
||||
transition: all var(--tg-transition-fast);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.tg-v2-card-action:hover {
|
||||
background: var(--background-modifier-hover);
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
/* ========== Buttons ========== */
|
||||
.tg-v2-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.625rem 1.25rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
border-radius: var(--tg-radius-md);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all var(--tg-transition-fast);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tg-v2-button::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-radius: var(--tg-radius-full);
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
transform: translate(-50%, -50%);
|
||||
transition:
|
||||
width var(--tg-transition-slow),
|
||||
height var(--tg-transition-slow);
|
||||
}
|
||||
|
||||
.tg-v2-button:active::before {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.tg-v2-button-primary {
|
||||
background: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
}
|
||||
|
||||
.tg-v2-button-primary:hover {
|
||||
background: var(--interactive-accent-hover);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--tg-shadow);
|
||||
}
|
||||
|
||||
.tg-v2-button-secondary {
|
||||
background: var(--background-secondary);
|
||||
color: var(--text-normal);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.tg-v2-button-secondary:hover {
|
||||
background: var(--background-modifier-hover);
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.tg-v2-button-ghost {
|
||||
background: transparent;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.tg-v2-button-ghost:hover {
|
||||
background: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.tg-v2-button-danger {
|
||||
background: var(--text-error);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.tg-v2-button-danger:hover {
|
||||
background: #dc2626;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--tg-shadow);
|
||||
}
|
||||
|
||||
/* ========== Animations ========== */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(4px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== Responsive Design ========== */
|
||||
@media (max-width: 1024px) {
|
||||
.task-list-container,
|
||||
.task-tree-container {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.tg-v2-filter-panel {
|
||||
width: 100%;
|
||||
right: -100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.tg-task-list-item {
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.task-tree-project-header {
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.tg-v2-card {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== Dark Mode Adjustments ========== */
|
||||
.theme-dark {
|
||||
--tg-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3);
|
||||
--tg-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-md:
|
||||
0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-lg:
|
||||
0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-xl:
|
||||
0 20px 25px -5px rgb(0 0 0 / 0.4), 0 8px 10px -6px rgb(0 0 0 / 0.3);
|
||||
}
|
||||
|
||||
.theme-dark .tg-v2-card:hover,
|
||||
.theme-dark .tg-task-list-item:hover,
|
||||
.theme-dark .task-tree-project:hover {
|
||||
box-shadow:
|
||||
0 0 0 1px var(--interactive-accent),
|
||||
var(--tg-shadow-md);
|
||||
}
|
||||
|
||||
/* ========== Progress Bars ========== */
|
||||
.tg-v2-progress {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
background: var(--background-modifier-border);
|
||||
border-radius: var(--tg-radius-full);
|
||||
overflow: hidden;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.tg-v2-progress-bar {
|
||||
height: 100%;
|
||||
background: var(--interactive-accent);
|
||||
border-radius: var(--tg-radius-full);
|
||||
transition: width var(--tg-transition-slow);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tg-v2-progress-bar::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.3),
|
||||
transparent
|
||||
);
|
||||
animation: shimmer 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== Tags ========== */
|
||||
.tg-v2-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.25rem 0.625rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
border-radius: var(--tg-radius-full);
|
||||
background: var(--background-secondary);
|
||||
color: var(--text-normal);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
margin-right: 0.25rem;
|
||||
transition: all var(--tg-transition-fast);
|
||||
}
|
||||
|
||||
.tg-v2-tag:hover {
|
||||
background: var(--background-modifier-hover);
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.tg-v2-tag-primary {
|
||||
background: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.tg-v2-tag-success {
|
||||
background: #10b981;
|
||||
color: white;
|
||||
border-color: #10b981;
|
||||
}
|
||||
|
||||
.tg-v2-tag-warning {
|
||||
background: #f59e0b;
|
||||
color: white;
|
||||
border-color: #f59e0b;
|
||||
}
|
||||
|
||||
.tg-v2-tag-danger {
|
||||
background: var(--text-error);
|
||||
color: white;
|
||||
border-color: var(--text-error);
|
||||
}
|
||||
|
||||
.workspace-name-with-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: var(--size-2-2);
|
||||
}
|
||||
24
src/index.ts
24
src/index.ts
|
|
@ -322,6 +322,30 @@ export default class TaskProgressBarPlugin extends Plugin {
|
|||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "open-task-genius-changelog",
|
||||
name: t("Open Task Genius changelog"),
|
||||
callback: () => {
|
||||
if (!this.changelogManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetVersion =
|
||||
this.manifest?.version ||
|
||||
this.settings.changelog.lastVersion;
|
||||
|
||||
if (!targetVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isBeta = targetVersion.toLowerCase().includes("beta");
|
||||
void this.changelogManager.openChangelog(
|
||||
targetVersion,
|
||||
isBeta,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
addIcon("task-genius", getTaskGeniusIcon());
|
||||
addIcon("completed", getStatusIcon("completed"));
|
||||
addIcon("inProgress", getStatusIcon("inProgress"));
|
||||
|
|
|
|||
|
|
@ -34,7 +34,11 @@ export class ChangelogManager {
|
|||
return;
|
||||
}
|
||||
|
||||
const cached = getCachedChangelog(version, isBeta);
|
||||
const cached = getCachedChangelog(
|
||||
version,
|
||||
isBeta,
|
||||
this.plugin.app,
|
||||
);
|
||||
if (cached) {
|
||||
this.currentVersionDisplayed = version;
|
||||
await view.setContent({
|
||||
|
|
@ -58,7 +62,7 @@ export class ChangelogManager {
|
|||
return;
|
||||
}
|
||||
|
||||
cacheChangelog(version, isBeta, data);
|
||||
cacheChangelog(version, isBeta, data, this.plugin.app);
|
||||
await view.setContent({
|
||||
version,
|
||||
markdown: data.markdown,
|
||||
|
|
|
|||
|
|
@ -357,7 +357,7 @@
|
|||
}
|
||||
|
||||
.fluent-project-list.is-tree-view
|
||||
.fluent-project-item[data-level]:not([data-level="0"]):hover::before {
|
||||
.fluent-project-item[data-level]:not([data-level="0"]):hover::before {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
|
|
@ -597,7 +597,10 @@
|
|||
|
||||
/* ===== Collapsed Sidebar (Rail) ===== */
|
||||
.tg-fluent-sidebar-container {
|
||||
transition: width 0.2s ease, min-width 0.2s ease, max-width 0.2s ease;
|
||||
transition:
|
||||
width 0.2s ease,
|
||||
min-width 0.2s ease,
|
||||
max-width 0.2s ease;
|
||||
}
|
||||
|
||||
/* When the sidebar element itself is collapsed */
|
||||
|
|
@ -628,7 +631,9 @@
|
|||
border-radius: var(--radius-s);
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s ease, color 0.15s ease;
|
||||
transition:
|
||||
background-color 0.15s ease,
|
||||
color 0.15s ease;
|
||||
}
|
||||
|
||||
.fluent-rail-btn:hover {
|
||||
|
|
@ -791,7 +796,9 @@
|
|||
border-radius: 4px;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s ease, color 0.15s ease;
|
||||
transition:
|
||||
background-color 0.15s ease,
|
||||
color 0.15s ease;
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
|
@ -815,8 +822,8 @@
|
|||
|
||||
/* Hide sidebar toggle on desktop when in mobile drawer mode */
|
||||
.tg-fluent-sidebar-container.is-mobile-drawer
|
||||
~ .tg-fluent-main-container
|
||||
.sidebar-toggle {
|
||||
~ .tg-fluent-main-container
|
||||
.sidebar-toggle {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
|
@ -875,6 +882,7 @@
|
|||
height: 400px;
|
||||
filter: blur(150px);
|
||||
animation: shadow-slide infinite 4s linear alternate;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes shadow-slide {
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
/* Shadows */
|
||||
--tg-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--tg-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1),
|
||||
0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1),
|
||||
0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1),
|
||||
0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-md:
|
||||
0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-lg:
|
||||
0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--tg-shadow-xl:
|
||||
0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
||||
|
||||
/* Animations */
|
||||
--tg-transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.task-list-item {
|
||||
.tg-task-list-item {
|
||||
background: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--tg-radius-md);
|
||||
|
|
@ -60,13 +60,13 @@
|
|||
transition: background var(--tg-transition-fast);
|
||||
}
|
||||
|
||||
.task-list-item:hover {
|
||||
.tg-task-list-item:hover {
|
||||
box-shadow: var(--tg-shadow);
|
||||
transform: translateY(-1px);
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.task-list-item:hover::before {
|
||||
.tg-task-list-item:hover::before {
|
||||
background: var(--interactive-accent);
|
||||
}
|
||||
|
||||
|
|
@ -564,7 +564,8 @@
|
|||
border-radius: var(--tg-radius-full);
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
transform: translate(-50%, -50%);
|
||||
transition: width var(--tg-transition-slow),
|
||||
transition:
|
||||
width var(--tg-transition-slow),
|
||||
height var(--tg-transition-slow);
|
||||
}
|
||||
|
||||
|
|
@ -669,7 +670,7 @@
|
|||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.task-list-item {
|
||||
.tg-task-list-item {
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
|
|
@ -686,18 +687,20 @@
|
|||
.theme-dark {
|
||||
--tg-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3);
|
||||
--tg-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.4),
|
||||
0 2px 4px -2px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.4),
|
||||
0 4px 6px -4px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.4),
|
||||
0 8px 10px -6px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-md:
|
||||
0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-lg:
|
||||
0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.3);
|
||||
--tg-shadow-xl:
|
||||
0 20px 25px -5px rgb(0 0 0 / 0.4), 0 8px 10px -6px rgb(0 0 0 / 0.3);
|
||||
}
|
||||
|
||||
.theme-dark .tg-fluent-card:hover,
|
||||
.theme-dark .task-list-item:hover,
|
||||
.theme-dark .tg-task-list-item:hover,
|
||||
.theme-dark .task-tree-project:hover {
|
||||
box-shadow: 0 0 0 1px var(--interactive-accent), var(--tg-shadow-md);
|
||||
box-shadow:
|
||||
0 0 0 1px var(--interactive-accent),
|
||||
var(--tg-shadow-md);
|
||||
}
|
||||
|
||||
/* ========== Progress Bars ========== */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
/* Noise Layer Effect */
|
||||
.tg-noise-layer {
|
||||
position: relative;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tg-noise-layer::before {
|
||||
|
|
|
|||
|
|
@ -29,22 +29,12 @@
|
|||
width: 200px;
|
||||
min-width: 200px;
|
||||
border-right: 1px solid var(--background-modifier-border);
|
||||
border-top: unset;
|
||||
}
|
||||
|
||||
.tg-fluent-container.component-preview-topnav {
|
||||
background: var(--background-primary);
|
||||
}
|
||||
|
||||
.component-showcase:has(
|
||||
.tg-fluent-container.component-preview-sidebar
|
||||
+ .tg-fluent-container.component-preview-topnav
|
||||
)
|
||||
.tg-fluent-container.component-preview-topnav {
|
||||
border-top: unset;
|
||||
background: var(--background-primary);
|
||||
}
|
||||
|
||||
/* Component showcase container */
|
||||
.component-showcase {
|
||||
display: flex;
|
||||
|
|
@ -77,7 +67,6 @@
|
|||
.component-showcase-description h3 {
|
||||
margin: 0;
|
||||
color: var(--text-normal);
|
||||
font-size: var(--font-ui-large);
|
||||
}
|
||||
|
||||
.component-showcase-description p {
|
||||
|
|
@ -297,6 +286,41 @@
|
|||
position: relative;
|
||||
outline: 1px solid var(--color-accent);
|
||||
outline-offset: -1px;
|
||||
sborder-radius: var(--radius-s);
|
||||
border-radius: var(--radius-s);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.component-showcase:has(.fluent-top-navigation):not(
|
||||
:has(.component-preview-sidebar)
|
||||
) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.component-showcase-preview.focus-mode.tg-fluent-container.component-preview-topnav {
|
||||
flex: unset;
|
||||
height: max-content;
|
||||
}
|
||||
|
||||
.fluent-top-navigation.component-preview.is-focused {
|
||||
outline: unset;
|
||||
}
|
||||
|
||||
.component-showcase-preview:has(
|
||||
.component-preview-sidebar + .component-preview-topnav
|
||||
)
|
||||
.component-preview-sidebar {
|
||||
border-top: unset;
|
||||
}
|
||||
|
||||
.component-showcase:has(
|
||||
.tg-fluent-container.component-preview-sidebar
|
||||
+ .tg-fluent-container.component-preview-topnav
|
||||
)
|
||||
.tg-fluent-container.component-preview-topnav {
|
||||
border-top: unset;
|
||||
background: var(--background-primary);
|
||||
}
|
||||
|
||||
.onboarding-content .resources-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@
|
|||
/* shadcn design tokens */
|
||||
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--shadow-md: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
--shadow-lg: 0 4px 6px -1px rgb(0 0 0 / 0.1),
|
||||
0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--shadow-xl: 0 10px 15px -3px rgb(0 0 0 / 0.1),
|
||||
0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--shadow-lg:
|
||||
0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--shadow-xl:
|
||||
0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
/* Modal specific styles */
|
||||
|
|
@ -49,12 +49,6 @@
|
|||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Header section */
|
||||
.onboarding-view .onboarding-header {
|
||||
padding: calc(var(--onboarding-spacing) * 4) var(--onboarding-spacing) var(--size-4-2) var(--onboarding-spacing);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Hide onboarding-header if no header content */
|
||||
.onboarding-view .onboarding-header:empty {
|
||||
display: none;
|
||||
|
|
@ -150,7 +144,9 @@
|
|||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
transform: translate(-50%, -50%);
|
||||
transition: width 0.6s, height 0.6s;
|
||||
transition:
|
||||
width 0.6s,
|
||||
height 0.6s;
|
||||
}
|
||||
|
||||
.onboarding-view .onboarding-buttons button:active::after {
|
||||
|
|
@ -264,8 +260,9 @@
|
|||
rgba(var(--interactive-accent-rgb), 0.05) 0%,
|
||||
var(--background-primary) 100%
|
||||
);
|
||||
box-shadow: var(--shadow-md),
|
||||
0 0 0 3px rgba(var(--interactive-accent-rgb), 0.1);
|
||||
box-shadow:
|
||||
var(--shadow-md),
|
||||
0 0 0 3px rgba(var(--interactive-accent-rgb), 0.1);
|
||||
}
|
||||
|
||||
.onboarding-view .settings-check-action-card:hover {
|
||||
|
|
@ -694,6 +691,11 @@
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.onboarding-view .change-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.onboarding-view .change-icon {
|
||||
color: var(--interactive-accent);
|
||||
font-size: 1.1em;
|
||||
|
|
@ -1466,8 +1468,13 @@
|
|||
/* Wrapper to prevent layout shift */
|
||||
.intro-typing-wrapper {
|
||||
width: 100%;
|
||||
max-width: max(900px, 70%);
|
||||
margin: 0 auto;
|
||||
padding-left: calc(var(--onboarding-spacing) * 3);
|
||||
padding-right: calc(var(--onboarding-spacing) * 3);
|
||||
}
|
||||
|
||||
.onboarding-view .onboarding-content.intro-typing-wrapper {
|
||||
padding-left: calc(var(--onboarding-spacing) * 3);
|
||||
padding-right: calc(var(--onboarding-spacing) * 3);
|
||||
}
|
||||
|
||||
.intro-typing {
|
||||
|
|
@ -1522,9 +1529,10 @@
|
|||
opacity: 0;
|
||||
filter: blur(4px);
|
||||
transform: translateY(-2px);
|
||||
transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
filter 0.4s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition:
|
||||
opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
filter 0.4s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
|
@ -1582,7 +1590,9 @@
|
|||
.intro-line-fadeout {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
transition: opacity 0.5s ease-out, transform 0.5s ease-out;
|
||||
transition:
|
||||
opacity 0.5s ease-out,
|
||||
transform 0.5s ease-out;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
|
@ -1686,9 +1696,10 @@
|
|||
}
|
||||
|
||||
.config-check-typing .check-line {
|
||||
margin-bottom: var(--size-4-4);
|
||||
line-height: 1.6;
|
||||
color: var(--text-normal);
|
||||
margin-block-start: var(--size-4-2);
|
||||
margin-block-end: var(--size-4-2);
|
||||
}
|
||||
|
||||
.config-check-typing .check-line-1 {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,9 @@
|
|||
.quick-capture-tab.active {
|
||||
background: hsl(var(--tg-tab-background));
|
||||
color: hsl(var(--tg-tab-foreground));
|
||||
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
box-shadow:
|
||||
0 1px 3px 0 rgb(0 0 0 / 0.1),
|
||||
0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
.quick-capture-tabs.is-hidden {
|
||||
|
|
@ -153,19 +155,12 @@
|
|||
.quick-capture-continue {
|
||||
padding: var(--size-2-2) var(--size-4-3);
|
||||
background: transparent;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--radius-s);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.quick-capture-continue:hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
color: var(--text-normal);
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
/* File Name Input Component */
|
||||
.file-name-input-container {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -358,6 +358,7 @@ const translations = {
|
|||
"Current stage": "Current stage",
|
||||
"Type": "Type",
|
||||
"Next": "Next",
|
||||
"Next to Introduction": "Next to Introduction",
|
||||
"Name and ID are required.": "Name and ID are required.",
|
||||
"End of file": "End of file",
|
||||
"Include in cycle": "Include in cycle",
|
||||
|
|
@ -679,6 +680,7 @@ const translations = {
|
|||
"No tasks with the selected tags": "No tasks with the selected tags",
|
||||
"Select a tag to see related tasks": "Select a tag to see related tasks",
|
||||
"Open Task Genius view": "Open Task Genius view",
|
||||
"Open Task Genius changelog": "Open Task Genius changelog",
|
||||
"Minimal Quick Capture": "Minimal Quick Capture",
|
||||
"Refresh task index": "Refresh task index",
|
||||
"Refreshing task index...": "Refreshing task index...",
|
||||
|
|
|
|||
|
|
@ -532,6 +532,7 @@ const translations = {
|
|||
"No tasks with the selected tags": "没有带有所选标签的任务",
|
||||
"Select a tag to see related tasks": "选择一个标签以查看相关任务",
|
||||
"Open Task Genius view": "打开 Task Genius 视图",
|
||||
"Open Task Genius changelog": "打开 Task Genius 更新日志",
|
||||
"Task capture with metadata": "带元数据的任务捕获",
|
||||
"Refresh task index": "刷新任务索引",
|
||||
"Refreshing task index...": "正在刷新任务索引...",
|
||||
|
|
|
|||
|
|
@ -465,6 +465,7 @@ const translations = {
|
|||
"No tasks with the selected tags": "沒有帶有所選標籤的任務",
|
||||
"Select a tag to see related tasks": "選擇一個標籤以查看相關任務",
|
||||
"Open Task Genius view": "打開 Task Genius 視圖",
|
||||
"Open Task Genius changelog": "打開 Task Genius 更新日誌",
|
||||
"Task capture with metadata": "帶元數據的任務捕獲",
|
||||
"Refresh task index": "刷新任務索引",
|
||||
"Refreshing task index...": "正在刷新任務索引...",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { App } from "obsidian";
|
||||
|
||||
const STORAGE_KEY = "task-genius-changelog-cache";
|
||||
|
||||
type CacheChannel = "stable" | "beta";
|
||||
|
|
@ -17,6 +19,41 @@ interface ChangelogCachePayload {
|
|||
const getChannelKey = (isBeta: boolean): CacheChannel =>
|
||||
isBeta ? "beta" : "stable";
|
||||
|
||||
const isChangelogCacheEntry = (
|
||||
value: unknown,
|
||||
): value is ChangelogCacheEntry => {
|
||||
if (!value || typeof value !== "object") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const entry = value as Partial<ChangelogCacheEntry>;
|
||||
return (
|
||||
typeof entry.version === "string" &&
|
||||
typeof entry.markdown === "string" &&
|
||||
typeof entry.sourceUrl === "string" &&
|
||||
typeof entry.updatedAt === "number"
|
||||
);
|
||||
};
|
||||
|
||||
const sanitizeCachePayload = (value: unknown): ChangelogCachePayload => {
|
||||
if (!value || typeof value !== "object") {
|
||||
return {};
|
||||
}
|
||||
|
||||
const payload = value as Partial<Record<CacheChannel, unknown>>;
|
||||
const sanitized: ChangelogCachePayload = {};
|
||||
|
||||
if (isChangelogCacheEntry(payload.stable)) {
|
||||
sanitized.stable = payload.stable;
|
||||
}
|
||||
|
||||
if (isChangelogCacheEntry(payload.beta)) {
|
||||
sanitized.beta = payload.beta;
|
||||
}
|
||||
|
||||
return sanitized;
|
||||
};
|
||||
|
||||
const getStorage = (): Storage | null => {
|
||||
try {
|
||||
if (typeof window === "undefined") {
|
||||
|
|
@ -29,7 +66,15 @@ const getStorage = (): Storage | null => {
|
|||
}
|
||||
};
|
||||
|
||||
const loadCache = (): ChangelogCachePayload => {
|
||||
const loadCache = (app?: App): ChangelogCachePayload => {
|
||||
try {
|
||||
if (typeof app?.loadLocalStorage === "function") {
|
||||
return sanitizeCachePayload(app.loadLocalStorage(STORAGE_KEY));
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("[ChangelogCache] Failed to load via app localStorage", error);
|
||||
}
|
||||
|
||||
const storage = getStorage();
|
||||
if (!storage) {
|
||||
return {};
|
||||
|
|
@ -41,36 +86,46 @@ const loadCache = (): ChangelogCachePayload => {
|
|||
return {};
|
||||
}
|
||||
|
||||
const parsed = JSON.parse(raw) as ChangelogCachePayload;
|
||||
if (!parsed || typeof parsed !== "object") {
|
||||
return {};
|
||||
}
|
||||
|
||||
return parsed;
|
||||
} catch {
|
||||
return sanitizeCachePayload(JSON.parse(raw));
|
||||
} catch (error) {
|
||||
console.warn("[ChangelogCache] Failed to load via window localStorage", error);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
const saveCache = (cache: ChangelogCachePayload): void => {
|
||||
const saveCache = (cache: ChangelogCachePayload, app?: App): void => {
|
||||
try {
|
||||
if (typeof app?.saveLocalStorage === "function") {
|
||||
app.saveLocalStorage(STORAGE_KEY, cache);
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("[ChangelogCache] Failed to save via app localStorage", error);
|
||||
}
|
||||
|
||||
const storage = getStorage();
|
||||
if (!storage) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const serialized = JSON.stringify(cache);
|
||||
storage.setItem(STORAGE_KEY, serialized);
|
||||
} catch {
|
||||
// Swallow errors silently; cache is an optimization only.
|
||||
if (!cache.stable && !cache.beta) {
|
||||
storage.removeItem(STORAGE_KEY);
|
||||
return;
|
||||
}
|
||||
|
||||
storage.setItem(STORAGE_KEY, JSON.stringify(cache));
|
||||
} catch (error) {
|
||||
console.warn("[ChangelogCache] Failed to save via window localStorage", error);
|
||||
}
|
||||
};
|
||||
|
||||
export const getCachedChangelog = (
|
||||
version: string,
|
||||
isBeta: boolean,
|
||||
app?: App,
|
||||
): ChangelogCacheEntry | null => {
|
||||
const cache = loadCache();
|
||||
const cache = loadCache(app);
|
||||
const channel = getChannelKey(isBeta);
|
||||
const entry = cache[channel];
|
||||
if (!entry || entry.version !== version) {
|
||||
|
|
@ -82,8 +137,9 @@ export const getCachedChangelog = (
|
|||
|
||||
export const getLatestCachedChangelog = (
|
||||
isBeta: boolean,
|
||||
app?: App,
|
||||
): ChangelogCacheEntry | null => {
|
||||
const cache = loadCache();
|
||||
const cache = loadCache(app);
|
||||
const channel = getChannelKey(isBeta);
|
||||
return cache[channel] ?? null;
|
||||
};
|
||||
|
|
@ -92,8 +148,9 @@ export const cacheChangelog = (
|
|||
version: string,
|
||||
isBeta: boolean,
|
||||
data: Pick<ChangelogCacheEntry, "markdown" | "sourceUrl">,
|
||||
app?: App,
|
||||
): void => {
|
||||
const cache = loadCache();
|
||||
const cache = loadCache(app);
|
||||
const channel = getChannelKey(isBeta);
|
||||
cache[channel] = {
|
||||
version,
|
||||
|
|
@ -101,6 +158,5 @@ export const cacheChangelog = (
|
|||
sourceUrl: data.sourceUrl,
|
||||
updatedAt: Date.now(),
|
||||
};
|
||||
saveCache(cache);
|
||||
saveCache(cache, app);
|
||||
};
|
||||
|
||||
|
|
|
|||
156
styles.css
156
styles.css
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue