feat: 新增新手指引弹窗和更新日志弹窗

This commit is contained in:
Ted 2026-04-11 01:20:07 +08:00
parent 0bb3b4715f
commit 3c2502ae41
15 changed files with 336 additions and 23 deletions

View file

@ -53,6 +53,9 @@ const jsContext = await esbuild.context({
treeShaking: true,
outfile: "dist/main.js",
minify: prod,
loader: {
".md": "text",
},
plugins: [copyPlugin],
});

View file

@ -4,10 +4,7 @@ import { DefaultRandomReviewFilter, RandomReviewFilter } from "./models/RandomRe
import { SortType } from "./models/Enum";
import { getToday } from "./utils/utils";
export const CUR_APP_DATA_VERSION = 1;
export interface BanyanAppData {
version: number;
// in app
sortType: SortType;
firstUseDate: string;
@ -24,7 +21,6 @@ export interface BanyanAppData {
export const DEFAULT_APP_DATA: BanyanAppData = {
version: CUR_APP_DATA_VERSION,
// in app
sortType: 'created',
firstUseDate: getToday(),

View file

@ -1,11 +1,13 @@
import { Plugin, WorkspaceLeaf } from 'obsidian';
import { BanyanPluginSettings, DEFAULT_SETTINGS, CUR_SETTINGS_VERSION } from './BanyanPluginSettings';
import { BanyanAppData, DEFAULT_APP_DATA, CUR_APP_DATA_VERSION } from './BanyanAppData';
import { BanyanAppData, DEFAULT_APP_DATA } from './BanyanAppData';
import { CARD_DASHBOARD_VIEW_TYPE, CardDashboard } from './pages/CardDashboard';
import { BanyanSettingTab } from './BanyanSettingTab';
import { FileUtils } from './utils/fileUtils';
import { i18n } from './utils/i18n';
import { TagFilter, isEmptyTagFilter } from './models/TagFilter';
import { AnnouncementModal } from './components/AnnouncementModal';
import { getWelcomeMarkdown, getChangelogMarkdown } from './utils/announcements';
export default class BanyanPlugin extends Plugin {
@ -82,6 +84,44 @@ export default class BanyanPlugin extends Plugin {
});
}
checkAnnouncements = async () => {
const curVersion = this.manifest.version;
const lastVersion = this.settings.lastVersion;
const settingVersion = this.settings.settingsVersion;
if ((!lastVersion || lastVersion === '') && settingVersion === CUR_SETTINGS_VERSION) {
// 初次启动
const modal = new AnnouncementModal(this.app, {
type: 'welcome',
content: getWelcomeMarkdown(),
onClose: () => {
this.settings.lastVersion = curVersion;
this.saveSettings();
}
});
modal.open();
} else if (lastVersion !== curVersion) {
// 版本更新
const updateContent = getChangelogMarkdown();
if (updateContent && updateContent.trim().length > 0) {
const modal = new AnnouncementModal(this.app, {
type: 'update',
content: updateContent,
onClose: () => {
this.settings.lastVersion = curVersion;
this.saveSettings();
}
});
modal.open();
} else {
// 如果当前版本没有更新公告,也更新版本号,避免重复检查
this.settings.lastVersion = curVersion;
await this.saveSettings();
}
}
}
onunload() {
// do nothing
}
@ -120,7 +160,7 @@ export default class BanyanPlugin extends Plugin {
setupRandomReview = () => {
if (!this.settings.enableRandomReview) return;
const icons = ['dice', 'shuffle', 'dices', 'dice-6',
'dice-5', 'dice-4', 'dice-3', 'dice-2', 'dice-1'];
this.randomRibbonIcons = [];
@ -143,7 +183,7 @@ export default class BanyanPlugin extends Plugin {
});
}
updateSettingIfNeeded = async () => {
updateSettingIfNeeded = () => {
// *** 版本更新时,在以下添加更新逻辑 ***
if (this.settings.settingsVersion < 2) {
const getNewFilterIfNeeded = (tf: TagFilter) => {
@ -199,7 +239,7 @@ export default class BanyanPlugin extends Plugin {
}
if (this.settings.settingsVersion < 9) {
const usedViewSchemes = this.appData.viewSchemes && this.appData.viewSchemes.length > 0;
let usedRandomReview = false;
if (this.appData.randomReviewFilters && this.appData.randomReviewFilters.length > 0) {
if (this.appData.randomReviewFilters.length > 1) {
@ -212,8 +252,8 @@ export default class BanyanPlugin extends Plugin {
this.settings.enableViewSchemes = usedViewSchemes;
this.settings.enableRandomReview = usedRandomReview;
}
this.checkAnnouncements();
this.settings.settingsVersion = CUR_SETTINGS_VERSION;
this.appData.version = CUR_APP_DATA_VERSION;
this.updateSavedFile();
this.saveSettings();
this.saveAppData();
@ -276,7 +316,6 @@ export default class BanyanPlugin extends Plugin {
if (migrated) {
console.log('Detected legacy settings, migrated to appData');
this.appData.version = CUR_APP_DATA_VERSION;
await this.saveAppData();
await this.saveSettings();
}

View file

@ -24,6 +24,8 @@ export interface BanyanPluginSettings {
enableViewSchemes?: boolean;
enableRandomReview?: boolean;
// meta
lastVersion?: string;
}
export const CUR_SETTINGS_VERSION = 9;
@ -52,4 +54,5 @@ export const DEFAULT_SETTINGS: BanyanPluginSettings = {
enableViewSchemes: false,
enableRandomReview: false,
lastVersion: '',
}

View file

@ -6,6 +6,8 @@ import { useCombineStore } from './store';
import { CardContentMaxHeightType, FontTheme, NewNoteLocationMode } from './models/Enum';
import { openMigrateTitleModal } from './components/MigrateTitleModal';
import { openRemoveIdModal } from './components/RemoveIdModal';
import { AnnouncementModal } from './components/AnnouncementModal';
import { getWelcomeMarkdown, getChangelogMarkdown } from './utils/announcements';
export class BanyanSettingTab extends PluginSettingTab {
plugin: BanyanPlugin;
@ -23,8 +25,6 @@ export class BanyanSettingTab extends PluginSettingTab {
this.setupCardsDirectorySetting(containerEl);
this.setupOpenWhenStartObsidianSetting(containerEl);
this.setupCardsColumnsSetting(containerEl);
this.setupEnableViewSchemesSetting(containerEl);
this.setupEnableRandomReviewSetting(containerEl);
// 卡片视图
new Setting(containerEl).setName(i18n.t('setting_header_cards')).setHeading();
@ -40,11 +40,18 @@ export class BanyanSettingTab extends PluginSettingTab {
this.setupUseZkPrefixerFormatSetting(containerEl);
this.setupShowAddNoteRibbonSetting(containerEl);
// 帮助
new Setting(containerEl).setName(i18n.t('setting_header_help')).setHeading();
this.setupHelpSetting(containerEl);
// 旧版清理
new Setting(containerEl).setName(i18n.t('setting_header_clean')).setHeading();
this.setupEnableViewSchemesSetting(containerEl);
this.setupEnableRandomReviewSetting(containerEl);
this.setupMigrateTitleToFilenameSetting(containerEl);
}
setupCardsDirectorySetting(containerEl: HTMLElement) {
const dateDesc = document.createDocumentFragment();
dateDesc.appendText(i18n.t('setting_note_directory_desc1'));
@ -288,4 +295,33 @@ export class BanyanSettingTab extends PluginSettingTab {
});
}
setupHelpSetting(containerEl: HTMLElement) {
new Setting(containerEl)
.setName(i18n.t('setting_show_welcome_name'))
.setDesc(i18n.t('setting_show_welcome_desc'))
.addButton(btn => {
btn.setButtonText(i18n.t('setting_show_welcome_btn'))
.onClick(async () => {
const modal = new AnnouncementModal(this.app, {
type: 'welcome',
content: getWelcomeMarkdown()
});
modal.open();
});
});
new Setting(containerEl)
.setName(i18n.t('setting_show_update_name'))
.setDesc(i18n.t('setting_show_update_desc'))
.addButton(btn => {
btn.setButtonText(i18n.t('setting_show_update_btn'))
.onClick(async () => {
const modal = new AnnouncementModal(this.app, {
type: 'update',
content: getChangelogMarkdown()
});
modal.open();
});
});
}
}

View file

@ -1,4 +1,14 @@
### Changelog
# Changelog
### 1.4.3
Improvements
- Now we have guidance pop-up and changelog pop-up, which can also be viewed in the settings panel.
- Now we add switches for "Random Review" and "View Scheme" in the setting. If you have been using these features, all remains the same; otherwise, they are disabled by default. These two features may be considered for removal in later version.
- We optimized the refresh logic of the card panel. Now Adding or editing notes will no longer cause a full refresh of the card panel.
- Fixed the extra blank block appeared in the search box on mobile.
- Optimized some styles.
### 1.4.2

View file

@ -1,4 +1,17 @@
### 更新日志
# 更新日志
### 1.4.3
新增
- 指引弹窗和版本更新日志弹窗,并且可以在设置面板中主动查看。
- 「随机回顾」和「视图空间」的设置开关如果你使用过该功能开关是打开的使用照旧否则默认是关闭的日后考虑下掉这2个功能。
修复和优化
- 优化了卡片面板的刷新逻辑,现在新增或修改笔记不会再导致卡片面板全量刷新了。
- 修复移动端搜索框出现额外空白块的问题。
- 对一些样式进行了优化。
### 1.4.2

View file

@ -0,0 +1,25 @@
# 🌲 Welcome to Banyan
Banyan allows you to visualize your notes through a card view and retrieve them using a tag-based filtering system, rather than a traditional tree directory.
### 🚀 Getting Started
This plugin only manages notes within the `Notes Directory` (defaulted to `cards`).
The storage structure of notes and subdirectories within this folder is no longer important.
The `banyan_config` under the "Notes Directory" stores plugin configuration files. It cannot be hidden for now, as setting it to a hidden directory will cause the MD files within it to fail to load.
The Card Panel displays notes chronologically and allows you to view specific notes through filtering schemes.
Filters include tags, dates, and keywords. Tags can be combined using **"OR, AND, NOT"** logic to create powerful filtering combinations.
Click the **Card Icon** 🗂️ in the functional area to open the Card Panel.
Click the **Lightbulb Icon** 💡 in the functional area to create a new note.
The current time is used as the default title, but you can modify it as you wish.
New notes are stored by default in a `Year/Quarter/Month/Day` hierarchy—though the hierarchy is secondary, you can customize the storage directory in the settings.
*Please feel free to explore more. Happy learning in your forest of knowledge!*
### 📮 Contact Me
github issue: [obsidian-banyan - ratiger | Github](https://github.com/ratiger/obsidian-banyan)
email: 2084939306@qq.com
rednote: ratiger (rednote number: 1392996322)

View file

@ -0,0 +1,26 @@
# 🌲 欢迎使用 Banyan
Banyan 通过卡片视图直观查看笔记内容,并通过以标签为主的筛选方案,而非传统的树形目录来检索笔记。
### 🚀 基本使用
本插件只管理「笔记目录」(默认为 `cards`)下的笔记;
该目录下的笔记与子目录的存放结构不再紧要。
「笔记目录」的根目录下的「banyan_config」目录用于存储应用配置文件暂时无法隐藏因为设置为隐藏目录会导致其内的md文件读取失败。
卡片面板以时间顺序展示笔记,并通过筛选方案展示指定笔记;
筛选包括标签、日期、关键字,其中标签通过「或且非」组合出强大的筛选方案;
点击功能区的卡片图标 🗂️ 打开卡片面板。
点击功能区的灯泡图标 💡 新建笔记;
默认以当时时间作为新建笔记的标题,但你可以任意修改它;
新建笔记将默认按 `年/季度/月/日` 的层级存放,但层级其实无关紧要,不过你也可以在设置中自定义存放目录;
*尽请探索更多内容,祝您在的知识森林中学习愉快!*
### 📮 与我联系
github issue: [obsidian-banyan - ratiger | Github](https://github.com/ratiger/obsidian-banyan)
邮箱: 2084939306@qq.com
小红书: ratiger (小红书号: 1392996322)

View file

@ -0,0 +1,63 @@
import { App, Modal, MarkdownRenderer, Component } from "obsidian";
import * as React from "react";
import { createRoot, Root } from "react-dom/client";
import { i18n } from "src/utils/i18n";
interface AnnouncementModalProps {
type: 'welcome' | 'update';
content: string;
onClose?: () => void;
}
export class AnnouncementModal extends Modal {
props: AnnouncementModalProps;
root: Root | null = null;
constructor(app: App, props: AnnouncementModalProps) {
super(app);
this.props = props;
}
onOpen() {
this.modalEl.addClass('announcement-modal');
this.root = createRoot(this.modalEl);
this.root.render(
<React.StrictMode>
<AnnouncementContainer app={this.app} props={this.props} close={() => { this.close() }} />
</React.StrictMode>
);
(this as any).shouldRestoreSelection = false;
}
onClose() {
this.root?.unmount();
this.props.onClose?.();
}
}
const AnnouncementContainer = ({ app, props, close }: {
app: App, props: AnnouncementModalProps, close: () => void
}) => {
const { type, content } = props;
const buttonText = type === 'welcome' ? i18n.t('announcement_get_started') : i18n.t('announcement_close');
const contentRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (contentRef.current) {
contentRef.current.empty();
const component = new Component();
component.load();
MarkdownRenderer.render(app, content, contentRef.current, '', component);
}
}, [content, app]);
return (
<div className="announcement-container">
<div className="announcement-content markdown-rendered" ref={contentRef}>
</div>
<div className="announcement-footer">
<button className="announcement-btn mod-cta" onClick={close}>{buttonText}</button>
</div>
</div>
);
};

View file

@ -246,4 +246,52 @@
font-size: 32px;
cursor: pointer;
transition: box-shadow 0.2s, background 0.2s;
}
/* 公告弹窗 */
.announcement-modal {
max-width: 500px;
}
.announcement-container {
padding: 24px;
display: flex;
flex-direction: column;
gap: 20px;
}
.announcement-content {
line-height: 1.6;
max-height: 65vh;
overflow-y: auto;
padding-right: 12px;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */
}
.announcement-content::-webkit-scrollbar {
display: none; /* Chrome, Safari and Opera */
}
.announcement-content h1:first-child {
margin-top: 0;
text-align: center;
color: var(--text-accent);
}
.announcement-content h3 {
margin-top: 1.5em;
border-bottom: 1px solid var(--background-modifier-border);
padding-bottom: 0.3em;
}
.announcement-footer {
display: flex;
justify-content: center;
}
.announcement-btn {
width: 100%;
padding: 10px !important;
font-weight: bold !important;
}

4
src/declarations.d.ts vendored Normal file
View file

@ -0,0 +1,4 @@
declare module "*.md" {
const content: string;
export default content;
}

View file

@ -0,0 +1,19 @@
import { i18n } from "./i18n";
// Import markdown files as strings
import welcome_en from "../announcements/welcome_en.md";
import welcome_zh from "../announcements/welcome_zh.md";
import changelog_en from "../announcements/changelog_en.md";
import changelog_zh from "../announcements/changelog_zh.md";
export const getWelcomeMarkdown = (): string => {
const lang = i18n.getCurrentLanguage();
if (lang === 'zh') return welcome_zh;
return welcome_en;
};
export const getChangelogMarkdown = (): string => {
const lang = i18n.getCurrentLanguage();
if (lang === 'zh') return changelog_zh;
return changelog_en;
};

View file

@ -108,10 +108,10 @@ export default {
setting_card_content_max_height_expand: "Expand",
setting_enable_view_schemes_name: "Enable \"View schemes\"",
setting_enable_view_schemes_desc: "Whether to display and enable the \"View schemes\" feature in the sidebar.",
setting_enable_view_schemes_desc: "Most of the use cases of this feature are replaced by \"Filter schemes\". So we are considering removing it.",
setting_enable_random_review_name: "Enable \"Random review\"",
setting_enable_random_review_desc: "Whether to display and enable the \"Random review\" feature in the sidebar.",
setting_enable_random_review_desc: "Most of the use cases of this feature are replaced by \"Random browse\". So we are considering removing it.",
// font
setting_font_theme_name: "Font size",
@ -234,4 +234,18 @@ export default {
loading_text: "Loading...",
reached_bottom: "You have reached the bottom.~",
/* Announcement Modal */
announcement_welcome_title: "Welcome to Banyan",
announcement_update_title: "What's New in Banyan",
announcement_get_started: "Get Started",
announcement_close: "Close",
/* Settings - Help */
setting_header_help: "❓ Help",
setting_show_welcome_name: "Tutorial",
setting_show_welcome_desc: "About Banyan usage.",
setting_show_welcome_btn: "View",
setting_show_update_name: "Changelog",
setting_show_update_desc: "All versions changelog.",
setting_show_update_btn: "View",
};

View file

@ -62,7 +62,7 @@ export default {
setting_header_basic: "⚙️ 基础设置",
setting_header_cards: "🗂 卡片视图",
setting_header_editor: "✏️ 新建笔记",
setting_header_clean: "⚠️ 旧版数据清理",
setting_header_clean: "⚠️ 旧版清理",
setting_note_directory_name: "笔记目录",
setting_note_directory_desc1: "「卡片面板」只会管理该目录下的所有笔记。",
@ -109,11 +109,11 @@ export default {
setting_card_content_max_height_normal: "一般",
setting_card_content_max_height_expand: "不折叠",
setting_enable_view_schemes_name: "启用「视图空间」",
setting_enable_view_schemes_desc: "是否在侧边栏显示并在后台启用「视图空间」功能。",
setting_enable_random_review_name: "启用「随机回顾」",
setting_enable_random_review_desc: "是否在侧边栏显示并在后台启用「随机回顾」功能。",
setting_enable_view_schemes_name: "启用「视图空间」功能",
setting_enable_view_schemes_desc: "该功能的使用场景更多被「筛选方案」替代了,故正考虑下掉该功能。",
setting_enable_random_review_name: "启用「随机回顾」功能",
setting_enable_random_review_desc: "该功能的使用场景更多被「乱序浏览」替代了,故正考虑下掉该功能。",
// 字体
setting_font_theme_name: "内容区域字体大小",
@ -236,4 +236,18 @@ export default {
loading_text: "加载中...",
reached_bottom: "你已经到底部了",
/* Announcement Modal */
announcement_welcome_title: "欢迎使用 Banyan",
announcement_update_title: "Banyan 更新日志",
announcement_get_started: "开始使用",
announcement_close: "关闭",
/* 设置 - 帮助 */
setting_header_help: "❓ 帮助",
setting_show_welcome_name: "新手指引",
setting_show_welcome_desc: "关于 Banyan 的使用说明。",
setting_show_welcome_btn: "查看",
setting_show_update_name: "更新日志",
setting_show_update_desc: "所有版本的更新内容。",
setting_show_update_btn: "查看",
};