-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.ts
321 lines (265 loc) · 10.6 KB
/
main.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
import { App, Plugin, PluginSettingTab, Setting, WorkspaceLeaf, ItemView, Notice, TFile } from 'obsidian';
import * as opentype from 'opentype.js';
import * as path from 'path';
import { Font } from 'opentype.js';
// 定义视图类型
const VIEW_TYPE_FONT = "font-preview-view";
interface FontViewerSettings {
fontSize: number;
fontDirectory: string; // 相对于 vault 根目录的路径
}
const DEFAULT_SETTINGS: FontViewerSettings = {
fontSize: 16,
fontDirectory: '.obsidian/fonts' // 默认路径
}
// 创建字体预览视图类
class FontPreviewView extends ItemView {
plugin: FontViewerPlugin;
constructor(leaf: WorkspaceLeaf, plugin: FontViewerPlugin) {
super(leaf);
this.plugin = plugin;
}
getViewType(): string {
return VIEW_TYPE_FONT;
}
getDisplayText(): string {
return "Font Preview";
}
getIcon(): string {
return "text";
}
async onOpen() {
const container = this.containerEl.children[1];
container.empty();
// Add title
container.createEl("h3", { text: "Font Preview Settings" });
// Add font size slider
const sizeControl = container.createEl("div", { cls: "font-size-control" });
sizeControl.createEl("span", { text: "Font Size: " });
const sizeSlider = sizeControl.createEl("input", {
type: "range",
attr: {
min: "8",
max: "72",
value: this.plugin.settings.fontSize.toString()
}
});
const sizeLabel = sizeControl.createEl("span", {
text: `${this.plugin.settings.fontSize}px`
});
// Add font directory input
const dirControl = container.createEl("div", { cls: "font-dir-control" });
dirControl.createEl("span", { text: "Font Directory: " });
const dirInput = dirControl.createEl("input", {
type: "text",
value: this.plugin.settings.fontDirectory,
placeholder: "Enter font directory path"
});
// Add generate button
const generateButton = container.createEl("button", {
text: "Generate Font Preview",
cls: "mod-cta"
});
// Add hint message
container.createEl("div", {
text: "Supported formats: .ttf, .otf, .woff, .woff2",
cls: "font-preview-hint"
});
// Event listeners remain the same
sizeSlider.addEventListener("input", async (e) => {
const size = parseInt((e.target as HTMLInputElement).value);
sizeLabel.setText(`${size}px`);
this.plugin.settings.fontSize = size;
await this.plugin.saveSettings();
});
dirInput.addEventListener("change", async (e) => {
this.plugin.settings.fontDirectory = (e.target as HTMLInputElement).value;
await this.plugin.saveSettings();
});
generateButton.addEventListener("click", () => {
this.plugin.generateFontPreview();
});
}
}
export default class FontViewerPlugin extends Plugin {
settings: FontViewerSettings;
view: FontPreviewView;
async onload() {
await this.loadSettings();
// 注册视图
this.registerView(
VIEW_TYPE_FONT,
(leaf) => (this.view = new FontPreviewView(leaf, this))
);
// 添加侧边栏按钮
this.addRibbonIcon("text", "Open Font Preview", () => {
this.activateView();
});
// 添加设置选项
this.addSettingTab(new FontViewerSettingTab(this.app, this));
}
async onunload() {
// 清理视图
this.app.workspace.detachLeavesOfType(VIEW_TYPE_FONT);
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
async activateView() {
const { workspace } = this.app;
let leaf = workspace.getLeavesOfType(VIEW_TYPE_FONT)[0];
if (!leaf) {
const newLeaf = workspace.getRightLeaf(false);
if (newLeaf) {
await newLeaf.setViewState({ type: VIEW_TYPE_FONT });
leaf = newLeaf;
}
}
workspace.revealLeaf(leaf);
}
async generateFontPreview() {
if (!this.settings.fontDirectory) {
new Notice('Please set the font directory first');
return;
}
try {
// Check if directory exists
const dirExists = await this.app.vault.adapter.exists(this.settings.fontDirectory);
if (!dirExists) {
try {
await this.app.vault.createFolder(this.settings.fontDirectory);
new Notice('Font directory created');
} catch (error) {
new Notice('Cannot create font directory: ' + error.message);
return;
}
}
// List font files
const files = await this.app.vault.adapter.list(this.settings.fontDirectory);
const fontFiles = files.files.filter(file =>
file.endsWith('.ttf') || file.endsWith('.otf')
);
// Generate preview content
const content = await this.generatePreviewContent(fontFiles);
// Create or update preview file
const previewFileName = 'Font-Preview.md';
let file = this.app.vault.getAbstractFileByPath(previewFileName);
if (file instanceof TFile) {
await this.app.vault.modify(file, content);
} else {
file = await this.app.vault.create(previewFileName, content);
}
// Open preview file
if (file instanceof TFile) {
await this.app.workspace.getLeaf().openFile(file);
new Notice('Font preview generated successfully!');
}
} catch (error) {
console.error('Font preview error:', error);
new Notice('Error generating preview: ' + error.message);
}
}
private async generatePreviewContent(fontFiles: string[]): Promise<string> {
const contentArray: string[] = [];
contentArray.push('# Font Preview\n');
contentArray.push('## Settings\n');
contentArray.push(`- Font Directory: \`${this.settings.fontDirectory}\`\n`);
contentArray.push(`- Font Size: ${this.settings.fontSize}px\n\n`);
if (fontFiles.length === 0) {
contentArray.push('> [!warning] No Font Files Found\n');
contentArray.push('> Please put font files (.ttf, .otf) into the font directory\n\n');
return contentArray.join('');
}
contentArray.push('## Font List\n\n');
contentArray.push('| Font Name | Preview |\n');
contentArray.push('|-----------|----------|\n');
for (const fontPath of fontFiles) {
try {
console.log('Processing font:', fontPath);
const arrayBuffer = await this.app.vault.adapter.readBinary(fontPath);
if (!arrayBuffer) {
throw new Error('Could not read font file');
}
const font = await new Promise<Font>((resolve, reject) => {
try {
const font = opentype.parse(arrayBuffer);
resolve(font);
} catch (err) {
reject(err);
}
});
const fontName = path.basename(fontPath, path.extname(fontPath));
const base64 = arrayBufferToBase64(arrayBuffer);
const fontFormat = fontPath.endsWith('.otf') ? 'opentype' : 'truetype';
const dataUrl = `data:font/${fontFormat};charset=utf-8;base64,${base64}`;
contentArray.push(`<style>
@font-face {
font-family: '${fontName}';
src: url('${dataUrl}') format('${fontFormat}');
}
</style>`);
contentArray.push(
`| ${fontName} | ` +
`<div style="font-family: '${fontName}'; font-size: ${this.settings.fontSize}px;">` +
`The quick brown fox jumps over the lazy dog<br>` +
`ABCDEFGHIJKLMNOPQRSTUVWXYZ<br>` +
`abcdefghijklmnopqrstuvwxyz<br>` +
`1234567890</div> |`
);
} catch (error) {
console.error('Font processing error:', error);
contentArray.push(`| Error | Failed to load font: ${error.message} |`);
}
}
contentArray.push('\n## Instructions\n');
contentArray.push('- Supported font formats: .ttf, .otf\n');
contentArray.push(`- Place font files in: \`${this.settings.fontDirectory}\`\n`);
contentArray.push('- Adjust preview font size in plugin settings\n');
return contentArray.join('\n');
}
}
// Helper function to convert ArrayBuffer to base64
function arrayBufferToBase64(buffer: ArrayBuffer): string {
let binary = '';
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
class FontViewerSettingTab extends PluginSettingTab {
plugin: FontViewerPlugin;
constructor(app: App, plugin: FontViewerPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const {containerEl} = this;
containerEl.empty();
new Setting(containerEl)
.setName('Font Directory')
.setDesc('Set the directory path containing your font files')
.addText(text => text
.setPlaceholder('Enter font directory path')
.setValue(this.plugin.settings.fontDirectory)
.onChange(async (value) => {
this.plugin.settings.fontDirectory = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Font Size')
.setDesc('Set the preview font size (in pixels)')
.addSlider(slider => slider
.setLimits(8, 72, 1)
.setValue(this.plugin.settings.fontSize)
.setDynamicTooltip()
.onChange(async (value) => {
this.plugin.settings.fontSize = value;
await this.plugin.saveSettings();
}));
}
}