mirror of
https://github.com/raven-pensieve/obsidian-ace-code-editor.git
synced 2026-07-22 05:48:56 +00:00
feat: 增加配置项,优化设置界面布局 (#28)
* 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 渲染描述。
This commit is contained in:
parent
68dd5fdfc6
commit
a5c58f9eec
15 changed files with 2571 additions and 214 deletions
1769
package-lock.json
generated
1769
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -48,6 +48,7 @@
|
|||
"dotenv": "^16.5.0",
|
||||
"html-react-parser": "^5.2.5",
|
||||
"lucide-react": "^0.523.0",
|
||||
"radix-ui": "^1.4.2",
|
||||
"react": "^19.1.0",
|
||||
"react-ace": "^14.0.1",
|
||||
"react-dom": "^19.1.0"
|
||||
|
|
|
|||
118
src/components/Base/TabNav.tsx
Normal file
118
src/components/Base/TabNav.tsx
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
import { Tabs } from "radix-ui";
|
||||
import * as React from "react";
|
||||
|
||||
export interface TabNavItem {
|
||||
id: string;
|
||||
title: string;
|
||||
content: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
interface TabNavProps {
|
||||
tabs: TabNavItem[];
|
||||
defaultValue?: string;
|
||||
orientation?: "horizontal" | "vertical";
|
||||
onChange?: (value: string) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const TabNav: React.FC<TabNavProps> = ({
|
||||
tabs,
|
||||
defaultValue,
|
||||
orientation = "horizontal",
|
||||
onChange,
|
||||
className,
|
||||
}) => {
|
||||
const enabledTabs = tabs.filter((tab) => !tab.disabled);
|
||||
const defaultTab = defaultValue || enabledTabs[0]?.id;
|
||||
const scrollContainerRef = React.useRef<HTMLDivElement>(null);
|
||||
const scrollPositionRef = React.useRef<{ [key: string]: number }>({});
|
||||
const [currentTab, setCurrentTab] = React.useState(defaultTab);
|
||||
|
||||
// 监听标签切换,保存当前滚动位置
|
||||
const handleTabChange = React.useCallback(
|
||||
(value: string) => {
|
||||
// 保存当前标签的滚动位置
|
||||
if (scrollContainerRef.current && currentTab) {
|
||||
scrollPositionRef.current[currentTab] =
|
||||
scrollContainerRef.current.scrollTop;
|
||||
}
|
||||
|
||||
setCurrentTab(value);
|
||||
onChange?.(value);
|
||||
|
||||
// 延迟恢复新标签的滚动位置
|
||||
setTimeout(() => {
|
||||
if (
|
||||
scrollContainerRef.current &&
|
||||
scrollPositionRef.current[value]
|
||||
) {
|
||||
scrollContainerRef.current.scrollTop =
|
||||
scrollPositionRef.current[value];
|
||||
}
|
||||
}, 0);
|
||||
},
|
||||
[onChange, currentTab]
|
||||
);
|
||||
|
||||
// 在组件更新后保持滚动位置
|
||||
React.useLayoutEffect(() => {
|
||||
if (
|
||||
scrollContainerRef.current &&
|
||||
currentTab &&
|
||||
scrollPositionRef.current[currentTab]
|
||||
) {
|
||||
scrollContainerRef.current.scrollTop =
|
||||
scrollPositionRef.current[currentTab];
|
||||
}
|
||||
});
|
||||
|
||||
// 监听滚动事件,实时保存滚动位置
|
||||
const handleScroll = React.useCallback(() => {
|
||||
if (scrollContainerRef.current && currentTab) {
|
||||
scrollPositionRef.current[currentTab] =
|
||||
scrollContainerRef.current.scrollTop;
|
||||
}
|
||||
}, [currentTab]);
|
||||
|
||||
return (
|
||||
<Tabs.Root
|
||||
className={`ace--TabGroup ${className}`}
|
||||
defaultValue={defaultTab}
|
||||
value={currentTab}
|
||||
data-orientation={orientation}
|
||||
onValueChange={handleTabChange}
|
||||
>
|
||||
<Tabs.List className="ace--TabList" data-orientation={orientation}>
|
||||
{orientation === "vertical" && (
|
||||
<div className="ace--TabResizeBar"></div>
|
||||
)}
|
||||
{enabledTabs.map((tab) => (
|
||||
<Tabs.Trigger
|
||||
className="ace--Tab"
|
||||
key={tab.id}
|
||||
value={tab.id}
|
||||
>
|
||||
<span className="ace--TabTitle">{tab.title}</span>
|
||||
</Tabs.Trigger>
|
||||
))}
|
||||
</Tabs.List>
|
||||
|
||||
<div
|
||||
className="ace--TabPanels"
|
||||
ref={scrollContainerRef}
|
||||
onScroll={handleScroll}
|
||||
>
|
||||
{enabledTabs.map((tab) => (
|
||||
<Tabs.Content
|
||||
className="ace--TabPanel"
|
||||
key={tab.id}
|
||||
value={tab.id}
|
||||
>
|
||||
{tab.content}
|
||||
</Tabs.Content>
|
||||
))}
|
||||
</div>
|
||||
</Tabs.Root>
|
||||
);
|
||||
};
|
||||
|
|
@ -8,11 +8,13 @@ import {
|
|||
} from "@/src/core/services/AceThemes";
|
||||
import { t } from "@/src/i18n/i18n";
|
||||
import AceCodeEditorPlugin from "@/src/main";
|
||||
import parse from "html-react-parser";
|
||||
import { Notice, Platform } from "obsidian";
|
||||
import * as React from "react";
|
||||
import { IconPicker } from "../Base/IconPicker";
|
||||
import { Input } from "../Base/Input";
|
||||
import { Select } from "../Base/Select";
|
||||
import { TabNav, TabNavItem } from "../Base/TabNav";
|
||||
import { TagInput } from "../Base/TagInput";
|
||||
import { Toggle } from "../Base/Toggle";
|
||||
import { SettingsItem } from "./SettingItem";
|
||||
|
|
@ -245,155 +247,294 @@ export const AceSettings: React.FC<AceSettingsProps> = ({ plugin }) => {
|
|||
label: keyboard,
|
||||
}));
|
||||
|
||||
const EditorSettings = () => {
|
||||
return (
|
||||
<>
|
||||
<SettingsItem
|
||||
name={t("setting.highlightActiveLine.name")}
|
||||
desc={t("setting.highlightActiveLine.desc")}
|
||||
>
|
||||
<Toggle
|
||||
checked={settings.highlightActiveLine}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ highlightActiveLine: value })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.highlightSelectedWord.name")}
|
||||
desc={t("setting.highlightSelectedWord.desc")}
|
||||
>
|
||||
<Toggle
|
||||
checked={settings.highlightSelectedWord}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ highlightSelectedWord: value })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const RendererSettings = () => {
|
||||
return (
|
||||
<>
|
||||
<SettingsItem
|
||||
name={t("setting.lightTheme.name")}
|
||||
desc={t("setting.lightTheme.desc")}
|
||||
>
|
||||
<Select
|
||||
options={lightThemeOptions}
|
||||
value={settings.lightTheme}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ lightTheme: value as string })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.darkTheme.name")}
|
||||
desc={t("setting.darkTheme.desc")}
|
||||
>
|
||||
<Select
|
||||
options={darkThemeOptions}
|
||||
value={settings.darkTheme}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ darkTheme: value as string })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.fontFamily.name")}
|
||||
desc={t("setting.fontFamily.desc")}
|
||||
collapsible={true}
|
||||
defaultCollapsed={false}
|
||||
>
|
||||
<TagInput
|
||||
values={settings.fontFamily}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ fontFamily: value })
|
||||
}
|
||||
suggestions={systemFonts}
|
||||
placeholder={t("setting.fontFamily.placeholder")}
|
||||
renderCustomSuggestion={(font) => (
|
||||
<div
|
||||
className="ace-font-family"
|
||||
style={{
|
||||
fontFamily: font,
|
||||
}}
|
||||
>
|
||||
<span>{font}</span>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.fontSize.name")}
|
||||
desc={t("setting.fontSize.desc")}
|
||||
>
|
||||
<Input
|
||||
type="number"
|
||||
value={settings.fontSize}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ fontSize: Number(value) })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.showLineNumbers.name")}
|
||||
desc={t("setting.showLineNumbers.desc")}
|
||||
>
|
||||
<Toggle
|
||||
checked={settings.showLineNumbers}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ showLineNumbers: value })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.showPrintMargin.name")}
|
||||
desc={t("setting.showPrintMargin.desc")}
|
||||
>
|
||||
<Toggle
|
||||
checked={settings.showPrintMargin}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ showPrintMargin: value })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.showInvisibles.name")}
|
||||
desc={t("setting.showInvisibles.desc")}
|
||||
>
|
||||
<Toggle
|
||||
checked={settings.showInvisibles}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ showInvisibles: value })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.displayIndentGuides.name")}
|
||||
desc={t("setting.displayIndentGuides.desc")}
|
||||
>
|
||||
<Toggle
|
||||
checked={settings.displayIndentGuides}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ displayIndentGuides: value })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.showFoldWidgets.name")}
|
||||
desc={t("setting.showFoldWidgets.desc")}
|
||||
>
|
||||
<Toggle
|
||||
checked={settings.showFoldWidgets}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ showFoldWidgets: value })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const SessionSettings = () => {
|
||||
return (
|
||||
<>
|
||||
<SettingsItem
|
||||
name={t("setting.supportExtensions.name")}
|
||||
desc={t("setting.supportExtensions.desc")}
|
||||
collapsible={true}
|
||||
defaultCollapsed={false}
|
||||
>
|
||||
<TagInput
|
||||
values={settings.supportExtensions}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ supportExtensions: value })
|
||||
}
|
||||
placeholder={t("setting.supportExtensions.placeholder")}
|
||||
suggestions={Object.values(languageModeMap).flat()}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.keyboard.name")}
|
||||
desc={t("setting.keyboard.desc")}
|
||||
>
|
||||
<Select
|
||||
options={keyboardOptions}
|
||||
value={settings.keyboard}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ keyboard: value as string })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.tabSize.name")}
|
||||
desc={t("setting.tabSize.desc")}
|
||||
>
|
||||
<Input
|
||||
type="number"
|
||||
value={settings.tabSize}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ tabSize: Number(value) })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const ExtendSettings = () => {
|
||||
return (
|
||||
<>
|
||||
<SettingsItem
|
||||
name={t("setting.snippetsManager.name")}
|
||||
desc={t("setting.snippetsManager.desc")}
|
||||
>
|
||||
<Toggle
|
||||
checked={settings.snippetsManager.location}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({
|
||||
snippetsManager: {
|
||||
...settings.snippetsManager,
|
||||
location: value,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
<IconPicker
|
||||
app={plugin.app}
|
||||
value={settings.snippetsManager.icon}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({
|
||||
snippetsManager: {
|
||||
...settings.snippetsManager,
|
||||
icon: value,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const AboutSettings = () => {
|
||||
return (
|
||||
<>
|
||||
<SettingsItem
|
||||
name={"wiki"}
|
||||
desc={parse(t("setting.desc"))}
|
||||
></SettingsItem>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const settingsTabNavItems: TabNavItem[] = [
|
||||
{
|
||||
id: "renderer",
|
||||
title: t("setting.tabs.renderer"),
|
||||
content: <RendererSettings />,
|
||||
},
|
||||
{
|
||||
id: "session",
|
||||
title: t("setting.tabs.session"),
|
||||
content: <SessionSettings />,
|
||||
},
|
||||
{
|
||||
id: "editor",
|
||||
title: t("setting.tabs.editor"),
|
||||
content: <EditorSettings />,
|
||||
},
|
||||
{
|
||||
id: "extend",
|
||||
title: t("setting.tabs.extend"),
|
||||
content: <ExtendSettings />,
|
||||
},
|
||||
{
|
||||
id: "about",
|
||||
title: t("setting.tabs.about"),
|
||||
content: <AboutSettings />,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsItem
|
||||
name={t("setting.supportExtensions.name")}
|
||||
desc={t("setting.supportExtensions.desc")}
|
||||
collapsible={true}
|
||||
defaultCollapsed={false}
|
||||
>
|
||||
<TagInput
|
||||
values={settings.supportExtensions}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ supportExtensions: value })
|
||||
}
|
||||
placeholder={t("setting.supportExtensions.placeholder")}
|
||||
suggestions={Object.values(languageModeMap).flat()}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.snippetsManager.name")}
|
||||
desc={t("setting.snippetsManager.desc")}
|
||||
>
|
||||
<Toggle
|
||||
checked={settings.snippetsManager.location}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({
|
||||
snippetsManager: {
|
||||
...settings.snippetsManager,
|
||||
location: value,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
<IconPicker
|
||||
app={plugin.app}
|
||||
value={settings.snippetsManager.icon}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({
|
||||
snippetsManager: {
|
||||
...settings.snippetsManager,
|
||||
icon: value,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.lightTheme.name")}
|
||||
desc={t("setting.lightTheme.desc")}
|
||||
>
|
||||
<Select
|
||||
options={lightThemeOptions}
|
||||
value={settings.lightTheme}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ lightTheme: value as string })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.darkTheme.name")}
|
||||
desc={t("setting.darkTheme.desc")}
|
||||
>
|
||||
<Select
|
||||
options={darkThemeOptions}
|
||||
value={settings.darkTheme}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ darkTheme: value as string })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.keyboard.name")}
|
||||
desc={t("setting.keyboard.desc")}
|
||||
>
|
||||
<Select
|
||||
options={keyboardOptions}
|
||||
value={settings.keyboard}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ keyboard: value as string })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.fontFamily.name")}
|
||||
desc={t("setting.fontFamily.desc")}
|
||||
collapsible={true}
|
||||
defaultCollapsed={false}
|
||||
>
|
||||
<TagInput
|
||||
values={settings.fontFamily}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ fontFamily: value })
|
||||
}
|
||||
suggestions={systemFonts}
|
||||
placeholder={t("setting.fontFamily.placeholder")}
|
||||
renderCustomSuggestion={(font) => (
|
||||
<div
|
||||
className="ace-font-family"
|
||||
style={{
|
||||
fontFamily: font,
|
||||
}}
|
||||
>
|
||||
<span>{font}</span>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.fontSize.name")}
|
||||
desc={t("setting.fontSize.desc")}
|
||||
>
|
||||
<Input
|
||||
type="number"
|
||||
value={settings.fontSize}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ fontSize: Number(value) })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.tabSize.name")}
|
||||
desc={t("setting.tabSize.desc")}
|
||||
>
|
||||
<Input
|
||||
type="number"
|
||||
value={settings.tabSize}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ tabSize: Number(value) })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
|
||||
<SettingsItem
|
||||
name={t("setting.lineNumbers.name")}
|
||||
desc={t("setting.lineNumbers.desc")}
|
||||
>
|
||||
<Toggle
|
||||
checked={settings.lineNumbers}
|
||||
onChange={(value) =>
|
||||
handleUpdateConfig({ lineNumbers: value })
|
||||
}
|
||||
/>
|
||||
</SettingsItem>
|
||||
</>
|
||||
<TabNav
|
||||
tabs={settingsTabNavItems}
|
||||
defaultValue="renderer"
|
||||
className="ace-settings-container"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import * as React from "react";
|
|||
|
||||
interface SettingsItemProps {
|
||||
name: string;
|
||||
desc?: string;
|
||||
desc?: string | React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
collapsible?: boolean;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import { ObsidianUtils } from "@/src/core/utils/ObsidianUtils";
|
||||
import { t } from "@/src/i18n/i18n";
|
||||
import AceCodeEditorPlugin from "@/src/main";
|
||||
import parse from "html-react-parser";
|
||||
import { App, PluginSettingTab } from "obsidian";
|
||||
import * as React from "react";
|
||||
import { createRoot, Root } from "react-dom/client";
|
||||
|
|
@ -38,26 +35,7 @@ export default class AceCodeEditorSettingTab extends PluginSettingTab {
|
|||
private renderContent() {
|
||||
this.root?.render(
|
||||
<React.StrictMode>
|
||||
<div className="ace-settings-container">
|
||||
<div className="ace-settings-header">
|
||||
<div className="ace-settings-header-left">
|
||||
<span>{parse(t("setting.desc"))}</span>
|
||||
</div>
|
||||
<div className="ace-settings-header-right">
|
||||
<button
|
||||
className="mod-cta"
|
||||
onClick={() =>
|
||||
ObsidianUtils.reloadPlugin(this.app)
|
||||
}
|
||||
>
|
||||
{t("command.reload")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ace-settings-content">
|
||||
<AceSettings plugin={this.plugin} />
|
||||
</div>
|
||||
</div>
|
||||
<AceSettings plugin={this.plugin} />
|
||||
</React.StrictMode>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export interface ICodeEditorConfig {
|
|||
lightTheme: AceLightThemes;
|
||||
darkTheme: AceDarkThemes;
|
||||
keyboard: AceKeyboard;
|
||||
lineNumbers: boolean;
|
||||
showLineNumbers: boolean;
|
||||
fontSize: number;
|
||||
fontFamily: string[];
|
||||
tabSize: number;
|
||||
|
|
@ -19,6 +19,12 @@ export interface ICodeEditorConfig {
|
|||
location: boolean;
|
||||
icon: string;
|
||||
};
|
||||
showPrintMargin: boolean;
|
||||
showInvisibles: boolean;
|
||||
displayIndentGuides: boolean;
|
||||
showFoldWidgets: boolean;
|
||||
highlightActiveLine: boolean;
|
||||
highlightSelectedWord: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_CONFIG: ICodeEditorConfig = {
|
||||
|
|
@ -26,7 +32,7 @@ export const DEFAULT_CONFIG: ICodeEditorConfig = {
|
|||
lightTheme: "chrome",
|
||||
darkTheme: "monokai",
|
||||
keyboard: "default",
|
||||
lineNumbers: true,
|
||||
showLineNumbers: true,
|
||||
fontSize: 14,
|
||||
fontFamily: ["Consolas", "Courier New", "monospace"],
|
||||
tabSize: 4,
|
||||
|
|
@ -34,6 +40,12 @@ export const DEFAULT_CONFIG: ICodeEditorConfig = {
|
|||
location: false,
|
||||
icon: "code",
|
||||
},
|
||||
showPrintMargin: true,
|
||||
showInvisibles: false,
|
||||
displayIndentGuides: true,
|
||||
showFoldWidgets: true,
|
||||
highlightActiveLine: true,
|
||||
highlightSelectedWord: true,
|
||||
};
|
||||
|
||||
export interface ICodeBlock {
|
||||
|
|
|
|||
|
|
@ -121,7 +121,9 @@ export class AceService {
|
|||
// -- editor选项 --
|
||||
// 选中样式 selectionStyle: text [line|text]
|
||||
// 高亮当前行 highlightActiveLine: true
|
||||
highlightActiveLine: config.highlightActiveLine,
|
||||
// 高亮选中文本 highlightSelectedWord: true
|
||||
highlightSelectedWord: config.highlightSelectedWord,
|
||||
// 是否只读 readOnly: false
|
||||
// 光标样式 cursorStyle: ace [ace|slim|smooth|wide]
|
||||
// 合并撤销 mergeUndoDeltas: false [always]
|
||||
|
|
@ -139,15 +141,19 @@ export class AceService {
|
|||
// 高亮边线 highlightGutterLine: true
|
||||
// 滚动动画 animatedScroll: false
|
||||
// 显示不可见字符 showInvisibles: false
|
||||
showInvisibles: config.showInvisibles,
|
||||
// 显示打印边距 showPrintMargin: true
|
||||
showPrintMargin: config.showPrintMargin,
|
||||
// 设置页边距 printMarginColumn: 80
|
||||
// 显示并设置页边距 printMargin: false
|
||||
// 淡入折叠部件 fadeFoldWidgets: false
|
||||
// 显示折叠部件 showFoldWidgets: true
|
||||
showFoldWidgets: config.showFoldWidgets,
|
||||
// 显示行号
|
||||
showLineNumbers: config.lineNumbers,
|
||||
showLineNumbers: config.showLineNumbers,
|
||||
// 显示行号区域 showGutter: true
|
||||
// 显示参考线 displayIndentGuides: true
|
||||
displayIndentGuides: config.displayIndentGuides,
|
||||
// 设置字号
|
||||
fontSize: config.fontSize,
|
||||
// 设置字体
|
||||
|
|
|
|||
|
|
@ -56,6 +56,13 @@ const translations: BaseMessage = {
|
|||
},
|
||||
},
|
||||
setting: {
|
||||
tabs: {
|
||||
renderer: "Renderer",
|
||||
session: "Session",
|
||||
editor: "Editor",
|
||||
extend: "Extend",
|
||||
about: "About",
|
||||
},
|
||||
desc: "View <a href='https://docs.ravenhogwarts.top/en/obsidian-ace-code-editor/' target='_blank'>wiki documentation</a> to learn more features",
|
||||
supportExtensions: {
|
||||
name: "Register file extension",
|
||||
|
|
@ -78,7 +85,7 @@ const translations: BaseMessage = {
|
|||
name: "Keyboard",
|
||||
desc: "Keyboard style for code editor",
|
||||
},
|
||||
lineNumbers: {
|
||||
showLineNumbers: {
|
||||
name: "Line numbers",
|
||||
desc: "Show line numbers",
|
||||
},
|
||||
|
|
@ -95,6 +102,30 @@ const translations: BaseMessage = {
|
|||
name: "Indentation width",
|
||||
desc: "Width of tab key",
|
||||
},
|
||||
showPrintMargin: {
|
||||
name: "Show print margin",
|
||||
desc: "Vertical line in code editor",
|
||||
},
|
||||
showInvisibles: {
|
||||
name: "Show invisibles",
|
||||
desc: "Display invisible characters in code editor",
|
||||
},
|
||||
displayIndentGuides: {
|
||||
name: "Display indent guides",
|
||||
desc: "Show indent guides in code editor",
|
||||
},
|
||||
showFoldWidgets: {
|
||||
name: "Show fold widgets",
|
||||
desc: "Display fold widgets in code editor",
|
||||
},
|
||||
highlightActiveLine: {
|
||||
name: "Highlight active line",
|
||||
desc: "Highlight the current line in code editor",
|
||||
},
|
||||
highlightSelectedWord: {
|
||||
name: "Highlight selected word",
|
||||
desc: "Highlight the selected word in code editor",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,13 @@ const translations: BaseMessage = {
|
|||
},
|
||||
},
|
||||
setting: {
|
||||
tabs: {
|
||||
renderer: "渲染",
|
||||
session: "会话",
|
||||
editor: "编辑",
|
||||
extend: "扩展",
|
||||
about: "关于",
|
||||
},
|
||||
desc: "查看<a href='https://docs.ravenhogwarts.top/obsidian-ace-code-editor/' target='_blank'>wiki文档</a>了解更多功能",
|
||||
supportExtensions: {
|
||||
name: "注册文件类型",
|
||||
|
|
@ -75,7 +82,7 @@ const translations: BaseMessage = {
|
|||
name: "键盘",
|
||||
desc: "代码编辑器的键盘风格",
|
||||
},
|
||||
lineNumbers: {
|
||||
showLineNumbers: {
|
||||
name: "行号",
|
||||
desc: "显示行号",
|
||||
},
|
||||
|
|
@ -92,6 +99,30 @@ const translations: BaseMessage = {
|
|||
name: "缩进宽度",
|
||||
desc: "tab键的宽度",
|
||||
},
|
||||
showPrintMargin: {
|
||||
name: "打印边距",
|
||||
desc: "显示代码编辑器中的竖线",
|
||||
},
|
||||
showInvisibles: {
|
||||
name: "不可见字符",
|
||||
desc: "在代码编辑器中显示不可见字符",
|
||||
},
|
||||
displayIndentGuides: {
|
||||
name: "缩进参考线",
|
||||
desc: "在代码编辑器中显示缩进参考线",
|
||||
},
|
||||
showFoldWidgets: {
|
||||
name: "折叠部件",
|
||||
desc: "在代码编辑器中显示折叠部件",
|
||||
},
|
||||
highlightActiveLine: {
|
||||
name: "当前行高亮",
|
||||
desc: "在代码编辑器中高亮当前行",
|
||||
},
|
||||
highlightSelectedWord: {
|
||||
name: "选中文本高亮",
|
||||
desc: "在代码编辑器中高亮选中的文本",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,13 @@ const translations: BaseMessage = {
|
|||
},
|
||||
},
|
||||
setting: {
|
||||
tabs: {
|
||||
renderer: "渲染",
|
||||
session: "會話",
|
||||
editor: "編輯",
|
||||
extend: "擴展",
|
||||
about: "關於",
|
||||
},
|
||||
desc: "查看<a href='https://docs.ravenhogwarts.top/obsidian-ace-code-editor/' target='_blank'>wiki文件</a>了解更多功能",
|
||||
supportExtensions: {
|
||||
name: "註冊檔案副檔名",
|
||||
|
|
@ -75,7 +82,7 @@ const translations: BaseMessage = {
|
|||
name: "鍵盤",
|
||||
desc: "程式碼編輯器的鍵盤風格",
|
||||
},
|
||||
lineNumbers: {
|
||||
showLineNumbers: {
|
||||
name: "行號",
|
||||
desc: "顯示行號",
|
||||
},
|
||||
|
|
@ -92,6 +99,30 @@ const translations: BaseMessage = {
|
|||
name: "縮排寬度",
|
||||
desc: "tab鍵的寬度",
|
||||
},
|
||||
showPrintMargin: {
|
||||
name: "顯示列印邊界",
|
||||
desc: "程式碼編輯器中的直線",
|
||||
},
|
||||
showInvisibles: {
|
||||
name: "顯示不可見字符",
|
||||
desc: "在程式碼編輯器中顯示不可見字符",
|
||||
},
|
||||
displayIndentGuides: {
|
||||
name: "顯示縮排指導線",
|
||||
desc: "在程式碼編輯器中顯示縮排指導線",
|
||||
},
|
||||
showFoldWidgets: {
|
||||
name: "顯示折疊小工具",
|
||||
desc: "在程式碼編輯器中顯示折疊小工具",
|
||||
},
|
||||
highlightActiveLine: {
|
||||
name: "高亮當前行",
|
||||
desc: "在程式碼編輯器中高亮當前行",
|
||||
},
|
||||
highlightSelectedWord: {
|
||||
name: "高亮選中文本",
|
||||
desc: "在程式碼編輯器中高亮選中的單詞",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,13 @@ export type BaseMessage = {
|
|||
};
|
||||
};
|
||||
setting: {
|
||||
tabs: {
|
||||
renderer: string;
|
||||
session: string;
|
||||
editor: string;
|
||||
extend: string;
|
||||
about: string;
|
||||
};
|
||||
desc: string;
|
||||
supportExtensions: SettingsItem<{
|
||||
placeholder: string;
|
||||
|
|
@ -77,12 +84,18 @@ export type BaseMessage = {
|
|||
lightTheme: IBaseSettingsItem;
|
||||
darkTheme: IBaseSettingsItem;
|
||||
keyboard: IBaseSettingsItem;
|
||||
lineNumbers: IBaseSettingsItem;
|
||||
showLineNumbers: IBaseSettingsItem;
|
||||
fontSize: IBaseSettingsItem;
|
||||
fontFamily: SettingsItem<{
|
||||
placeholder: string;
|
||||
}>;
|
||||
tabSize: IBaseSettingsItem;
|
||||
showPrintMargin: IBaseSettingsItem;
|
||||
showInvisibles: IBaseSettingsItem;
|
||||
displayIndentGuides: IBaseSettingsItem;
|
||||
showFoldWidgets: IBaseSettingsItem;
|
||||
highlightActiveLine: IBaseSettingsItem;
|
||||
highlightSelectedWord: IBaseSettingsItem;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
239
style/BaseTabNav.css
Normal file
239
style/BaseTabNav.css
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
/* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +1,32 @@
|
|||
.ace-settings-container {
|
||||
padding: 1rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.ace-settings-header {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
flex-direction: column;
|
||||
|
||||
.ace-settings-header-left {
|
||||
.ace--TabGroup {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.ace--TabList {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ace--TabPanels {
|
||||
background: var(--background-secondary);
|
||||
padding: 1.5rem;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-top: unset;
|
||||
flex: 1;
|
||||
}
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
/* 为设置页面提供固定的高度,确保内部滚动生效 */
|
||||
max-height: calc(100vh - 120px);
|
||||
|
||||
.ace-settings-header-right {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.ace-settings-content {
|
||||
background: var(--background-secondary);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
|
||||
.ace-font-family {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.ace-font-family {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import "./BaseInput.css";
|
|||
import "./BaseModal.css";
|
||||
import "./BaseSelect.css";
|
||||
import "./BaseSettingItem.css";
|
||||
import "./BaseTabNav.css";
|
||||
import "./BaseTagInput.css";
|
||||
import "./BaseToggle.css";
|
||||
import "./CreateCodeFileModal.css";
|
||||
|
|
|
|||
Loading…
Reference in a new issue