mirror of
https://github.com/raven-pensieve/obsidian-ace-code-editor.git
synced 2026-07-22 05:48:56 +00:00
* feat(ui): 新增可配置标签页组件及样式
添加基于 radix-ui 的Nav 组件用于渲染可水平或垂直的
选项卡导航,并提供默认选中项、变更回调和自定义类名接口。
组件将传入的 tabs 列表映射为 Trigger 与 Content,垂直模式下
在列表侧支持可调整尺寸的占位条。
新增完整的基础样式文件,规范选项卡组、选项卡列表、触发
按钮与面板的布局和交互样式,包括:
- 横向/纵向布局切换与滚动优化(隐藏滚动条、iOS 平滑滚动)
- 按钮样式重置、悬停/聚焦/激活态样式与动画(激活下划线)
- 纵向模式下的左侧高亮条与面板布局适配
- 面板内容的入场动画和可滚动容器
目的:提供一个可复用、无侵入且可主题化的基础选项卡控件,
满足不同方向布局与可访问性需求,便于在项目中统一使用。
* feat(ui): 支持 Tab 禁用、修正样式前缀并重构设置页渲染
- 在 TabNav 中新增 Tab 接口的 disabled 字段,允许记选项为禁用。
- 使 TabNav 只对未禁用的选项渲染和作为默认激活项(filter enabledTabs),避免禁用项被选中。
- 统一组件 CSS 类名前缀由 "ace-..." 更改为 "ace--...",并同步更新多个子元素(TabGroup、TabList、Tab、
TabTitle、TabResizeBar、TabPanels、TabPanel)。
- 在 SettingsTab 中简化渲染逻辑,移除冗余容器和多余依赖,直接渲染 AceSettings 组件,减少嵌套 DOM。
- 在 AceSettings 中引入 TabNav 类型、parse 和将设置项按编辑器/渲染器分组的初步结构:
- 添加 RendererSettings 子组件(渲染器相关配置示例),包含主题、字体族与字号等设置项。
- 添加占位 EditorSettings 子组件,便于后续扩展编辑器相关配置。
- 清理不必要的 import(如 ObsidianUtils、t 的部分使用调整)并重构部分设置项实现以提高可维护性。
变更原因:
- 需要支持禁用的选项卡行为并防止其被默认激活。
- 统一样式前缀以修复命名不一致导致的样式问题。
- 精简设置页结构,降低 DOM 复杂度并为后续按模块拆分设置项做准备。
* feat: 增强编辑器设置与国际化文案支持
添加并暴露更多编辑器相关的设置(行号、边距、不可见字符缩进参考线、折叠部件、当前行高亮、选中文本高亮),并在设置界面
中加入对应的开关控件,方便用户在 UI 中直接切换这些行为。
更新 i18n 类型定义(types.ts)以包含新增设置字段,避免类型错误;
在英文与简体中文资源文件(locales/en.ts、locales/zh.ts)中增加相
应的文案及新增的 tabs 分组(renderer/session/editor/extend/about),
以支持设置面板的分组显示与多语言呈现。
修改 Settings 组件(AceSettings.tsx),在 Editor 与 Renderer 配置区
域渲染新增的 SettingsItem 和 Toggle 控件,连接到现有的配置更新
handleUpdateConfig,确保开关变更会保存到设置中。
目的:扩展编辑器功能可配置性,完善多语言支持及类型声明,提升
用户在设置中对编辑器行为的可控性与可发现性。
* refactor(settings): 移除多余配置
* feat(tab): 为选项卡面板添加独立滚动与位置记忆
- 在 TabNav 组件中引入对面板容器的 ref 与滚动位
置缓存(scrollContainerRef、scrollPositionRef),并新增
currentTab 状态以控制当前激活标签。
- 将 onValueChange 替换为 handleTabChange:切换标签时先
保存当前标签的滚动位置,更新 currentTab 并延迟恢复
目标标签的滚动位置,保证视图切换时位置一致。
- 增加 useLayoutEffect 与 onScroll 处理器(handleScroll),
在组件更新或滚动时实时恢复/保存滚动位置信息,改善
用户在标签间切换时的体验。
- 在相关 CSS(BaseTabNav / SettingsTab)中调整面板滚动样
式:使用 overflow-y、隐藏横向滚动、设置 max-height/
min-height,并在设置页容器中使用 flex 布局与内部滚动,
以确保面板在不同布局与设备下都有稳定且可用的滚动区
域。
* style: 精简设置面板样式并增强组件描述类型
- 移除 .ace-settings-container 的多余内边距和居中外边距,
使设置面板在上更紧凑并避免重复间距。
- 将 SettingsItem 的 desc 属性类型扩展为 string | React.ReactNode,
支持传入富文本或 React 节点以便渲染更复杂的描述内容。
- 在使用处直接以 parse(t("setting.desc")) 作为 desc 传入,
避免嵌套子节点冗余,代码更简洁且一致地通过 desc 渲染描述。
239 lines
5.2 KiB
CSS
239 lines
5.2 KiB
CSS
/* Base Tab Group Layout */
|
|
.ace--TabGroup {
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
max-height: inherit;
|
|
border-radius: var(--radius-m);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.ace--TabGroup[data-orientation="vertical"] {
|
|
flex-direction: row;
|
|
}
|
|
|
|
.ace--TabList {
|
|
display: flex;
|
|
background-color: transparent;
|
|
color: var(--text-normal);
|
|
border-bottom: 1px solid var(--background-modifier-border);
|
|
overflow-x: auto;
|
|
-webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */
|
|
scrollbar-width: none; /* Firefox */
|
|
min-height: 32px; /* Slightly taller for better touch targets */
|
|
}
|
|
|
|
.ace--TabList::-webkit-scrollbar {
|
|
display: none; /* Chrome, Safari, Edge */
|
|
}
|
|
|
|
.ace--TabList[data-orientation="vertical"] {
|
|
flex-direction: column;
|
|
min-width: 150px;
|
|
max-width: 220px;
|
|
width: 25%;
|
|
height: 100%;
|
|
border-bottom: none;
|
|
border-right: 1px solid var(--background-modifier-border);
|
|
position: relative;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
/* Tab Button Reset and Styling */
|
|
button.ace--Tab {
|
|
all: unset; /* Reset all styles */
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 32px;
|
|
min-height: 32px;
|
|
padding: 0 var(--size-4-2);
|
|
cursor: pointer;
|
|
position: relative;
|
|
color: var(--text-muted);
|
|
font-size: var(--font-ui-small);
|
|
transition: color 150ms ease, background-color 150ms ease;
|
|
user-select: none;
|
|
white-space: nowrap;
|
|
font-weight: var(--font-normal);
|
|
flex-shrink: 0;
|
|
box-shadow: none;
|
|
outline: none;
|
|
}
|
|
|
|
button.ace--Tab:focus-visible {
|
|
box-shadow: none;
|
|
outline: none;
|
|
}
|
|
|
|
/* Tab Title Text Styling */
|
|
.ace--Tab .ace--TabTitle {
|
|
line-height: 1.4;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
padding: 0 var(--size-4-1);
|
|
max-width: 180px;
|
|
}
|
|
|
|
/* Active Tab Styling */
|
|
.ace--Tab[data-state="active"] {
|
|
color: var(--text-normal);
|
|
background-color: var(--background-primary);
|
|
font-weight: var(--font-medium);
|
|
position: relative;
|
|
}
|
|
|
|
.ace--Tab[data-state="active"]::after {
|
|
content: "";
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 2px;
|
|
background-color: var(--text-normal);
|
|
}
|
|
|
|
/* Tab Hover States */
|
|
.ace--Tab:hover {
|
|
color: var(--text-normal);
|
|
background-color: var(--background-modifier-hover);
|
|
}
|
|
|
|
.ace--Tab:focus-visible {
|
|
box-shadow: inset 0 0 0 2px var(--background-modifier-border-focus);
|
|
outline: none;
|
|
}
|
|
|
|
/* Active Tab in Vertical Orientation */
|
|
.ace--TabList[data-orientation="vertical"] .ace--Tab {
|
|
justify-content: flex-start;
|
|
padding: var(--size-4-1) var(--size-4-3);
|
|
height: auto;
|
|
min-height: 36px;
|
|
border-left: 3px solid transparent;
|
|
}
|
|
|
|
.ace--TabList[data-orientation="vertical"] .ace--Tab[data-state="active"] {
|
|
border-left: 3px solid var(--interactive-accent);
|
|
}
|
|
|
|
.ace--TabList[data-orientation="vertical"]
|
|
.ace--Tab[data-state="active"]::after {
|
|
display: none;
|
|
}
|
|
|
|
/* Tab Content Panel Container */
|
|
.ace--TabPanels {
|
|
flex: 1;
|
|
padding: var(--size-4-3);
|
|
border-radius: 0 0 var(--radius-m) var(--radius-m);
|
|
background-color: var(--background-primary);
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
width: 100%;
|
|
/* 确保有固定的最大高度,以便内部滚动生效 */
|
|
max-height: calc(100vh - 200px);
|
|
min-height: 400px;
|
|
}
|
|
|
|
.ace--TabGroup[data-orientation="vertical"] .ace--TabPanels {
|
|
margin-top: 0;
|
|
border-radius: 0 var(--radius-m) var(--radius-m) 0;
|
|
height: 100%;
|
|
/* 对于垂直布局,使用更精确的高度控制 */
|
|
max-height: calc(100vh - 150px);
|
|
}
|
|
|
|
/* Individual Tab Panel */
|
|
.ace--TabPanel {
|
|
outline: none;
|
|
animation: fadeIn 200ms ease;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0.8;
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/* Resize Bar for Vertical Tabs */
|
|
.ace--TabList[data-orientation="vertical"] .ace--TabResizeBar {
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
width: 4px;
|
|
height: 100%;
|
|
opacity: 0;
|
|
background-color: var(--interactive-accent);
|
|
cursor: ew-resize;
|
|
transition: opacity 150ms ease;
|
|
z-index: 10;
|
|
}
|
|
|
|
.ace--TabList[data-orientation="vertical"] .ace--TabResizeBar:hover,
|
|
.ace--TabList[data-orientation="vertical"] .ace--TabResizeBar:active {
|
|
opacity: 0.7;
|
|
}
|
|
|
|
/* Mobile Responsiveness */
|
|
@media (max-width: 768px) {
|
|
.ace--TabGroup[data-orientation="vertical"] {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.ace--TabList[data-orientation="vertical"] {
|
|
flex-direction: row;
|
|
min-width: 100%;
|
|
max-width: 100%;
|
|
width: 100%;
|
|
height: auto;
|
|
border-right: none;
|
|
border-bottom: 1px solid var(--background-modifier-border);
|
|
overflow-x: auto;
|
|
overflow-y: hidden;
|
|
}
|
|
|
|
.ace--TabList[data-orientation="vertical"] .ace--Tab {
|
|
border-left: none;
|
|
border-bottom: 3px solid transparent;
|
|
padding: var(--size-4-2) var(--size-4-3);
|
|
}
|
|
|
|
.ace--TabList[data-orientation="vertical"] .ace--Tab[data-state="active"] {
|
|
border-left: none;
|
|
border-bottom: 3px solid var(--interactive-accent);
|
|
}
|
|
|
|
.ace--TabList[data-orientation="vertical"] .ace--TabResizeBar {
|
|
display: none;
|
|
}
|
|
|
|
.ace--TabGroup[data-orientation="vertical"] .ace--TabPanels {
|
|
border-radius: 0 0 var(--radius-m) var(--radius-m);
|
|
/* 移动端也要保持内部滚动 */
|
|
max-height: calc(100vh - 200px);
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
.ace--Tab .ace--TabTitle {
|
|
max-width: 140px;
|
|
}
|
|
}
|
|
|
|
/* Touch device enhancements */
|
|
@media (pointer: coarse) {
|
|
.ace--Tab {
|
|
padding: var(--size-4-2) var(--size-4-3);
|
|
min-height: 44px; /* Better touch target */
|
|
}
|
|
|
|
.ace--TabList {
|
|
min-height: 44px;
|
|
}
|
|
}
|