├── LICENSE.md
├── README.md
├── data.json
├── main.js
├── manifest.json
├── obsidian-sengener
├── 11.gif
├── 48c10a17095bc84a8939d4a7ac2326e.jpg
├── _english_demo.gif
├── _new_demo_en.gif
├── b3df27009a29d0d01a8234967235832.jpg
├── data.json
├── demo.gif
├── demo_cn.gif
├── main.js
├── manifest.json
└── styles.css
├── readme_CN.md
└── styles.css
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Zazaji
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SenGenerPlugin for obsidian [中文](readme_CN.md)
2 |
3 | This plugin is used to generate a serial of Sentenses for writting.
4 |
5 | 
6 |
7 | 
8 |
9 | 
10 |
11 | 
12 |
13 | 
14 |
15 | 
16 |
17 | ## Thanks
18 | Sengener based on obsidian-completr. I copied lots of code from it.
19 | https://github.com/tth05/obsidian-completr
20 |
21 | ## How to use this plugin
22 |
23 | 1. download and install obsidian. Create a vault and specify the path.
24 |
25 | 2. goto the vault folder. download from releases , and extract them to the plugin folder,
26 | the path like: VAULT/.obsidian/plugins/obsidian-sengener.
27 |
28 | 3. Open setting, enable community plugins, then set you options, enable SenSener and configure hot-keys. The default shortcut key is ctrl+`quotation`.
29 |
30 | 4. You can also select different authoring models and adjust other parameters.
31 |
32 | 5. Create a document and start writing. Enjoy it. And contact me: zazaji@sina.com.
33 |
34 | ## Parameter
35 | - API address: service address : Fill in your own API address. I built an example service, which includes English model, dialogue model(Chinese), work report model(Chinese) and Tencent welm model(Chinese and few English suport). Sample address: https://fwzd.myfawu.com
36 | - Type: You can select different authoring models.
37 | - Token: the token used to log in to Tencent welm. You can apply by yourself on Tencent welm official website.
38 | - Enable searching: Whether to enable full-text retrieval. Currently, it provides full-text retrieval for the report model.
39 | - Number of choices: How many candidates. Don’t choose too many, which will affect the speed.
40 | - Max length: the number of words produced at a time. Don’t choose too many, which will affect the speed.
41 |
42 | ## Build your data service
43 |
44 | - I build the API service with GPT2. You can also use GPT2 to generate Sentenses directly , or Another one, like yuanyu, or chatGPT.
45 | - You can build your API service for generating Sentenses. Or you can use the sample service, just for test.
46 | - The example provides english model and chinese model, and Full-text search . If you want use Another language , you can train your language generation service.
47 | - Data service contain text-generator and full-text-search. **The new repo will be on soon**.
48 |
49 |
50 | - You can build a API service like :
51 | Post Json:
52 |
53 | ```Json
54 | {
55 | "context": "Yes, We ",
56 | "token": "Your_token",
57 | "article_type": "english",
58 | "max_length": 10,
59 | "number": 3,
60 | "is_index": true
61 | }
62 | ```
63 |
64 | Response Json:
65 | ```Json
66 |
67 | {
68 | "ref": [{
69 | "content": "...",
70 | "title": "Nothing"
71 | }],
72 | "sentenses": [{
73 | "value": ", the people of the United States, stand together"
74 | }, {
75 | "value": " to say, this is the best deal we've"
76 | }, {
77 | "value": " can't say anything, but it's not our"
78 | }]
79 | }
80 | ```
81 |
--------------------------------------------------------------------------------
/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "apiUrl": "https://fwzd.myfawu.com/",
3 | "chioceNumber": 3,
4 | "maxLength": 10,
5 | "className": "article",
6 | "isIndex": true,
7 | "token": "cchi48mv9mc753cgt5ig",
8 | "cnNote": "Writting assistant: Enjoy writing, full-text search and text-generation supported.
写作助手:人工智能技术,让你文思如泉涌,写文章、作诗、报告纵享丝滑。可定制自己的专属模型。mailto:zazaji@sina.com"
9 | }
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | # SenGenerPlugin for obsidian
3 | This plugin is used to generate a serial of Sentenses for writting.
4 | First , You need to build your API service for generating Sentenses.
5 | I build the API service with GPT2. You can also use GPT2 to generate Sentenses directly , or Another one.
6 |
7 | Author:https://github.com/zazaji
8 | Thanks: https://github.com/tth05/obsidian-completr
9 | */
10 | var __show = false;
11 | var __end;
12 | var __apiUrl;
13 | // var __apiUrl2;
14 | var __activeView;
15 | var __article_types;
16 | var __max_length = 100;
17 | var __token;
18 | var __isIndex;
19 | var __cn_note;
20 | var __time = new Date().getTime() - 2000;
21 | var __article_type;
22 | const QUOTE_VIEW_TYPE = "QUOTE";
23 | var BARCONTAINER;
24 | var statusBarItem;
25 | var __defProp = Object.defineProperty;
26 | var __defProps = Object.defineProperties;
27 | var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
28 | var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
29 | var __getOwnPropNames = Object.getOwnPropertyNames;
30 | var __getOwnPropSymbols = Object.getOwnPropertySymbols;
31 | var __hasOwnProp = Object.prototype.hasOwnProperty;
32 | var __propIsEnum = Object.prototype.propertyIsEnumerable;
33 | var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
34 | var __spreadValues = (a, b) => {
35 | for (var prop in b || (b = {}))
36 | if (__hasOwnProp.call(b, prop))
37 | __defNormalProp(a, prop, b[prop]);
38 | if (__getOwnPropSymbols)
39 | for (var prop of __getOwnPropSymbols(b)) {
40 | if (__propIsEnum.call(b, prop))
41 | __defNormalProp(a, prop, b[prop]);
42 | }
43 | return a;
44 | };
45 | var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
46 | var __export = (target, all) => {
47 | for (var name in all)
48 | __defProp(target, name, { get: all[name], enumerable: true });
49 | };
50 | var __copyProps = (to, from, except, desc) => {
51 | if (from && typeof from === "object" || typeof from === "function") {
52 | for (let key of __getOwnPropNames(from))
53 | if (!__hasOwnProp.call(to, key) && key !== except)
54 | __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
55 | }
56 | return to;
57 | };
58 | var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
59 | var __async = (__this, __arguments, generator) => {
60 | return new Promise((resolve, reject) => {
61 | var fulfilled = (value) => {
62 | try {
63 | step(generator.next(value));
64 | } catch (e) {
65 | reject(e);
66 | }
67 | };
68 | var rejected = (value) => {
69 | try {
70 | step(generator.throw(value));
71 | } catch (e) {
72 | reject(e);
73 | }
74 | };
75 | var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
76 | step((generator = generator.apply(__this, __arguments)).next());
77 | });
78 | };
79 |
80 | // src/main.ts
81 | var main_exports = {};
82 | __export(main_exports, {
83 | default: () => SenGenerPlugin
84 | });
85 | module.exports = __toCommonJS(main_exports);
86 | var import_obsidian5 = require("obsidian");
87 |
88 | // src/snippet_manager.ts
89 | var SnippetManager = class {
90 | constructor() {
91 | this.currentPlaceholderReferences = [];
92 | }
93 | onunload() {
94 | this.clearAllPlaceholders();
95 | }
96 | clearAllPlaceholders() {
97 | if (this.currentPlaceholderReferences.length === 0)
98 | return;
99 | const firstRef = this.currentPlaceholderReferences[0];
100 | const view = editorToCodeMirrorView(firstRef.editor);
101 | view.dispatch({
102 | effects: clearMarks.of(null)
103 | });
104 | this.currentPlaceholderReferences = [];
105 | }
106 | };
107 |
108 | // src/provider/provider.ts
109 | function getSuggestionDisplayName(suggestion, lowerCase = false) {
110 | const res = typeof suggestion === "string" ? suggestion : suggestion.displayName;
111 | return lowerCase ? res.toLowerCase() : res;
112 | }
113 |
114 | function content_text(text) {
115 | BARCONTAINER.children[5].empty();
116 | BARCONTAINER.children[5].createDiv('sengener_ssmall markdown-preview-view', (el) => {
117 | obsidian.MarkdownRenderer.renderMarkdown(text, el, '', this);
118 | });
119 | }
120 |
121 | //根据总页数和当前页分页
122 | function pagebar(n, m) {
123 | BARCONTAINER.children[4].empty();
124 | start = ((n > 5) && (m - 2 > 1)) ? m - 2 : 1;
125 | start = ((n <= 5) || (start + 4 < n)) ? start : n - 4;
126 | end = start + 4 > n ? ((m + 2 <= n) && ((n > 5)) ? m + 2 : n) : start + 4;
127 | if (1 < start) {
128 | BARCONTAINER.children[4].createEl("a", { text: "<<" }, (el) => {
129 | el.onClickEvent(() => {
130 | searchTerm(start - 3, __article_type);
131 | })
132 | })
133 | }
134 | for (let i = start; i <= end; i++) {
135 | if (m != i) {
136 | BARCONTAINER.children[4].createEl("a", { text: i }, (el) => {
137 | el.onClickEvent(() => {
138 | searchTerm(i, __article_type);
139 | })
140 | })
141 | } else {
142 | BARCONTAINER.children[4].createEl("a", { text: i, cls: "active" });
143 | }
144 | }
145 |
146 | if (n > end) {
147 | BARCONTAINER.children[4].createEl("a", { text: ">>" }, (el) => {
148 | el.onClickEvent(() => {
149 | searchTerm(end + 3, __article_type);
150 | })
151 | })
152 | }
153 | }
154 |
155 | function getSuggestionReplacement(suggestion) {
156 | return typeof suggestion === "string" ? suggestion : suggestion.replacement;
157 | }
158 | //状态栏信息提示
159 | function bar_text(text, timeout = 10000) {
160 | statusBarItem.empty();
161 | statusBarItem.createEl("span", { text: text });
162 | if (timeout > 0) {
163 | setTimeout(() => { statusBarItem.empty(); }, timeout);
164 | }
165 | return '';
166 | }
167 | //
168 | // src/settings.ts
169 | var DEFAULT_SETTINGS = {
170 | // apiUrl: "https://transformer.huggingface.co/autocomplete/",
171 | apiUrl: "https://fwzd.myfawu.com/",
172 | chioceNumber: 3,
173 | maxLength: 20,
174 | className: "poem",
175 | isIndex: false
176 | }
177 |
178 |
179 | //全文检索函数
180 | async function searchTerm(page, article_type) {
181 | let idata = {
182 | "context": BARCONTAINER.children[2].value,
183 | 'page': page,
184 | 'token': __token,
185 | 'article_type': article_type,
186 | };
187 | bar_text("Loading...🍋");
188 | try {
189 | let res = await fetch(__apiUrl + 'refer', {
190 | method: "post",
191 | headers: {
192 | "content-type": "application/json"
193 | },
194 | body: JSON.stringify(idata)
195 | });
196 | let data = await res.json();
197 | if (data != '') {
198 |
199 | let text = data['ref'].map(function(item) { return ' -' + item['title'] + '
' + item['content'] + '' }).join('
');
200 | bar_text("Done! 🍀");
201 | pagebar(data['page'], page);
202 | content_text(text);
203 | }
204 | } catch (e) {
205 | console.log(e);
206 | return bar_text("Failed. 🆘");
207 | }
208 |
209 | }
210 |
211 | //获取功能列表
212 | async function get_article_type() {
213 | try {
214 | let res = await fetch(__apiUrl + 'func');
215 | let data = await res.json();
216 | if (data != '') {
217 | __article_types = data;
218 | if (__article_types.hasOwnProperty(__article_type) == false) {
219 | __article_type = Object.keys(__article_types)[0];
220 | }
221 | bar_text("Loading " + __article_type + "🍀");
222 | }
223 | } catch (e) {
224 | console.log(e);
225 | return bar_text("Failed. 🆘");
226 | }
227 | }
228 |
229 | //生成句子函数
230 | async function senGenerate(url, text, atype, number, max_length, isindex = false) {
231 | console.log(new Date().getTime() - __time);
232 | if (new Date().getTime() - __time < 1.5 * 1000) {
233 | return bar_text("Request slowly. 🆘");
234 | }
235 | __time = new Date().getTime();
236 | let idata = {
237 | "context": text,
238 | 'token': __token,
239 | "model_size": "distilgpt2/small",
240 | "article_type": atype,
241 | "top_p": 0.9,
242 | "temperature": 1,
243 | "max_time": 1.2,
244 | "max_length": max_length,
245 | "is_index": isindex,
246 | "number": number
247 | };
248 | try {
249 | let res = await fetch(url, {
250 | method: "post",
251 | headers: {
252 | "content-type": "application/json"
253 | },
254 | body: JSON.stringify(idata)
255 | });
256 | let data = await res.json();
257 | if (data != '') {
258 | if (isindex) {
259 | BARCONTAINER.children[2].value = data['keywords'];
260 | let text = data['ref'].map(function(item) { return '
' + item['title'] + '
' + item['content'] + '' }).join('
');
261 | pagebar(data['page'], 1);
262 | content_text(text);
263 | }
264 | console.log(data['sentences']);
265 | return data['sentences'].map(function(item) { return item['value'] })
266 | } else {
267 | return [];
268 | }
269 | } catch (e) {
270 | console.log(e);
271 | return bar_text("Failed. 🆘");
272 | }
273 | }
274 | //🥔🍡🍧🐬🍫🌒🥓🦑🦃🍋🐌🦂🥛🍔🐔🍘💐🌔🍺🍙🐡🦞🐋🦚🦀🌱🍐🥥🎂🐠🍕💚💞🥕🍨🍇🆖
275 | //💟🍭🍢🥀🥑🍋🌺🍤🐤🍟🍁🐝🥬🛶🌳🌖🍝🌼🧂🥤🍱🍿🍩🦜⏬🆘🥠💛🐦🍗🌰🌭🧁🫒🐙🦆🃏❗
276 | //🥜🌶💓🐥🥣⏰🍵💗🍸🧆🌚🍄🌝🍣🍹🌐🦠🎾🍈🫐🦩🦉⛔🍖🥡🌌🕷🐟🆓🍮🐓💕️🦴🍼🍛🐳🦗⛅
277 | //💙❤🍓🏕🌞🌗🪴🦢🌓🧅🌮🌁🍒🍊🐚🌏🌛🍏🦋⏳🍚🍠🌸🌋🦐🧄🐜🌕🍻🥝🐧🍯⚘🍪🥫🍾❣🥮🌴💔
278 | //🍂🐛🌵🌍🧀🥨🐾🥂🍆🌽🌯🦪🥩🥧🌠🍀🥙🥘🍃🍍🥒⏫🍅🥞🍦🍰🥟🦅🍷💜❓🍲🥪.🍀🍉💘🌉
279 | //🧃💮🦟🧇🥚🍥🍳🍶🥗🎍🦈🍴🥯🍽🕸🌿➰🆑🥭🌘🌜🍜💖🌲🐣🐞🍑☕🍬🌎🥖🍞🆘💝🥐🧈
280 | var obsidian = require('obsidian');
281 | // 添加右侧栏
282 | var import_obsidian3 = require("obsidian");
283 | // 初始化右侧栏
284 | class QUOTEListView extends obsidian.ItemView {
285 | constructor(leaf, plugin) {
286 | super(leaf);
287 | this.plugin = plugin;
288 | this.lastRerender = 0;
289 | this.groupedItems = [];
290 | this.itemsByFile = new Map();
291 | this.initialLoad = true;
292 | this.searchTerm = "";
293 | }
294 | getViewType() {
295 | return QUOTE_VIEW_TYPE;
296 | }
297 | getDisplayText() {
298 | return "QUOTE List";
299 | }
300 | getIcon() {
301 | return 'list';
302 | }
303 | async onload() {
304 | setTimeout(() => {
305 | __article_type = this.plugin.settings.className;
306 | __apiUrl = this.plugin.settings.apiUrl;
307 | __token = this.plugin.settings.token;
308 | __isIndex = this.plugin.settings.isIndex;
309 | __cn_note = this.plugin.settings.cnNote;
310 | __max_length = this.plugin.settings.max_length;
311 | get_article_type();
312 | }, 3000);
313 | statusBarItem = this.plugin.addStatusBarItem();
314 |
315 | }
316 | // 右侧栏
317 | async onOpen() {
318 | BARCONTAINER = this.containerEl.children[1];
319 | // BARCONTAINER = container;
320 | BARCONTAINER.empty();
321 | BARCONTAINER.createEl("h4", { text: "Writting Assistant", cls: 'sengener_col-10' });
322 | BARCONTAINER.createEl("button", { text: "Auto", type: 'button', cls: 'sengener_col-2' }, (el) => {
323 | el.onClickEvent(() => {
324 | bar_text("Loading...🍋 ");
325 | auto_write(7, 7);
326 | });
327 | });
328 |
329 | BARCONTAINER.createEl("input", { value: "", type: 'text', cls: 'sengener_col-6', placeholder: 'Please input keywords' });
330 | BARCONTAINER.createEl("button", { text: "🍳", type: 'button', cls: 'sengener_col-2' }, (el) => {
331 | el.onClickEvent(() => {
332 | __apiUrl = this.plugin.settings.apiUrl;
333 | searchTerm(1, __article_type);
334 | })
335 | });
336 | BARCONTAINER.createDiv("pagination");
337 | BARCONTAINER.createDiv("content", (el) => {
338 | obsidian.MarkdownRenderer.renderMarkdown('', el, '', this);
339 | });
340 | }
341 |
342 | async onClose() {
343 | // Nothing to clean up.
344 | }
345 | }
346 |
347 |
348 | //生成多个句子函数
349 | function auto_write(j, n) {
350 | if (__activeView) {
351 | let line = __activeView.editor.lastLine();
352 | let cursor = __activeView.editor.getLine(line).length;
353 | __end.ch = cursor;
354 | __end.line = line;
355 | let words = ''
356 | for (var i = line - 3; i < line; i++) {
357 | if (i >= 0) {
358 | words += __activeView.editor.getLine(i) + '\n';
359 | }
360 | }
361 | let last_word = __activeView.editor.getLine(line).slice(0, cursor);
362 | words += last_word;
363 |
364 | data = __async(this, null, function*() {
365 |
366 | let data = yield senGenerate(__apiUrl + 'generate', words, __article_type, 1, 10, false);
367 | if (data.length > 0) {
368 | words += data[0];
369 | const replacement = getSuggestionReplacement(data[0]);
370 | const endPos = __end;
371 | __activeView.editor.replaceRange(replacement, endPos, __spreadProps(__spreadValues({}, endPos), {
372 | ch: __end.ch
373 | }));
374 | __activeView.editor.setCursor(__spreadProps(__spreadValues({}, endPos), { ch: endPos.ch + replacement.length }));
375 |
376 | if (j > 0) {
377 | setTimeout(function(j, n) {
378 | auto_write(j - 1, n);
379 | }, Math.max(1400, 3000 - new Date().getTime() - __time), j, n);
380 |
381 | } else {
382 | bar_text("Done! 🍀");
383 | }
384 | }
385 | });
386 |
387 | } else {
388 | bar_text('Please input some words and press hot-keys to generate first .🆘');
389 | }
390 | }
391 |
392 | //生成候选项
393 | var SuggestionPopup = class extends import_obsidian3.EditorSuggest {
394 | constructor(app, settings, snippetManager) {
395 | var _a;
396 | super(app);
397 | this.disableSnippets = (_a = app.vault.config) == null ? void 0 : _a.legacyEditor;
398 | this.settings = settings;
399 | this.snippetManager = snippetManager;
400 | this.word = '';
401 | }
402 | getSuggestions() {
403 |
404 | __activeView = this.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView);
405 | let words= __activeView.editor.getSelection();
406 | console.log(words)
407 | if (words.length == 0) {
408 | __end = this.context.end;
409 | let line = this.context.end.line;
410 | let cursor = this.context.editor.getLine(line).length;
411 | for (var i = line - 3; i < line; i++) {
412 | if (i >= 0) {
413 | words += this.context.editor.getLine(i) + '\n';
414 | }
415 | }
416 | let last_word = this.context.editor.getLine(line).slice(0, cursor);
417 | words += last_word;
418 | }
419 |
420 | this.word = words;
421 | __apiUrl = this.settings.apiUrl;
422 | bar_text("Loading...🍋")
423 | return __async(this, null, function*() {
424 | return yield senGenerate(__apiUrl + 'generate', this.word, __article_type, this.settings.chioceNumber, this.settings.maxLength, __isIndex);
425 | });
426 | }
427 | onTrigger(cursor, editor, file) {
428 | console.log('onTrigger',this.justClosed);
429 | if (this.justClosed) {
430 |
431 | this.justClosed = false;
432 | return null;
433 | }
434 | if (__show == true) {
435 | let query = '';
436 | __show = false;
437 | return {
438 | start: __spreadProps(__spreadValues({}, cursor), {
439 | ch: cursor.ch - query.length
440 | }),
441 | end: cursor,
442 | query
443 | };
444 |
445 | }
446 | }
447 | renderSuggestion(value, el) {
448 | el.addClass("sengener-suggestion-item");
449 | bar_text("Done! 🍀");
450 | el.setText(getSuggestionDisplayName(value));
451 | }
452 |
453 | selectSuggestion(value, evt) {
454 | console.log('asdasd')
455 |
456 | const activeView = this.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView);
457 |
458 | const replacement = getSuggestionReplacement(value);
459 |
460 | const endPos = __end;
461 | activeView.editor.replaceRange(replacement, endPos, __spreadProps(__spreadValues({}, endPos), {
462 | ch: Math.min(endPos.ch, activeView.editor.getLine(endPos.line).length)
463 | }));
464 | if (replacement.contains("#") || replacement.contains("~")) {
465 | if (!this.disableSnippets) {
466 | this.snippetManager.handleSnippet(replacement, endPos, activeView.editor);
467 | } else {
468 | console.log("SenGener: Please enable Live Preview mode to use snippets");
469 | }
470 | } else {
471 | activeView.editor.setCursor(__spreadProps(__spreadValues({}, endPos), { ch: endPos.ch + replacement.length }));
472 | }
473 | this.close();
474 | this.justClosed = true;
475 | }
476 | selectNextItem(dir) {
477 | const self = this;
478 | self.suggestions.setSelectedItem(self.suggestions.selectedItem + dir, true);
479 | }
480 | getSelectedItem() {
481 | console.log('asdzzzdasd')
482 |
483 | const self = this;
484 | return self.suggestions.values[self.suggestions.selectedItem];
485 | }
486 | applySelectedItem() {
487 | const self = this;
488 | self.suggestions.useSelectedItem();
489 | }
490 | isVisible() {
491 | return this.isOpen;
492 | }
493 | preventNextTrigger() {
494 | console.log('rktyfdf')
495 |
496 | this.justClosed = true;
497 | }
498 | };
499 |
500 | // 设置内容,apiUrl:API服务地址,chioceNumber:选项数量
501 | var import_obsidian4 = require("obsidian");
502 | var SenGenerSettingsTab = class extends import_obsidian4.PluginSettingTab {
503 | constructor(app, plugin) {
504 | super(app, plugin);
505 | this.plugin = plugin;
506 | }
507 | display() {
508 | const { containerEl } = this;
509 | containerEl.empty();
510 | containerEl.createEl("h2", { text: "Settings for Writting Assistant." });
511 |
512 | containerEl.createDiv("content", (el) => {
513 | obsidian.MarkdownRenderer.renderMarkdown(__cn_note, el, '', this);
514 | });
515 |
516 | new import_obsidian4.Setting(containerEl).setName("API Address").setDesc("The service address for generating sentenses.\r\n\
517 | The default address is https://fwzd.myfawu.com/").addText((text) => text.setValue(this.plugin.settings.apiUrl)
518 | .onChange(
519 | (val) => __async(this, null, function*() {
520 | try {
521 | text.inputEl.removeClass("SenGener-settings-error");
522 | this.plugin.settings.apiUrl = val;
523 | yield this.plugin.saveSettings();
524 | } catch (e) {
525 | text.inputEl.addClass("SenGener-settings-error");
526 | }
527 | })));
528 |
529 | new import_obsidian4.Setting(containerEl).setName("token").setDesc("You can apply for your personal token for better experience, visit https://fwzd.myfawu.com/my/")
530 | .addText((text) => text.setValue(this.plugin.settings.token)
531 | .onChange(
532 | (val) => __async(this, null, function*() {
533 | try {
534 | text.inputEl.removeClass("SenGener-settings-error");
535 | this.plugin.settings.token = val;
536 | __token = val;
537 | yield this.plugin.saveSettings();
538 | } catch (e) {
539 | text.inputEl.addClass("SenGener-settings-error");
540 | }
541 | })));
542 |
543 | new import_obsidian4.Setting(containerEl).setName("Enable searching?").setDesc("Enable Text searching service").addToggle((toggle) => {
544 | toggle.setValue(this.plugin.settings.isIndex);
545 | toggle.onChange((value) => __async(this, null, function*() {
546 | __isIndex = value;
547 | this.plugin.settings.isIndex = value;
548 | yield this.plugin.saveSettings();
549 | }));
550 | });
551 |
552 | new import_obsidian4.Setting(containerEl).setName("Number of choices").setDesc(" Number of generated sentences(1-9)")
553 | .addText((text) => {
554 | text.inputEl.type = "number";
555 | text.setValue(this.plugin.settings.chioceNumber + "").onChange((val) => __async(this, null, function*() {
556 | if (!val || val < 1 || val > 9)
557 | return;
558 | this.plugin.settings.chioceNumber = parseInt(val);
559 | yield this.plugin.saveSettings();
560 | }));
561 | });
562 |
563 | new import_obsidian4.Setting(containerEl).setName("max length").setDesc(" Max words of generated sentences(5-50)")
564 | .addText((text) => {
565 | text.inputEl.type = "number";
566 | text.setValue(this.plugin.settings.maxLength + "").onChange((val) => __async(this, null, function*() {
567 | if (!val || val < 5 || val > 50)
568 | return;
569 | this.plugin.settings.maxLength = parseInt(val);
570 | yield this.plugin.saveSettings();
571 | }));
572 | });
573 |
574 | new import_obsidian4.Setting(containerEl).setName("Type").setDesc("Type of what you are writting")
575 | .addDropdown((dropdown) => dropdown.addOptions(__article_types)
576 | .setValue(this.plugin.settings.className).onChange((value) => __async(this, null, function*() {
577 | __article_type = value;
578 | this.plugin.settings.className = value;
579 | yield this.plugin.saveSettings();
580 | }))
581 | );
582 | }
583 |
584 | createEnabledSetting(propertyName, desc, container) {
585 | new import_obsidian4.Setting(container).setName("Enabled").setDesc(desc).addToggle((toggle) => toggle.setValue(this.plugin.settings[propertyName])
586 | .onChange((val) => __async(this, null, function*() {
587 | this.plugin.settings[propertyName] = val;
588 | yield this.plugin.saveSettings();
589 | })));
590 | }
591 | };
592 |
593 | // 设置操作快捷键
594 | var SenGenerPlugin = class extends import_obsidian5.Plugin {
595 | constructor() {
596 | super(...arguments);
597 |
598 | }
599 |
600 | async activateView() {
601 | if (this.app.workspace.getLeavesOfType(QUOTE_VIEW_TYPE).length)
602 | return;
603 | this.app.workspace.getRightLeaf(false).setViewState({
604 | type: QUOTE_VIEW_TYPE,
605 | active: true,
606 | });
607 | }
608 | onload() {
609 | this.registerView(QUOTE_VIEW_TYPE, (leaf) => {
610 | const newView = new QUOTEListView(leaf, this);
611 | return newView;
612 | });
613 |
614 | setTimeout(() => { this.activateView(); }, 2000);
615 | return __async(this, null, function*() {
616 |
617 | var _a;
618 | yield this.loadSettings();
619 | this.snippetManager = new SnippetManager();
620 | this._suggestionPopup = new SuggestionPopup(this.app, this.settings, this.snippetManager);
621 | this.registerEditorSuggest(this._suggestionPopup);
622 | this.addSettingTab(new SenGenerSettingsTab(this.app, this));
623 | this.setupCommands();
624 |
625 | });
626 | }
627 | setupCommands() {
628 | const app = this.app;
629 | app.scope.keys = [];
630 | const isHotkeyMatch = (hotkey, context, id) => {
631 | const modifiers = hotkey.modifiers,
632 | key = hotkey.key;
633 | if (modifiers !== null && (id.contains("sengener-bypass") ? !context.modifiers.contains(modifiers) : modifiers !== context.modifiers))
634 | return false;
635 | return !key || (key === context.vkey || !(!context.key || key.toLowerCase() !== context.key.toLowerCase()));
636 | };
637 | this.app.scope.register(null, null, (e, t) => {
638 | const hotkeyManager = app.hotkeyManager;
639 | hotkeyManager.bake();
640 |
641 | for (let bakedHotkeys = hotkeyManager.bakedHotkeys, bakedIds = hotkeyManager.bakedIds, r = 0; r < bakedHotkeys.length; r++) {
642 | const hotkey = bakedHotkeys[r];
643 | const id = bakedIds[r];
644 | if (isHotkeyMatch(hotkey, t, id)) {
645 | const command = app.commands.findCommand(id);
646 | if (!command || e.repeat && !command.repeatable) {
647 | return false;
648 | } else if (id.contains("sengener-bypass")) {
649 | this._suggestionPopup.close();
650 | const validMods = t.modifiers.replace(new RegExp(`${hotkey.modifiers},*`), "").split(",");
651 | let event = new KeyboardEvent("keydown", {
652 | key: hotkeyManager.defaultKeys[id][0].key,
653 | ctrlKey: validMods.contains("Ctrl"),
654 | shiftKey: validMods.contains("Shift"),
655 | altKey: validMods.contains("Alt"),
656 | metaKey: validMods.contains("Meta")
657 | });
658 | e.target.dispatchEvent(event);
659 | console.log("Hotkey " + id + " entered")
660 | return false;
661 | }
662 | if (app.commands.executeCommandById(id))
663 | return false;
664 | } else {
665 | this.justClosed = true;
666 | }
667 | }
668 | });
669 | //快捷键1-escape
670 | this.addCommand({
671 | id: "escape-popup",
672 | name: "escape-popup",
673 | hotkeys: [{
674 | key: "Escape",
675 | modifiers: []
676 | }],
677 | editorCallback: () => {},
678 | isVisible: () => this._suggestionPopup.isVisible()
679 | });
680 | //快捷键2-suggestion
681 | this.addCommand({
682 | id: "key-to-suggestion",
683 | name: "key-to-suggestion",
684 | hotkeys: [{
685 | key: "'",
686 | modifiers: ["Ctrl"]
687 | }],
688 | editorCallback: (editor) => {
689 | __show = true;
690 | this._suggestionPopup.trigger(editor, '', true);
691 | },
692 | isVisible: () => this._suggestionPopup.isVisible()
693 | });
694 | //快捷键3-switch-model
695 | this.addCommand({
696 | id: "key-to-switch-model",
697 | name: "key-to-switch-model",
698 | hotkeys: [{
699 | key: "]",
700 | modifiers: ["Alt"]
701 | }],
702 | editorCallback: (editor) => {
703 | if (__article_types == null) {
704 | get_article_type();
705 | setTimeout(() => {
706 | try {
707 | __article_type = Object.keys(__article_types)[0];
708 | bar_text("switch to " + __article_type + "🍀", 0);
709 | } catch (e) {
710 | __article_type = 'Null';
711 | bar_text("No model found, 💔", 0);
712 | }
713 | }, 1000);
714 |
715 | } else {
716 | if (__article_types.hasOwnProperty(__article_type) == false) {
717 | __article_type = Object.keys(__article_types)[0];
718 | } else if (Object.keys(__article_types).indexOf(__article_type) + 1 >= Object.keys(__article_types).length) {
719 | __article_type = Object.keys(__article_types)[0];
720 | } else {
721 | __article_type = Object.keys(__article_types)[Object.keys(__article_types).indexOf(__article_type) + 1];
722 | }
723 | bar_text("switch to " + __article_type + "🍀", 0);
724 | }
725 | },
726 | isVisible: () => this._suggestionPopup.isVisible()
727 | });
728 | }
729 |
730 |
731 | onunload() {
732 | return __async(this, null, function*() {
733 | // this.snippetManager.onunload();
734 | // newview.onunload();
735 | });
736 | }
737 | loadSettings() {
738 | return __async(this, null, function*() {
739 | this.settings = Object.assign({}, DEFAULT_SETTINGS, yield this.loadData());
740 |
741 | });
742 | }
743 | get suggestionPopup() {
744 | return this._suggestionPopup;
745 | }
746 | saveSettings() {
747 | return __async(this, null, function*() {
748 | yield this.saveData(this.settings);
749 | });
750 | }
751 | };
752 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "obsidian-SenGener",
3 | "name": "obsidian-SenGener",
4 | "version": "1.1.1",
5 | "minAppVersion": "1.0.0",
6 | "description": "This plugin can generate sentenses for writting with the help of API",
7 | "author": "zazaji",
8 | "authorUrl": "https://github.com/zazaji",
9 | "isDesktopOnly": false
10 | }
--------------------------------------------------------------------------------
/obsidian-sengener/11.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zazaji/obsidian-SenGener/4d1317b047d0636fd5838c3ca0ddd96dab8d5a55/obsidian-sengener/11.gif
--------------------------------------------------------------------------------
/obsidian-sengener/48c10a17095bc84a8939d4a7ac2326e.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zazaji/obsidian-SenGener/4d1317b047d0636fd5838c3ca0ddd96dab8d5a55/obsidian-sengener/48c10a17095bc84a8939d4a7ac2326e.jpg
--------------------------------------------------------------------------------
/obsidian-sengener/_english_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zazaji/obsidian-SenGener/4d1317b047d0636fd5838c3ca0ddd96dab8d5a55/obsidian-sengener/_english_demo.gif
--------------------------------------------------------------------------------
/obsidian-sengener/_new_demo_en.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zazaji/obsidian-SenGener/4d1317b047d0636fd5838c3ca0ddd96dab8d5a55/obsidian-sengener/_new_demo_en.gif
--------------------------------------------------------------------------------
/obsidian-sengener/b3df27009a29d0d01a8234967235832.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zazaji/obsidian-SenGener/4d1317b047d0636fd5838c3ca0ddd96dab8d5a55/obsidian-sengener/b3df27009a29d0d01a8234967235832.jpg
--------------------------------------------------------------------------------
/obsidian-sengener/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "apiUrl": "https://fwzd.myfawu.com/",
3 | "chioceNumber": 3,
4 | "className": "article",
5 | "isIndex": true,
6 | "token": "123456",
7 | "cnNote": "Writting assistant: Enjoy writing, full-text search and text-generation supported.
写作助手:人工智能技术,让你文思如泉涌,写文章、作诗、报告纵享丝滑。可定制自己的专属模型。mailto:zazaji@sina.com"
8 |
9 | }
--------------------------------------------------------------------------------
/obsidian-sengener/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zazaji/obsidian-SenGener/4d1317b047d0636fd5838c3ca0ddd96dab8d5a55/obsidian-sengener/demo.gif
--------------------------------------------------------------------------------
/obsidian-sengener/demo_cn.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zazaji/obsidian-SenGener/4d1317b047d0636fd5838c3ca0ddd96dab8d5a55/obsidian-sengener/demo_cn.gif
--------------------------------------------------------------------------------
/obsidian-sengener/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | # SenGenerPlugin for obsidian
3 | This plugin is used to generate a serial of Sentenses for writting.
4 | First , You need to build your API service for generating Sentenses.
5 | I build the API service with GPT2. You can also use GPT2 to generate Sentenses directly , or Another one.
6 |
7 | Author:https://github.com/zazaji
8 | Thanks: https://github.com/tth05/obsidian-completr
9 | */
10 | var __show = false;
11 | var __end;
12 | var __apiUrl;
13 | // var __apiUrl2;
14 | var __activeView;
15 | var __article_types;
16 | var __max_length = 100;
17 | var __token;
18 | var __isIndex;
19 | var __cn_note;
20 | var __time = new Date().getTime() - 2000;
21 | var __article_type;
22 | const QUOTE_VIEW_TYPE = "QUOTE";
23 | var BARCONTAINER;
24 | var statusBarItem;
25 | var __defProp = Object.defineProperty;
26 | var __defProps = Object.defineProperties;
27 | var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
28 | var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
29 | var __getOwnPropNames = Object.getOwnPropertyNames;
30 | var __getOwnPropSymbols = Object.getOwnPropertySymbols;
31 | var __hasOwnProp = Object.prototype.hasOwnProperty;
32 | var __propIsEnum = Object.prototype.propertyIsEnumerable;
33 | var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
34 | var __spreadValues = (a, b) => {
35 | for (var prop in b || (b = {}))
36 | if (__hasOwnProp.call(b, prop))
37 | __defNormalProp(a, prop, b[prop]);
38 | if (__getOwnPropSymbols)
39 | for (var prop of __getOwnPropSymbols(b)) {
40 | if (__propIsEnum.call(b, prop))
41 | __defNormalProp(a, prop, b[prop]);
42 | }
43 | return a;
44 | };
45 | var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
46 | var __export = (target, all) => {
47 | for (var name in all)
48 | __defProp(target, name, { get: all[name], enumerable: true });
49 | };
50 | var __copyProps = (to, from, except, desc) => {
51 | if (from && typeof from === "object" || typeof from === "function") {
52 | for (let key of __getOwnPropNames(from))
53 | if (!__hasOwnProp.call(to, key) && key !== except)
54 | __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
55 | }
56 | return to;
57 | };
58 | var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
59 | var __async = (__this, __arguments, generator) => {
60 | return new Promise((resolve, reject) => {
61 | var fulfilled = (value) => {
62 | try {
63 | step(generator.next(value));
64 | } catch (e) {
65 | reject(e);
66 | }
67 | };
68 | var rejected = (value) => {
69 | try {
70 | step(generator.throw(value));
71 | } catch (e) {
72 | reject(e);
73 | }
74 | };
75 | var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
76 | step((generator = generator.apply(__this, __arguments)).next());
77 | });
78 | };
79 |
80 | // src/main.ts
81 | var main_exports = {};
82 | __export(main_exports, {
83 | default: () => SenGenerPlugin
84 | });
85 | module.exports = __toCommonJS(main_exports);
86 | var import_obsidian5 = require("obsidian");
87 |
88 | // src/snippet_manager.ts
89 | var SnippetManager = class {
90 | constructor() {
91 | this.currentPlaceholderReferences = [];
92 | }
93 | onunload() {
94 | this.clearAllPlaceholders();
95 | }
96 | clearAllPlaceholders() {
97 | if (this.currentPlaceholderReferences.length === 0)
98 | return;
99 | const firstRef = this.currentPlaceholderReferences[0];
100 | const view = editorToCodeMirrorView(firstRef.editor);
101 | view.dispatch({
102 | effects: clearMarks.of(null)
103 | });
104 | this.currentPlaceholderReferences = [];
105 | }
106 | };
107 |
108 | // src/provider/provider.ts
109 | function getSuggestionDisplayName(suggestion, lowerCase = false) {
110 | const res = typeof suggestion === "string" ? suggestion : suggestion.displayName;
111 | return lowerCase ? res.toLowerCase() : res;
112 | }
113 |
114 | function content_text(text) {
115 | BARCONTAINER.children[5].empty();
116 | BARCONTAINER.children[5].createDiv('ssmall markdown-preview-view', (el) => {
117 | obsidian.MarkdownRenderer.renderMarkdown(text, el, '', this);
118 | });
119 | }
120 |
121 | //根据总页数和当前页分页
122 | function pagebar(n, m) {
123 | BARCONTAINER.children[4].empty();
124 | start = ((n > 5) && (m - 2 > 1)) ? m - 2 : 1;
125 | start = ((n <= 5) || (start + 4 < n)) ? start : n - 4;
126 | end = start + 4 > n ? ((m + 2 <= n) && ((n > 5)) ? m + 2 : n) : start + 4;
127 | if (1 < start) {
128 | BARCONTAINER.children[4].createEl("a", { text: "<<" }, (el) => {
129 | el.onClickEvent(() => {
130 | searchTerm(start - 3, __article_type);
131 | })
132 | })
133 | }
134 | for (let i = start; i <= end; i++) {
135 | if (m != i) {
136 | BARCONTAINER.children[4].createEl("a", { text: i }, (el) => {
137 | el.onClickEvent(() => {
138 | searchTerm(i, __article_type);
139 | })
140 | })
141 | } else {
142 | BARCONTAINER.children[4].createEl("a", { text: i, cls: "active" });
143 | }
144 | }
145 |
146 | if (n > end) {
147 | BARCONTAINER.children[4].createEl("a", { text: ">>" }, (el) => {
148 | el.onClickEvent(() => {
149 | searchTerm(end + 3, __article_type);
150 | })
151 | })
152 | }
153 | }
154 |
155 | function getSuggestionReplacement(suggestion) {
156 | return typeof suggestion === "string" ? suggestion : suggestion.replacement;
157 | }
158 | //状态栏信息提示
159 | function bar_text(text, timeout = 10000) {
160 | statusBarItem.empty();
161 | statusBarItem.createEl("span", { text: text });
162 | if (timeout > 0) {
163 | setTimeout(() => { statusBarItem.empty(); }, timeout);
164 | }
165 | return '';
166 | }
167 | //
168 | // src/settings.ts
169 | var DEFAULT_SETTINGS = {
170 | // apiUrl: "https://transformer.huggingface.co/autocomplete/",
171 | apiUrl: "https://fwzd.myfawu.com/",
172 | // apiUrl2: "https://fwzd.myfawu.com/refer/",
173 | chioceNumber: 3,
174 | maxLength: 20,
175 | className: "poem",
176 | isIndex: false
177 | }
178 |
179 |
180 | //全文检索函数
181 | async function searchTerm(page, article_type) {
182 | let idata = {
183 | "context": BARCONTAINER.children[2].value,
184 | 'page': page,
185 | 'token': __token,
186 | 'article_type': article_type,
187 | };
188 | bar_text("Loading...🍋");
189 | try {
190 | let res = await fetch(__apiUrl + 'refer', {
191 | method: "post",
192 | headers: {
193 | "content-type": "application/json"
194 | },
195 | body: JSON.stringify(idata)
196 | });
197 | let data = await res.json();
198 | if (data != '') {
199 |
200 | let text = data['ref'].map(function(item) { return ' -' + item['title'] + '
' + item['content'] + '' }).join('
');
201 | bar_text("Done! 🍀");
202 | pagebar(data['page'], page);
203 | content_text(text);
204 | }
205 | } catch (e) {
206 | console.log(e);
207 | return bar_text("Failed. 🆘");
208 | }
209 |
210 | }
211 |
212 | //获取功能列表
213 | async function get_article_type() {
214 | try {
215 | let res = await fetch(__apiUrl + 'func');
216 | let data = await res.json();
217 | if (data != '') {
218 | __article_types = data;
219 | if (__article_types.hasOwnProperty(__article_type) == false) {
220 | __article_type = Object.keys(__article_types)[0];
221 | }
222 | bar_text("Loading " + __article_type + "🍀");
223 | }
224 | } catch (e) {
225 | console.log(e);
226 | return bar_text("Failed. 🆘");
227 | }
228 | }
229 |
230 | //生成句子函数
231 | async function senGenerate(url, text, atype, number, max_length, isindex = false) {
232 | console.log(new Date().getTime() - __time);
233 | if (new Date().getTime() - __time < 1.5 * 1000) {
234 | return bar_text("Request slowly. 🆘");
235 | }
236 | __time = new Date().getTime();
237 | let idata = {
238 | "context": text,
239 | 'token': __token,
240 | "model_size": "distilgpt2/small",
241 | "article_type": atype,
242 | "top_p": 0.9,
243 | "temperature": 1,
244 | "max_time": 1.2,
245 | "max_length": max_length,
246 | "is_index": isindex,
247 | "number": number
248 | };
249 | try {
250 | let res = await fetch(url, {
251 | method: "post",
252 | headers: {
253 | "content-type": "application/json"
254 | },
255 | body: JSON.stringify(idata)
256 | });
257 | let data = await res.json();
258 | if (data != '') {
259 | if (isindex) {
260 | BARCONTAINER.children[2].value = data['keywords'];
261 | let text = data['ref'].map(function(item) { return '' + item['title'] + '
' + item['content'] + '' }).join('
');
262 | pagebar(data['page'], 1);
263 | content_text(text);
264 | }
265 | console.log(data['sentences']);
266 | return data['sentences'].map(function(item) { return item['value'] })
267 | } else {
268 | return [];
269 | }
270 | } catch (e) {
271 | console.log(e);
272 | return bar_text("Failed. 🆘");
273 | }
274 | }
275 | //🥔🍡🍧🐬🍫🌒🥓🦑🦃🍋🐌🦂🥛🍔🐔🍘💐🌔🍺🍙🐡🦞🐋🦚🦀🌱🍐🥥🎂🐠🍕💚💞🥕🍨🍇🆖
276 | //💟🍭🍢🥀🥑🍋🌺🍤🐤🍟🍁🐝🥬🛶🌳🌖🍝🌼🧂🥤🍱🍿🍩🦜⏬🆘🥠💛🐦🍗🌰🌭🧁🫒🐙🦆🃏❗
277 | //🥜🌶💓🐥🥣⏰🍵💗🍸🧆🌚🍄🌝🍣🍹🌐🦠🎾🍈🫐🦩🦉⛔🍖🥡🌌🕷🐟🆓🍮🐓💕️🦴🍼🍛🐳🦗⛅
278 | //💙❤🍓🏕🌞🌗🪴🦢🌓🧅🌮🌁🍒🍊🐚🌏🌛🍏🦋⏳🍚🍠🌸🌋🦐🧄🐜🌕🍻🥝🐧🍯⚘🍪🥫🍾❣🥮🌴💔
279 | //🍂🐛🌵🌍🧀🥨🐾🥂🍆🌽🌯🦪🥩🥧🌠🍀🥙🥘🍃🍍🥒⏫🍅🥞🍦🍰🥟🦅🍷💜❓🍲🥪.🍀🍉💘🌉
280 | //🧃💮🦟🧇🥚🍥🍳🍶🥗🎍🦈🍴🥯🍽🕸🌿➰🆑🥭🌘🌜🍜💖🌲🐣🐞🍑☕🍬🌎🥖🍞🆘💝🥐🧈
281 | var obsidian = require('obsidian');
282 | // 添加右侧栏
283 | var import_obsidian3 = require("obsidian");
284 | // 初始化右侧栏
285 | class QUOTEListView extends obsidian.ItemView {
286 | constructor(leaf, plugin) {
287 | super(leaf);
288 | this.plugin = plugin;
289 | this.lastRerender = 0;
290 | this.groupedItems = [];
291 | this.itemsByFile = new Map();
292 | this.initialLoad = true;
293 | this.searchTerm = "";
294 | }
295 | getViewType() {
296 | return QUOTE_VIEW_TYPE;
297 | }
298 | getDisplayText() {
299 | return "QUOTE List";
300 | }
301 | getIcon() {
302 | return 'list';
303 | }
304 | async onload() {
305 | setTimeout(() => {
306 | __article_type = this.plugin.settings.className;
307 | __apiUrl = this.plugin.settings.apiUrl;
308 | __token = this.plugin.settings.token;
309 | __isIndex = this.plugin.settings.isIndex;
310 | __cn_note = this.plugin.settings.cnNote;
311 | __max_length = this.plugin.settings.max_length;
312 | get_article_type();
313 | }, 3000);
314 | statusBarItem = this.plugin.addStatusBarItem();
315 |
316 | }
317 | // 右侧栏
318 | async onOpen() {
319 | BARCONTAINER = this.containerEl.children[1];
320 | // BARCONTAINER = container;
321 | BARCONTAINER.empty();
322 | BARCONTAINER.createEl("h4", { text: "Writting Assistant", cls: 'col-12' });
323 | BARCONTAINER.createEl("button", { text: "Auto", type: 'button', cls: 'col-2' }, (el) => {
324 | el.onClickEvent(() => {
325 | bar_text("Loading...🍋 ");
326 | auto_write(7, 7);
327 | });
328 | });
329 |
330 | BARCONTAINER.createEl("input", { value: "", type: 'text', cls: 'col-6', placeholder: 'Please input keywords' });
331 | BARCONTAINER.createEl("button", { text: "🍳", type: 'button', cls: 'col-2' }, (el) => {
332 | el.onClickEvent(() => {
333 | __apiUrl = this.plugin.settings.apiUrl;
334 | searchTerm(1, __article_type);
335 | })
336 | });
337 | BARCONTAINER.createDiv("pagination");
338 | BARCONTAINER.createDiv("content", (el) => {
339 | obsidian.MarkdownRenderer.renderMarkdown('', el, '', this);
340 | });
341 | }
342 |
343 | async onClose() {
344 | // Nothing to clean up.
345 | }
346 | }
347 |
348 |
349 | //生成句子函数
350 | function auto_write(j, n) {
351 | if (__activeView) {
352 | let line = __activeView.editor.lastLine();
353 | let cursor = __activeView.editor.getLine(line).length;
354 | __end.ch = cursor;
355 | __end.line = line;
356 | let words = ''
357 | for (var i = line - 3; i < line; i++) {
358 | if (i >= 0) {
359 | words += __activeView.editor.getLine(i) + '\n';
360 | }
361 | }
362 | let last_word = __activeView.editor.getLine(line).slice(0, cursor);
363 | words += last_word;
364 |
365 | data = __async(this, null, function*() {
366 |
367 | let data = yield senGenerate(__apiUrl + 'generate', words, __article_type, 1, 10, false);
368 | if (data.length > 0) {
369 | words += data[0];
370 | const replacement = getSuggestionReplacement(data[0]);
371 | const endPos = __end;
372 | __activeView.editor.replaceRange(replacement, endPos, __spreadProps(__spreadValues({}, endPos), {
373 | ch: __end.ch
374 | }));
375 | __activeView.editor.setCursor(__spreadProps(__spreadValues({}, endPos), { ch: endPos.ch + replacement.length }));
376 |
377 | if (j > 0) {
378 | setTimeout(function(j, n) {
379 | auto_write(j - 1, n);
380 | }, Math.max(1400, 3000 - new Date().getTime() - __time), j, n);
381 |
382 | } else {
383 | bar_text("Done! 🍀");
384 | }
385 | }
386 | });
387 |
388 | } else {
389 | bar_text('Please input some words and press hot-keys to generate first .🆘');
390 | }
391 | }
392 |
393 | //生成候选项
394 | var SuggestionPopup = class extends import_obsidian3.EditorSuggest {
395 | constructor(app, settings, snippetManager) {
396 | var _a;
397 | super(app);
398 | this.disableSnippets = (_a = app.vault.config) == null ? void 0 : _a.legacyEditor;
399 | this.settings = settings;
400 | this.snippetManager = snippetManager;
401 | this.word = '';
402 | }
403 | getSuggestions() {
404 | __activeView = this.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView);
405 | __end = this.context.end;
406 |
407 | let line = this.context.end.line;
408 | let cursor = this.context.editor.getLine(line).length;
409 | let words = ''
410 | for (var i = line - 3; i < line; i++) {
411 | if (i >= 0) {
412 | words += this.context.editor.getLine(i) + '\n';
413 | }
414 | }
415 | let last_word = this.context.editor.getLine(line).slice(0, cursor);
416 | words += last_word;
417 | this.word = words;
418 | __apiUrl = this.settings.apiUrl;
419 | bar_text("Loading...🍋")
420 | return __async(this, null, function*() {
421 | return yield senGenerate(__apiUrl + 'generate', this.word, __article_type, this.settings.chioceNumber, this.settings.maxLength, __isIndex);
422 | });
423 | }
424 | onTrigger(cursor, editor, file) {
425 | if (this.justClosed) {
426 | this.justClosed = false;
427 | return null;
428 | }
429 | if (__show == true) {
430 | let query = '';
431 | __show = false;
432 | return {
433 | start: __spreadProps(__spreadValues({}, cursor), {
434 | ch: cursor.ch - query.length
435 | }),
436 | end: cursor,
437 | query
438 | };
439 |
440 | }
441 | }
442 | renderSuggestion(value, el) {
443 | el.addClass("sengener-suggestion-item");
444 | bar_text("Done! 🍀");
445 | el.setText(getSuggestionDisplayName(value));
446 | }
447 |
448 | selectSuggestion(value, evt) {
449 | const activeView = this.app.workspace.getActiveViewOfType(import_obsidian3.MarkdownView);
450 |
451 | const replacement = getSuggestionReplacement(value);
452 |
453 | const endPos = __end;
454 | activeView.editor.replaceRange(replacement, endPos, __spreadProps(__spreadValues({}, endPos), {
455 | ch: Math.min(endPos.ch, activeView.editor.getLine(endPos.line).length)
456 | }));
457 | if (replacement.contains("#") || replacement.contains("~")) {
458 | if (!this.disableSnippets) {
459 | this.snippetManager.handleSnippet(replacement, endPos, activeView.editor);
460 | } else {
461 | console.log("SenGener: Please enable Live Preview mode to use snippets");
462 | }
463 | } else {
464 | activeView.editor.setCursor(__spreadProps(__spreadValues({}, endPos), { ch: endPos.ch + replacement.length }));
465 | }
466 | this.close();
467 | this.justClosed = true;
468 | }
469 | selectNextItem(dir) {
470 | const self = this;
471 | self.suggestions.setSelectedItem(self.suggestions.selectedItem + dir, true);
472 | }
473 | getSelectedItem() {
474 | const self = this;
475 | return self.suggestions.values[self.suggestions.selectedItem];
476 | }
477 | applySelectedItem() {
478 | const self = this;
479 | self.suggestions.useSelectedItem();
480 | }
481 | isVisible() {
482 | return this.isOpen;
483 | }
484 | preventNextTrigger() {
485 | this.justClosed = true;
486 | }
487 | };
488 |
489 | // 设置内容,apiUrl:API服务地址,chioceNumber:选项数量
490 | var import_obsidian4 = require("obsidian");
491 | var SenGenerSettingsTab = class extends import_obsidian4.PluginSettingTab {
492 | constructor(app, plugin) {
493 | super(app, plugin);
494 | this.plugin = plugin;
495 | }
496 | display() {
497 | const { containerEl } = this;
498 | containerEl.empty();
499 | containerEl.createEl("h2", { text: "Settings for Writting Assistant." });
500 |
501 | containerEl.createDiv("content", (el) => {
502 | obsidian.MarkdownRenderer.renderMarkdown(__cn_note, el, '', this);
503 | });
504 |
505 | new import_obsidian4.Setting(containerEl).setName("API Address").setDesc("The service address for generating sentenses.\r\n\
506 | The default address is https://fwzd.myfawu.com/").addText((text) => text.setValue(this.plugin.settings.apiUrl)
507 | .onChange(
508 | (val) => __async(this, null, function*() {
509 | try {
510 | text.inputEl.removeClass("SenGener-settings-error");
511 | this.plugin.settings.apiUrl = val;
512 | yield this.plugin.saveSettings();
513 | } catch (e) {
514 | text.inputEl.addClass("SenGener-settings-error");
515 | }
516 | })));
517 |
518 | new import_obsidian4.Setting(containerEl).setName("token").setDesc("You can apply for your personal token for better experience, visit https://fwzd.myfawu.com/my/")
519 | .addText((text) => text.setValue(this.plugin.settings.token)
520 | .onChange(
521 | (val) => __async(this, null, function*() {
522 | try {
523 | text.inputEl.removeClass("SenGener-settings-error");
524 | this.plugin.settings.token = val;
525 | __token = val;
526 | yield this.plugin.saveSettings();
527 | } catch (e) {
528 | text.inputEl.addClass("SenGener-settings-error");
529 | }
530 | })));
531 |
532 | new import_obsidian4.Setting(containerEl).setName("Enable searching?").setDesc("Enable Text searching service").addToggle((toggle) => {
533 | toggle.setValue(this.plugin.settings.isIndex);
534 | toggle.onChange((value) => __async(this, null, function*() {
535 | __isIndex = value;
536 | this.plugin.settings.isIndex = value;
537 | yield this.plugin.saveSettings();
538 | }));
539 | });
540 |
541 | new import_obsidian4.Setting(containerEl).setName("Number of choices").setDesc(" Number of generated sentences(1-9)")
542 | .addText((text) => {
543 | text.inputEl.type = "number";
544 | text.setValue(this.plugin.settings.chioceNumber + "").onChange((val) => __async(this, null, function*() {
545 | if (!val || val < 1 || val > 9)
546 | return;
547 | this.plugin.settings.chioceNumber = parseInt(val);
548 | yield this.plugin.saveSettings();
549 | }));
550 | });
551 |
552 | new import_obsidian4.Setting(containerEl).setName("max length").setDesc(" Max words of generated sentences(5-50)")
553 | .addText((text) => {
554 | text.inputEl.type = "number";
555 | text.setValue(this.plugin.settings.maxLength + "").onChange((val) => __async(this, null, function*() {
556 | if (!val || val < 5 || val > 50)
557 | return;
558 | this.plugin.settings.maxLength = parseInt(val);
559 | yield this.plugin.saveSettings();
560 | }));
561 | });
562 |
563 | new import_obsidian4.Setting(containerEl).setName("Type").setDesc("Type of what you are writting")
564 | .addDropdown((dropdown) => dropdown.addOptions(__article_types)
565 | .setValue(this.plugin.settings.className).onChange((value) => __async(this, null, function*() {
566 | __article_type = value;
567 | this.plugin.settings.className = value;
568 | yield this.plugin.saveSettings();
569 | }))
570 | );
571 | }
572 |
573 | createEnabledSetting(propertyName, desc, container) {
574 | new import_obsidian4.Setting(container).setName("Enabled").setDesc(desc).addToggle((toggle) => toggle.setValue(this.plugin.settings[propertyName])
575 | .onChange((val) => __async(this, null, function*() {
576 | this.plugin.settings[propertyName] = val;
577 | yield this.plugin.saveSettings();
578 | })));
579 | }
580 | };
581 |
582 | // 设置操作快捷键
583 | var SenGenerPlugin = class extends import_obsidian5.Plugin {
584 | constructor() {
585 | super(...arguments);
586 |
587 | }
588 |
589 | async activateView() {
590 | if (this.app.workspace.getLeavesOfType(QUOTE_VIEW_TYPE).length)
591 | return;
592 | this.app.workspace.getRightLeaf(false).setViewState({
593 | type: QUOTE_VIEW_TYPE,
594 | active: true,
595 | });
596 | }
597 | onload() {
598 | this.registerView(QUOTE_VIEW_TYPE, (leaf) => {
599 | const newView = new QUOTEListView(leaf, this);
600 | return newView;
601 | });
602 |
603 | setTimeout(() => { this.activateView(); }, 2000);
604 | return __async(this, null, function*() {
605 |
606 | var _a;
607 | yield this.loadSettings();
608 | this.snippetManager = new SnippetManager();
609 | this._suggestionPopup = new SuggestionPopup(this.app, this.settings, this.snippetManager);
610 | this.registerEditorSuggest(this._suggestionPopup);
611 | this.addSettingTab(new SenGenerSettingsTab(this.app, this));
612 | this.setupCommands();
613 |
614 | });
615 | }
616 | setupCommands() {
617 | const app = this.app;
618 | app.scope.keys = [];
619 | const isHotkeyMatch = (hotkey, context, id) => {
620 | const modifiers = hotkey.modifiers,
621 | key = hotkey.key;
622 | if (modifiers !== null && (id.contains("sengener-bypass") ? !context.modifiers.contains(modifiers) : modifiers !== context.modifiers))
623 | return false;
624 | return !key || (key === context.vkey || !(!context.key || key.toLowerCase() !== context.key.toLowerCase()));
625 | };
626 | this.app.scope.register(null, null, (e, t) => {
627 | const hotkeyManager = app.hotkeyManager;
628 | hotkeyManager.bake();
629 |
630 | for (let bakedHotkeys = hotkeyManager.bakedHotkeys, bakedIds = hotkeyManager.bakedIds, r = 0; r < bakedHotkeys.length; r++) {
631 | const hotkey = bakedHotkeys[r];
632 | const id = bakedIds[r];
633 | if (isHotkeyMatch(hotkey, t, id)) {
634 | const command = app.commands.findCommand(id);
635 | if (!command || e.repeat && !command.repeatable) {
636 | return false;
637 | } else if (id.contains("sengener-bypass")) {
638 | this._suggestionPopup.close();
639 | const validMods = t.modifiers.replace(new RegExp(`${hotkey.modifiers},*`), "").split(",");
640 | let event = new KeyboardEvent("keydown", {
641 | key: hotkeyManager.defaultKeys[id][0].key,
642 | ctrlKey: validMods.contains("Ctrl"),
643 | shiftKey: validMods.contains("Shift"),
644 | altKey: validMods.contains("Alt"),
645 | metaKey: validMods.contains("Meta")
646 | });
647 | e.target.dispatchEvent(event);
648 | console.log("Hotkey " + id + " entered")
649 | return false;
650 | }
651 | if (app.commands.executeCommandById(id))
652 | return false;
653 | } else {
654 | this.justClosed = true;
655 | }
656 | }
657 | });
658 | //快捷键1
659 | this.addCommand({
660 | id: "escape-popup",
661 | name: "escape-popup",
662 | hotkeys: [{
663 | key: "Escape",
664 | modifiers: []
665 | }],
666 | editorCallback: () => {},
667 | isVisible: () => this._suggestionPopup.isVisible()
668 | });
669 | //快捷键2
670 | this.addCommand({
671 | id: "key-to-suggestion",
672 | name: "key-to-suggestion",
673 | hotkeys: [{
674 | key: "'",
675 | modifiers: ["Ctrl"]
676 | }],
677 | editorCallback: (editor) => {
678 | __show = true;
679 | this._suggestionPopup.trigger(editor, '', true);
680 | },
681 | isVisible: () => this._suggestionPopup.isVisible()
682 | });
683 | //快捷键3
684 | this.addCommand({
685 | id: "key-to-switch-model",
686 | name: "key-to-switch-model",
687 | hotkeys: [{
688 | key: "]",
689 | modifiers: ["Alt"]
690 | }],
691 | editorCallback: (editor) => {
692 | if (__article_types == null) {
693 | __article_type = 'Null';
694 | bar_text("No model found, 💔", 0);
695 | } else {
696 | if (__article_types.hasOwnProperty(__article_type) == false) {
697 | __article_type = Object.keys(__article_types)[0];
698 | } else if (Object.keys(__article_types).indexOf(__article_type) + 1 >= Object.keys(__article_types).length) {
699 | __article_type = Object.keys(__article_types)[0];
700 | } else {
701 | __article_type = Object.keys(__article_types)[Object.keys(__article_types).indexOf(__article_type) + 1];
702 | }
703 | bar_text("switch to " + __article_type + "🍀", 0);
704 | }
705 | },
706 | isVisible: () => this._suggestionPopup.isVisible()
707 | });
708 | }
709 |
710 |
711 | onunload() {
712 | return __async(this, null, function*() {
713 | // this.snippetManager.onunload();
714 | // newview.onunload();
715 | });
716 | }
717 | loadSettings() {
718 | return __async(this, null, function*() {
719 | this.settings = Object.assign({}, DEFAULT_SETTINGS, yield this.loadData());
720 |
721 | });
722 | }
723 | get suggestionPopup() {
724 | return this._suggestionPopup;
725 | }
726 | saveSettings() {
727 | return __async(this, null, function*() {
728 | yield this.saveData(this.settings);
729 | });
730 | }
731 | };
732 |
--------------------------------------------------------------------------------
/obsidian-sengener/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "obsidian-SenGener",
3 | "name": "obsidian-SenGener",
4 | "version": "1.1.1",
5 | "minAppVersion": "1.0.0",
6 | "description": "This plugin can generate sentenses for writting with the help of API",
7 | "author": "zazaji",
8 | "authorUrl": "https://github.com/zazaji",
9 | "isDesktopOnly": false
10 | }
11 |
--------------------------------------------------------------------------------
/obsidian-sengener/styles.css:
--------------------------------------------------------------------------------
1 | .completr-suggestion-item {
2 | padding: 5px 10px 5px 10px;
3 | white-space: nowrap;
4 | overflow: hidden;
5 | text-overflow: ellipsis;
6 | }
7 |
8 | .completr-suggestion-placeholder {
9 | border-width: 1px 0 1px 0;
10 | border-style: solid;
11 | }
12 |
13 | .completr-settings-no-border {
14 | border: none;
15 | }
16 |
17 | .completr-settings-list-item {
18 | border-top: 1px solid grey;
19 | padding: 4px 0 0 0;
20 | }
21 |
22 | .completr-settings-error {
23 | border: 1px solid red !important;
24 | }
25 |
26 | /**
27 | Snippet color classes.
28 | ["lightskyblue", "orange", "lime", "pink", "cornsilk", "magenta", "navajowhite"]
29 | */
30 |
31 | .completr-suggestion-placeholder0 {
32 | border-color: lightskyblue;
33 | }
34 |
35 | /* These extra selectors enforce their color on all children, because CodeMirror does weird nesting of spans when
36 | nesting multiple decorations. */
37 | span.completr-suggestion-placeholder0 span {
38 | border-color: lightskyblue;
39 | }
40 |
41 | .completr-suggestion-placeholder1 {
42 | border-color: orange;
43 | }
44 |
45 | span.completr-suggestion-placeholder1 span {
46 | border-color: orange;
47 | }
48 |
49 | .completr-suggestion-placeholder2 {
50 | border-color: lime;
51 | }
52 |
53 | span.completr-suggestion-placeholder2 span {
54 | border-color: lime;
55 | }
56 |
57 | .completr-suggestion-placeholder3 {
58 | border-color: pink;
59 | }
60 |
61 | span.completr-suggestion-placeholder3 span {
62 | border-color: pink;
63 | }
64 |
65 | .completr-suggestion-placeholder4 {
66 | border-color: cornsilk;
67 | }
68 |
69 | span.completr-suggestion-placeholder4 span {
70 | border-color: cornsilk;
71 | }
72 |
73 | .completr-suggestion-placeholder5 {
74 | border-color: magenta;
75 | }
76 |
77 | span.completr-suggestion-placeholder5 span {
78 | border-color: magenta;
79 | }
80 |
81 | .completr-suggestion-placeholder6 {
82 | border-color: navajowhite;
83 | }
84 |
85 | span.completr-suggestion-placeholder6 span {
86 | border-color: navajowhite;
87 | }
88 |
--------------------------------------------------------------------------------
/readme_CN.md:
--------------------------------------------------------------------------------
1 | # 用Obsidian打造一个强大的写作辅助系统
2 |
3 |
4 | Github:
5 | https://github.com/zazaji/obsidian-SenGener/
6 |
7 | ## 用法
8 |
9 | 在写作的过程中,当文思枯竭的时候,按下快捷键(可以定义自己的快捷键),然后AI自动根据之前写的内容,自动给出提示。同时,在右侧边栏还能根据上句话,在文档库中进行全文检索,找到相应的内容。
10 | 作为一个熬夜码字的朋友,是不是觉得有种心动的感觉?
11 |
12 |
13 | 我最近用obsidian来作为管理笔记和写作的工具,所以我基于obsidian开发了这个插件。不过插件只是一方面,更重要的是后台有一个较大的全文检索服务加上一个人工智能训练后的模型。
14 | 要完全实现需要obsidian + 定制的插件 + AI数据服务。
15 |
16 | ## 实现具体过程
17 |
18 | - 从部分网站抓取了一些工作报告、百科、党建、新闻等数据。
19 | - 使用深度学习(我是用的是GPT2),训练了几个专业的模型,不同的模型,如写报告类、新闻类,效果和应用场景不同。当然,写诗填词也不在话下。
20 | - 同时将这些数据做成全文检索,并将检索和模型做成数据服务。
21 | - 使用obsidian的时候,通过快捷键调用数据服务,实现推荐列表和参考关联文档功能。
22 | - 目前我搭建了一个示例服务,基本做到开箱即用,方便大家免费测试使用。性能很一般,请大家手下留情。
23 |
24 | ## 使用方法
25 |
26 | - 下载安装obsidian。
27 | - 创建一个文库,并指定路径。
28 | - 进入该文库,并启用第三方插件模式。
29 | - 进入到文库所在路径,releases下载解压后放入`.obsidian/plugins/obsidian-SenGener`文件夹。
30 | - 如果觉得以上操作复杂了,可采用最简单的方式,将github上的test.zip下载解压后,用obsidian打开,然后启用第三方插件模式。
31 | - 在第三方插件中启用该插件。并配置快捷键。默认快捷键是`ctrl + 引号`。也可以选择不同的创作模型,调整其他参数。
32 | - 创建一个文件,开始写作。
33 |
34 | ## 参数介绍
35 | - API address: 服务地址,填写自己的API地址。我搭建了一个示例服务,里面包含了英文模型、诗词、党建文章模型、工作报告模型、腾讯welm模型。地址:https://fwzd.myfawu.com/
36 |
37 | - Type: 可以选择不同的创作模型。
38 |
39 | - token: 用于登录腾讯welm的token。可自己去腾讯welm官网申请。
40 |
41 | - enable searching: 是否启用全文检索。目前为report模型提供全文检索。
42 |
43 | - Number of choices: 多少个候选项。不要选太多,影响速度。
44 |
45 | - max length: 一次产生的文字(英语是词)的数量。不要选太多,影响速度。
46 |
47 | ## 示例:
48 | - **英文写作**
49 | 本来GPT2就是基于英文设计的,用在这里效率不是一般的高。分分钟一篇文章。
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 | - 2022.12.6 增加中文诗词模型服务
80 | - 2022.12.6 增加党建文章模型服务
81 |
82 |
--------------------------------------------------------------------------------
/styles.css:
--------------------------------------------------------------------------------
1 | .sengener_match {
2 | color: red;
3 | }
4 |
5 | .sengener_right {
6 | text-align: right;
7 | position: absolute;
8 | right: 1em;
9 | float: 'right'
10 | }
11 |
12 | .sengener_ssmall {
13 | font-size: 0.9em;
14 | line-height: 1.5em;
15 | }
16 |
17 | .sengener_title {
18 | font-size: 1.3em;
19 | line-height: 2.5em;
20 | }
21 |
22 | .sengener_page {
23 | font-size: 1.3em;
24 | line-height: 2.5em;
25 | }
26 |
27 | .sengener_pagei {
28 | border: 1px;
29 | border-color: blue;
30 | border-radius: 5px;
31 | text-align: center;
32 | }
33 |
34 | ul {
35 | border: 1px;
36 | border-color: red;
37 | display: inline-block;
38 | padding: 0;
39 | margin: 0;
40 | }
41 |
42 | ul li {
43 | border: 1px;
44 | display: inline;
45 | }
46 |
47 | ul li a {
48 | border: 1px;
49 | color: rgba(255, 0, 0, 0.61);
50 | float: left;
51 | padding: 8px 16px;
52 | text-decoration: none;
53 | }
54 |
55 | .sengener_pagination {
56 | display: inline-block;
57 | border: 1px;
58 | border-color: #ffcc00;
59 | }
60 |
61 | .sengener_pagination a {
62 | border: 1px;
63 | border-color: rgb(133, 129, 129);
64 | border-style: solid;
65 | float: left;
66 | padding: 4px 8px;
67 | margin: 4px;
68 | text-decoration: none;
69 | }
70 |
71 | .sengener_pagination a.active {
72 | background-color: #808080;
73 | color: rgb(10, 6, 206);
74 | }
75 |
76 | .pagination a:hover:not(.active) {
77 | background-color: #333;
78 | }
79 |
80 | .sengener_col-10 {
81 | position: relative;
82 | width: 75%;
83 | }
84 |
85 | .sengener_col-6 {
86 | position: relative;
87 | width: 50%;
88 | }
89 |
90 | .sengener_col-2 {
91 | position: relative;
92 | width: 21%;
93 | }
94 |
--------------------------------------------------------------------------------