Compare commits
15 Commits
12d823bfc5
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 521496f2df | |||
| 7f14056210 | |||
| 6b50219f55 | |||
| ab41c81a53 | |||
| 30063feba4 | |||
| 5b32d919f3 | |||
| 78804225bd | |||
| d34d77c34d | |||
| 10b7d60a86 | |||
| 6b9ac80177 | |||
| 798019bbbf | |||
| d17550351d | |||
| e4a339bd77 | |||
| ab0cbad418 | |||
| 7ab924d668 |
@@ -22,7 +22,5 @@
|
|||||||
"showIndentGuide": false,
|
"showIndentGuide": false,
|
||||||
"spellcheck": false,
|
"spellcheck": false,
|
||||||
"foldIndent": false,
|
"foldIndent": false,
|
||||||
"userIgnoreFilters": [
|
"userIgnoreFilters": null
|
||||||
"attachment/"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"enabledCssSnippets": [
|
"enabledCssSnippets": [],
|
||||||
"myTheme"
|
|
||||||
],
|
|
||||||
"monospaceFontFamily": "JetBrainsMono Nerd Font Mono",
|
"monospaceFontFamily": "JetBrainsMono Nerd Font Mono",
|
||||||
"textFontFamily": "思源黑体",
|
"textFontFamily": "思源黑体",
|
||||||
"interfaceFontFamily": "JetBrainsMono Nerd Font Mono",
|
"interfaceFontFamily": "JetBrainsMono Nerd Font Mono",
|
||||||
|
|||||||
@@ -7,15 +7,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "file",
|
"type": "file",
|
||||||
"ctime": 1768964533760,
|
"ctime": 1778660024443,
|
||||||
"path": "work/移动杭研/开发记录/7.18.0/开发笔记.md",
|
"path": "work/移动杭研/开发记录/7.21.0/开发笔记 7.21.0.md"
|
||||||
"title": "7.18.0 开发笔记"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "file",
|
|
||||||
"ctime": 1772680366639,
|
|
||||||
"path": "work/移动杭研/开发记录/7.19.0/开发笔记.md",
|
|
||||||
"title": "7.19.0 开发笔记"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
"pinned": [
|
"pinned": [
|
||||||
"file-explorer:reveal-active-file",
|
"file-explorer:reveal-active-file",
|
||||||
"file-explorer:move-file",
|
"file-explorer:move-file",
|
||||||
"open-with-default-app:show",
|
|
||||||
"app:reload"
|
"app:reload"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
[
|
[
|
||||||
"obsidian-paste-image-rename",
|
|
||||||
"update-relative-links",
|
"update-relative-links",
|
||||||
"quickadd",
|
|
||||||
"calendar",
|
"calendar",
|
||||||
"recent-files-obsidian",
|
|
||||||
"oz-clear-unused-images",
|
"oz-clear-unused-images",
|
||||||
"obsidian-excalidraw-plugin",
|
"obsidian-excalidraw-plugin",
|
||||||
"templater-obsidian",
|
"templater-obsidian",
|
||||||
"obsidian-linter"
|
"obsidian-linter",
|
||||||
|
"recent-files-obsidian",
|
||||||
|
"quickadd"
|
||||||
]
|
]
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
"page-preview": true,
|
"page-preview": true,
|
||||||
"daily-notes": true,
|
"daily-notes": true,
|
||||||
"templates": false,
|
"templates": false,
|
||||||
"note-composer": false,
|
"note-composer": true,
|
||||||
"command-palette": true,
|
"command-palette": true,
|
||||||
"slash-command": false,
|
"slash-command": false,
|
||||||
"editor-status": true,
|
"editor-status": true,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"folder": "calendar/diary",
|
"folder": "calendar/diary",
|
||||||
"template": "attachment/templates/日记模板",
|
"template": "attachment/templates/日常记录-日记模板",
|
||||||
"autorun": false
|
"autorun": false
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
"showAttachments": false,
|
"showAttachments": false,
|
||||||
"hideUnresolved": false,
|
"hideUnresolved": false,
|
||||||
"showOrphans": true,
|
"showOrphans": true,
|
||||||
"collapse-color-groups": true,
|
"collapse-color-groups": false,
|
||||||
"colorGroups": [],
|
"colorGroups": [],
|
||||||
"collapse-display": false,
|
"collapse-display": false,
|
||||||
"showArrow": false,
|
"showArrow": false,
|
||||||
@@ -17,6 +17,6 @@
|
|||||||
"repelStrength": 10,
|
"repelStrength": 10,
|
||||||
"linkStrength": 1,
|
"linkStrength": 1,
|
||||||
"linkDistance": 250,
|
"linkDistance": 250,
|
||||||
"scale": 0.09848280266864458,
|
"scale": 0.13807120506649795,
|
||||||
"close": true
|
"close": true
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"app:open-help": [],
|
||||||
"workspace:goto-tab-1": [],
|
"workspace:goto-tab-1": [],
|
||||||
"workspace:goto-tab-2": [],
|
"workspace:goto-tab-2": [],
|
||||||
"workspace:goto-tab-3": [],
|
"workspace:goto-tab-3": [],
|
||||||
@@ -7,6 +8,7 @@
|
|||||||
"workspace:goto-tab-6": [],
|
"workspace:goto-tab-6": [],
|
||||||
"workspace:goto-tab-7": [],
|
"workspace:goto-tab-7": [],
|
||||||
"workspace:goto-tab-8": [],
|
"workspace:goto-tab-8": [],
|
||||||
|
"workspace:goto-last-tab": [],
|
||||||
"editor:set-heading-1": [
|
"editor:set-heading-1": [
|
||||||
{
|
{
|
||||||
"modifiers": [
|
"modifiers": [
|
||||||
@@ -31,21 +33,10 @@
|
|||||||
"key": "3"
|
"key": "3"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"workspace:goto-last-tab": [],
|
|
||||||
"app:open-help": [],
|
|
||||||
"app:open-settings": [],
|
|
||||||
"obsidian-memos:show-thino-editor": [
|
|
||||||
{
|
|
||||||
"modifiers": [],
|
|
||||||
"key": "F1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"editor:toggle-comments": [],
|
|
||||||
"quickadd:runQuickAdd": [
|
"quickadd:runQuickAdd": [
|
||||||
{
|
{
|
||||||
"modifiers": [],
|
"modifiers": [],
|
||||||
"key": "F1"
|
"key": "F1"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"workspace:close-window": []
|
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
"copyLinkToElemenetAnchorTo100": false,
|
"copyLinkToElemenetAnchorTo100": false,
|
||||||
"copyFrameLinkByName": false,
|
"copyFrameLinkByName": false,
|
||||||
"disableDoubleClickTextEditing": false,
|
"disableDoubleClickTextEditing": false,
|
||||||
|
"phoneFooterSafeAreaPadding": false,
|
||||||
"folder": "excalidraw",
|
"folder": "excalidraw",
|
||||||
"cropFolder": "excalidraw/cropped",
|
"cropFolder": "excalidraw/cropped",
|
||||||
"annotateFolder": "excalidraw/annotations",
|
"annotateFolder": "excalidraw/annotations",
|
||||||
@@ -29,8 +30,10 @@
|
|||||||
"annotateSuffix": "",
|
"annotateSuffix": "",
|
||||||
"annotatePrefix": "annotated_",
|
"annotatePrefix": "annotated_",
|
||||||
"annotatePreserveSize": false,
|
"annotatePreserveSize": false,
|
||||||
|
"displaySVGInPreview": false,
|
||||||
"previewImageType": "SVGIMG",
|
"previewImageType": "SVGIMG",
|
||||||
"renderingConcurrency": 3,
|
"renderingConcurrency": 3,
|
||||||
|
"imageCacheRetentionDays": 30,
|
||||||
"allowImageCache": true,
|
"allowImageCache": true,
|
||||||
"allowImageCacheInScene": true,
|
"allowImageCacheInScene": true,
|
||||||
"displayExportedImageIfAvailable": false,
|
"displayExportedImageIfAvailable": false,
|
||||||
@@ -107,13 +110,14 @@
|
|||||||
"addDummyTextElement": false,
|
"addDummyTextElement": false,
|
||||||
"zoteroCompatibility": false,
|
"zoteroCompatibility": false,
|
||||||
"fieldSuggester": true,
|
"fieldSuggester": true,
|
||||||
|
"enableOnloadScripts": false,
|
||||||
"compatibilityMode": false,
|
"compatibilityMode": false,
|
||||||
"drawingOpenCount": 0,
|
"drawingOpenCount": 0,
|
||||||
"library": "deprecated",
|
"library": "deprecated",
|
||||||
"library2": {
|
"library2": {
|
||||||
"type": "excalidrawlib",
|
"type": "excalidrawlib",
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"source": "https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/2.20.6",
|
"source": "https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/2.22.3",
|
||||||
"libraryItems": []
|
"libraryItems": []
|
||||||
},
|
},
|
||||||
"imageElementNotice": true,
|
"imageElementNotice": true,
|
||||||
@@ -126,6 +130,7 @@
|
|||||||
"scriptEngineSettings": {},
|
"scriptEngineSettings": {},
|
||||||
"previousRelease": "2.20.6",
|
"previousRelease": "2.20.6",
|
||||||
"showReleaseNotes": false,
|
"showReleaseNotes": false,
|
||||||
|
"excalidrawMasteryPromoCollapsed": false,
|
||||||
"compareManifestToPluginVersion": true,
|
"compareManifestToPluginVersion": true,
|
||||||
"showNewVersionNotification": true,
|
"showNewVersionNotification": true,
|
||||||
"latexBoilerplate": "\\color{blue}",
|
"latexBoilerplate": "\\color{blue}",
|
||||||
@@ -482,15 +487,178 @@
|
|||||||
"canvasImmersiveEmbed": true,
|
"canvasImmersiveEmbed": true,
|
||||||
"startupScriptPath": "",
|
"startupScriptPath": "",
|
||||||
"aiEnabled": true,
|
"aiEnabled": true,
|
||||||
"openAIAPIToken": "sk-JynMarAEOuLlYb3_bdQAdHEhKp-vK9az-uQXGlw_VSKQIEHnCb86PeqtxLQ",
|
"aiVerboseLogging": false,
|
||||||
"openAIDefaultTextModel": "gpt-5.2",
|
"aiProviderProfiles": {
|
||||||
"openAIDefaultTextModelMaxTokens": 0,
|
"OpenAI": {
|
||||||
"openAIDefaultVisionModel": "gemini-3.1-pro-preview",
|
"provider": "openai",
|
||||||
"openAIDefaultImageGenerationModel": "gemini-3.1-pro-preview",
|
"apiKey": "",
|
||||||
"openAIURL": "https://api.ai-wave.org/v1/chat/completions",
|
"baseURL": "https://api.openai.com/v1"
|
||||||
"openAIImageGenerationURL": "https://api.openai.com/v1/images/generations",
|
},
|
||||||
"openAIImageEditsURL": "https://api.openai.com/v1/images/edits",
|
"Anthropic": {
|
||||||
"openAIImageVariationURL": "https://api.openai.com/v1/images/variations",
|
"provider": "anthropic",
|
||||||
|
"apiKey": "",
|
||||||
|
"baseURL": "https://api.anthropic.com/v1"
|
||||||
|
},
|
||||||
|
"Google Gemini": {
|
||||||
|
"provider": "google",
|
||||||
|
"apiKey": "",
|
||||||
|
"baseURL": "https://generativelanguage.googleapis.com/v1beta"
|
||||||
|
},
|
||||||
|
"xAI": {
|
||||||
|
"provider": "xai",
|
||||||
|
"apiKey": "",
|
||||||
|
"baseURL": "https://api.x.ai/v1"
|
||||||
|
},
|
||||||
|
"OpenAI-compatible": {
|
||||||
|
"provider": "openai-compatible",
|
||||||
|
"apiKey": "",
|
||||||
|
"baseURL": "https://api.openai.com/v1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"aiTextModelConfigs": {
|
||||||
|
"gpt-5-mini": {
|
||||||
|
"providerId": "OpenAI",
|
||||||
|
"model": "gpt-5-mini",
|
||||||
|
"endpoint": "",
|
||||||
|
"multimodalSupport": true
|
||||||
|
},
|
||||||
|
"claude-sonnet-4-5": {
|
||||||
|
"providerId": "Anthropic",
|
||||||
|
"model": "claude-sonnet-4-5",
|
||||||
|
"endpoint": "",
|
||||||
|
"multimodalSupport": true
|
||||||
|
},
|
||||||
|
"gemini-2.5-pro": {
|
||||||
|
"providerId": "Google Gemini",
|
||||||
|
"model": "gemini-2.5-pro",
|
||||||
|
"endpoint": "",
|
||||||
|
"multimodalSupport": true
|
||||||
|
},
|
||||||
|
"grok-4-fast": {
|
||||||
|
"providerId": "xAI",
|
||||||
|
"model": "grok-4-fast",
|
||||||
|
"endpoint": "",
|
||||||
|
"multimodalSupport": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"aiImageModelConfigs": {
|
||||||
|
"dall-e-2": {
|
||||||
|
"providerId": "OpenAI",
|
||||||
|
"model": "dall-e-2",
|
||||||
|
"supportedSizes": [
|
||||||
|
"256x256",
|
||||||
|
"512x512",
|
||||||
|
"1024x1024"
|
||||||
|
],
|
||||||
|
"supportsPromptImageTransforms": true,
|
||||||
|
"supportsMaskImageEdits": true
|
||||||
|
},
|
||||||
|
"dall-e-3": {
|
||||||
|
"providerId": "OpenAI",
|
||||||
|
"model": "dall-e-3",
|
||||||
|
"supportedSizes": [
|
||||||
|
"1024x1024",
|
||||||
|
"1792x1024",
|
||||||
|
"1024x1792"
|
||||||
|
],
|
||||||
|
"supportsPromptImageTransforms": false,
|
||||||
|
"supportsMaskImageEdits": false
|
||||||
|
},
|
||||||
|
"gpt-image-1": {
|
||||||
|
"providerId": "OpenAI",
|
||||||
|
"model": "gpt-image-1",
|
||||||
|
"supportedSizes": [
|
||||||
|
"1024x1024",
|
||||||
|
"1536x1024",
|
||||||
|
"1024x1536"
|
||||||
|
],
|
||||||
|
"supportsPromptImageTransforms": true,
|
||||||
|
"supportsMaskImageEdits": true
|
||||||
|
},
|
||||||
|
"gpt-image-1-mini": {
|
||||||
|
"providerId": "OpenAI",
|
||||||
|
"model": "gpt-image-1-mini",
|
||||||
|
"supportedSizes": [
|
||||||
|
"1024x1024",
|
||||||
|
"1536x1024",
|
||||||
|
"1024x1536"
|
||||||
|
],
|
||||||
|
"supportsPromptImageTransforms": true,
|
||||||
|
"supportsMaskImageEdits": true
|
||||||
|
},
|
||||||
|
"gpt-image-1.5": {
|
||||||
|
"providerId": "OpenAI",
|
||||||
|
"model": "gpt-image-1.5",
|
||||||
|
"supportedSizes": [
|
||||||
|
"1024x1024",
|
||||||
|
"1536x1024",
|
||||||
|
"1024x1536"
|
||||||
|
],
|
||||||
|
"supportsPromptImageTransforms": true,
|
||||||
|
"supportsMaskImageEdits": true
|
||||||
|
},
|
||||||
|
"gpt-image-2": {
|
||||||
|
"providerId": "OpenAI",
|
||||||
|
"model": "gpt-image-2",
|
||||||
|
"supportedSizes": [
|
||||||
|
"1024x1024",
|
||||||
|
"1536x1024",
|
||||||
|
"1024x1536",
|
||||||
|
"2048x2048"
|
||||||
|
],
|
||||||
|
"supportsPromptImageTransforms": true,
|
||||||
|
"supportsMaskImageEdits": true
|
||||||
|
},
|
||||||
|
"gemini-2.5-flash-image": {
|
||||||
|
"providerId": "Google Gemini",
|
||||||
|
"model": "gemini-2.5-flash-image",
|
||||||
|
"supportedSizes": [
|
||||||
|
"1024x1024"
|
||||||
|
],
|
||||||
|
"supportsPromptImageTransforms": true,
|
||||||
|
"supportsMaskImageEdits": false
|
||||||
|
},
|
||||||
|
"gemini-3.1-flash-image-preview": {
|
||||||
|
"providerId": "Google Gemini",
|
||||||
|
"model": "gemini-3.1-flash-image-preview",
|
||||||
|
"supportedSizes": [
|
||||||
|
"1024x1024"
|
||||||
|
],
|
||||||
|
"supportsPromptImageTransforms": true,
|
||||||
|
"supportsMaskImageEdits": false
|
||||||
|
},
|
||||||
|
"gemini-3-pro-image-preview": {
|
||||||
|
"providerId": "Google Gemini",
|
||||||
|
"model": "gemini-3-pro-image-preview",
|
||||||
|
"supportedSizes": [
|
||||||
|
"1024x1024"
|
||||||
|
],
|
||||||
|
"supportsPromptImageTransforms": true,
|
||||||
|
"supportsMaskImageEdits": false
|
||||||
|
},
|
||||||
|
"grok-imagine-image-quality": {
|
||||||
|
"providerId": "xAI",
|
||||||
|
"model": "grok-imagine-image-quality",
|
||||||
|
"supportedSizes": [
|
||||||
|
"1024x1024"
|
||||||
|
],
|
||||||
|
"supportsPromptImageTransforms": true,
|
||||||
|
"supportsMaskImageEdits": false
|
||||||
|
},
|
||||||
|
"grok-imagine-image-pro": {
|
||||||
|
"providerId": "xAI",
|
||||||
|
"model": "grok-imagine-image-pro",
|
||||||
|
"supportedSizes": [
|
||||||
|
"1024x1024"
|
||||||
|
],
|
||||||
|
"supportsPromptImageTransforms": true,
|
||||||
|
"supportsMaskImageEdits": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"aiDefaultTextModel": "gpt-5-mini",
|
||||||
|
"aiDefaultImageGenerationModel": "gpt-image-1",
|
||||||
|
"aiDefaultMaxOutgoingTokens": 0,
|
||||||
|
"aiDefaultMaxResponseTokens": 4096,
|
||||||
"modifierKeyConfig": {
|
"modifierKeyConfig": {
|
||||||
"Mac": {
|
"Mac": {
|
||||||
"LocalFileDragAction": {
|
"LocalFileDragAction": {
|
||||||
@@ -780,7 +948,6 @@
|
|||||||
"longPressDesktop": 500,
|
"longPressDesktop": 500,
|
||||||
"longPressMobile": 500,
|
"longPressMobile": 500,
|
||||||
"doubleClickLinkOpenViewMode": true,
|
"doubleClickLinkOpenViewMode": true,
|
||||||
"isDebugMode": false,
|
|
||||||
"rank": "Bronze",
|
"rank": "Bronze",
|
||||||
"modifierKeyOverrides": [
|
"modifierKeyOverrides": [
|
||||||
{
|
{
|
||||||
@@ -813,6 +980,7 @@
|
|||||||
"margin": "normal"
|
"margin": "normal"
|
||||||
},
|
},
|
||||||
"disableContextMenu": false,
|
"disableContextMenu": false,
|
||||||
|
"isDebugMode": false,
|
||||||
"defaultTrayMode": true,
|
"defaultTrayMode": true,
|
||||||
"compactModeOnTablets": true
|
"compactModeOnTablets": true
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-excalidraw-plugin",
|
"id": "obsidian-excalidraw-plugin",
|
||||||
"name": "Excalidraw",
|
"name": "Excalidraw",
|
||||||
"version": "2.21.0",
|
"version": "2.23.3",
|
||||||
"minAppVersion": "1.5.7",
|
"minAppVersion": "1.5.7",
|
||||||
"description": "Sketch Your Mind. An Obsidian plugin to edit and view Excalidraw drawings. Enter the world of 4D Visual PKM.",
|
"description": "Sketch Your Mind. Edit and view Excalidraw drawings. Enter the world of 4D Visual PKM.",
|
||||||
"author": "Zsolt Viczian",
|
"author": "Zsolt Viczian",
|
||||||
"authorUrl": "https://excalidraw-obsidian.online",
|
"authorUrl": "https://excalidraw-obsidian.online",
|
||||||
"fundingUrl": "https://ko-fi.com/zsolt",
|
"fundingUrl": "https://ko-fi.com/zsolt",
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"imageNamePattern": "image-{{DATE:YYYYMMDDHHmmssSSS}}",
|
|
||||||
"dupNumberAtStart": false,
|
|
||||||
"dupNumberDelimiter": "-",
|
|
||||||
"dupNumberAlways": false,
|
|
||||||
"autoRename": true,
|
|
||||||
"handleAllAttachments": false,
|
|
||||||
"excludeExtensionPattern": "",
|
|
||||||
"disableRenameNotice": true
|
|
||||||
}
|
|
||||||
@@ -1,942 +0,0 @@
|
|||||||
/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD */
|
|
||||||
var __defProp = Object.defineProperty;
|
|
||||||
var __defProps = Object.defineProperties;
|
|
||||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
||||||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
||||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
||||||
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
||||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
||||||
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
||||||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
||||||
var __spreadValues = (a, b) => {
|
|
||||||
for (var prop in b || (b = {}))
|
|
||||||
if (__hasOwnProp.call(b, prop))
|
|
||||||
__defNormalProp(a, prop, b[prop]);
|
|
||||||
if (__getOwnPropSymbols)
|
|
||||||
for (var prop of __getOwnPropSymbols(b)) {
|
|
||||||
if (__propIsEnum.call(b, prop))
|
|
||||||
__defNormalProp(a, prop, b[prop]);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
};
|
|
||||||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
||||||
var __commonJS = (cb, mod) => function __require() {
|
|
||||||
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
||||||
};
|
|
||||||
var __export = (target, all) => {
|
|
||||||
for (var name in all)
|
|
||||||
__defProp(target, name, { get: all[name], enumerable: true });
|
|
||||||
};
|
|
||||||
var __copyProps = (to, from, except, desc) => {
|
|
||||||
if (from && typeof from === "object" || typeof from === "function") {
|
|
||||||
for (let key of __getOwnPropNames(from))
|
|
||||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
||||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
||||||
}
|
|
||||||
return to;
|
|
||||||
};
|
|
||||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
||||||
var __async = (__this, __arguments, generator) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var fulfilled = (value) => {
|
|
||||||
try {
|
|
||||||
step(generator.next(value));
|
|
||||||
} catch (e) {
|
|
||||||
reject(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var rejected = (value) => {
|
|
||||||
try {
|
|
||||||
step(generator.throw(value));
|
|
||||||
} catch (e) {
|
|
||||||
reject(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
||||||
step((generator = generator.apply(__this, __arguments)).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// package.json
|
|
||||||
var require_package = __commonJS({
|
|
||||||
"package.json"(exports, module2) {
|
|
||||||
module2.exports = {
|
|
||||||
name: "obsidian-paste-image-rename",
|
|
||||||
version: "1.6.1",
|
|
||||||
main: "main.js",
|
|
||||||
scripts: {
|
|
||||||
start: "node esbuild.config.mjs",
|
|
||||||
build: "tsc -noEmit -skipLibCheck && BUILD_ENV=production node esbuild.config.mjs && cp manifest.json build",
|
|
||||||
version: "node version-bump.mjs && git add manifest.json versions.json",
|
|
||||||
release: "npm run build && gh release create ${npm_package_version} build/*"
|
|
||||||
},
|
|
||||||
keywords: [],
|
|
||||||
author: "Reorx",
|
|
||||||
license: "MIT",
|
|
||||||
devDependencies: {
|
|
||||||
"@types/node": "^18.11.18",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.49.0",
|
|
||||||
"@typescript-eslint/parser": "^5.49.0",
|
|
||||||
"builtin-modules": "^3.3.0",
|
|
||||||
esbuild: "0.16.17",
|
|
||||||
obsidian: "^1.1.1",
|
|
||||||
tslib: "2.5.0",
|
|
||||||
typescript: "4.9.4"
|
|
||||||
},
|
|
||||||
dependencies: {
|
|
||||||
"cash-dom": "^8.1.2"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// src/main.ts
|
|
||||||
var main_exports = {};
|
|
||||||
__export(main_exports, {
|
|
||||||
default: () => PasteImageRenamePlugin
|
|
||||||
});
|
|
||||||
module.exports = __toCommonJS(main_exports);
|
|
||||||
var import_obsidian2 = require("obsidian");
|
|
||||||
|
|
||||||
// src/batch.ts
|
|
||||||
var import_obsidian = require("obsidian");
|
|
||||||
|
|
||||||
// src/utils.ts
|
|
||||||
var DEBUG = false;
|
|
||||||
if (DEBUG)
|
|
||||||
console.log("DEBUG is enabled");
|
|
||||||
function debugLog(...args) {
|
|
||||||
if (DEBUG) {
|
|
||||||
console.log(new Date().toISOString().slice(11, 23), ...args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function createElementTree(rootEl, opts) {
|
|
||||||
const result = {
|
|
||||||
el: rootEl.createEl(opts.tag, opts),
|
|
||||||
children: []
|
|
||||||
};
|
|
||||||
const children = opts.children || [];
|
|
||||||
for (const child of children) {
|
|
||||||
result.children.push(createElementTree(result.el, child));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
var path = {
|
|
||||||
// Credit: @creationix/path.js
|
|
||||||
join(...partSegments) {
|
|
||||||
let parts = [];
|
|
||||||
for (let i = 0, l = partSegments.length; i < l; i++) {
|
|
||||||
parts = parts.concat(partSegments[i].split("/"));
|
|
||||||
}
|
|
||||||
const newParts = [];
|
|
||||||
for (let i = 0, l = parts.length; i < l; i++) {
|
|
||||||
const part = parts[i];
|
|
||||||
if (!part || part === ".")
|
|
||||||
continue;
|
|
||||||
else
|
|
||||||
newParts.push(part);
|
|
||||||
}
|
|
||||||
if (parts[0] === "")
|
|
||||||
newParts.unshift("");
|
|
||||||
return newParts.join("/");
|
|
||||||
},
|
|
||||||
// returns the last part of a path, e.g. 'foo.jpg'
|
|
||||||
basename(fullpath) {
|
|
||||||
const sp = fullpath.split("/");
|
|
||||||
return sp[sp.length - 1];
|
|
||||||
},
|
|
||||||
// return extension without dot, e.g. 'jpg'
|
|
||||||
extension(fullpath) {
|
|
||||||
const positions = [...fullpath.matchAll(new RegExp("\\.", "gi"))].map((a) => a.index);
|
|
||||||
return fullpath.slice(positions[positions.length - 1] + 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var filenameNotAllowedChars = /[^\p{L}0-9~`!@$&*()\-_=+{};'",<.>? ]/ug;
|
|
||||||
var sanitizer = {
|
|
||||||
filename(s) {
|
|
||||||
return s.replace(filenameNotAllowedChars, "").trim();
|
|
||||||
},
|
|
||||||
delimiter(s) {
|
|
||||||
s = this.filename(s);
|
|
||||||
if (!s)
|
|
||||||
s = "-";
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
function escapeRegExp(s) {
|
|
||||||
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
||||||
}
|
|
||||||
function lockInputMethodComposition(el) {
|
|
||||||
const state = {
|
|
||||||
lock: false
|
|
||||||
};
|
|
||||||
el.addEventListener("compositionstart", () => {
|
|
||||||
state.lock = true;
|
|
||||||
});
|
|
||||||
el.addEventListener("compositionend", () => {
|
|
||||||
state.lock = false;
|
|
||||||
});
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
// src/batch.ts
|
|
||||||
var ImageBatchRenameModal = class extends import_obsidian.Modal {
|
|
||||||
constructor(app, activeFile, renameFunc, onClose) {
|
|
||||||
super(app);
|
|
||||||
this.activeFile = activeFile;
|
|
||||||
this.renameFunc = renameFunc;
|
|
||||||
this.onCloseExtra = onClose;
|
|
||||||
this.state = {
|
|
||||||
namePattern: "",
|
|
||||||
extPattern: "",
|
|
||||||
nameReplace: "",
|
|
||||||
renameTasks: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
onOpen() {
|
|
||||||
this.containerEl.addClass("image-rename-modal");
|
|
||||||
const { contentEl, titleEl } = this;
|
|
||||||
titleEl.setText("Batch rename embeded files");
|
|
||||||
const namePatternSetting = new import_obsidian.Setting(contentEl).setName("Name pattern").setDesc("Please input the name pattern to match files (regex)").addText((text) => text.setValue(this.state.namePattern).onChange(
|
|
||||||
(value) => __async(this, null, function* () {
|
|
||||||
this.state.namePattern = value;
|
|
||||||
})
|
|
||||||
));
|
|
||||||
const npInputEl = namePatternSetting.controlEl.children[0];
|
|
||||||
npInputEl.focus();
|
|
||||||
const npInputState = lockInputMethodComposition(npInputEl);
|
|
||||||
npInputEl.addEventListener("keydown", (e) => __async(this, null, function* () {
|
|
||||||
if (e.key === "Enter" && !npInputState.lock) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!this.state.namePattern) {
|
|
||||||
errorEl.innerText = 'Error: "Name pattern" could not be empty';
|
|
||||||
errorEl.style.display = "block";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.matchImageNames(tbodyEl);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
const extPatternSetting = new import_obsidian.Setting(contentEl).setName("Extension pattern").setDesc("Please input the extension pattern to match files (regex)").addText((text) => text.setValue(this.state.extPattern).onChange(
|
|
||||||
(value) => __async(this, null, function* () {
|
|
||||||
this.state.extPattern = value;
|
|
||||||
})
|
|
||||||
));
|
|
||||||
const extInputEl = extPatternSetting.controlEl.children[0];
|
|
||||||
extInputEl.addEventListener("keydown", (e) => __async(this, null, function* () {
|
|
||||||
if (e.key === "Enter") {
|
|
||||||
e.preventDefault();
|
|
||||||
this.matchImageNames(tbodyEl);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
const nameReplaceSetting = new import_obsidian.Setting(contentEl).setName("Name replace").setDesc("Please input the string to replace the matched name (use $1, $2 for regex groups)").addText((text) => text.setValue(this.state.nameReplace).onChange(
|
|
||||||
(value) => __async(this, null, function* () {
|
|
||||||
this.state.nameReplace = value;
|
|
||||||
})
|
|
||||||
));
|
|
||||||
const nrInputEl = nameReplaceSetting.controlEl.children[0];
|
|
||||||
const nrInputState = lockInputMethodComposition(nrInputEl);
|
|
||||||
nrInputEl.addEventListener("keydown", (e) => __async(this, null, function* () {
|
|
||||||
if (e.key === "Enter" && !nrInputState.lock) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.matchImageNames(tbodyEl);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
const matchedContainer = contentEl.createDiv({
|
|
||||||
cls: "matched-container"
|
|
||||||
});
|
|
||||||
const tableET = createElementTree(matchedContainer, {
|
|
||||||
tag: "table",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
tag: "thead",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
tag: "tr",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
tag: "td",
|
|
||||||
text: "Original path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: "td",
|
|
||||||
text: "Renamed Name"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: "tbody"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
const tbodyEl = tableET.children[1].el;
|
|
||||||
const errorEl = contentEl.createDiv({
|
|
||||||
cls: "error",
|
|
||||||
attr: {
|
|
||||||
style: "display: none;"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
new import_obsidian.Setting(contentEl).addButton((button) => {
|
|
||||||
button.setButtonText("Rename all").setClass("mod-cta").onClick(() => {
|
|
||||||
new ConfirmModal(
|
|
||||||
this.app,
|
|
||||||
"Confirm rename all",
|
|
||||||
`Are you sure? This will rename all the ${this.state.renameTasks.length} images matched the pattern.`,
|
|
||||||
() => {
|
|
||||||
this.renameAll();
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
).open();
|
|
||||||
});
|
|
||||||
}).addButton((button) => {
|
|
||||||
button.setButtonText("Cancel").onClick(() => {
|
|
||||||
this.close();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
onClose() {
|
|
||||||
const { contentEl } = this;
|
|
||||||
contentEl.empty();
|
|
||||||
this.onCloseExtra();
|
|
||||||
}
|
|
||||||
renameAll() {
|
|
||||||
return __async(this, null, function* () {
|
|
||||||
debugLog("renameAll", this.state);
|
|
||||||
for (const task of this.state.renameTasks) {
|
|
||||||
yield this.renameFunc(task.file, task.name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
matchImageNames(tbodyEl) {
|
|
||||||
const { state } = this;
|
|
||||||
const renameTasks = [];
|
|
||||||
tbodyEl.empty();
|
|
||||||
const fileCache = this.app.metadataCache.getFileCache(this.activeFile);
|
|
||||||
if (!fileCache || !fileCache.embeds)
|
|
||||||
return;
|
|
||||||
const namePatternRegex = new RegExp(state.namePattern, "g");
|
|
||||||
const extPatternRegex = new RegExp(state.extPattern);
|
|
||||||
fileCache.embeds.forEach((embed) => {
|
|
||||||
const file = this.app.metadataCache.getFirstLinkpathDest(embed.link, this.activeFile.path);
|
|
||||||
if (!file) {
|
|
||||||
console.warn("file not found", embed.link);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (state.extPattern) {
|
|
||||||
const m0 = extPatternRegex.exec(file.extension);
|
|
||||||
if (!m0)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const stem = file.basename;
|
|
||||||
namePatternRegex.lastIndex = 0;
|
|
||||||
const m1 = namePatternRegex.exec(stem);
|
|
||||||
if (!m1)
|
|
||||||
return;
|
|
||||||
let renamedName = file.name;
|
|
||||||
if (state.nameReplace) {
|
|
||||||
namePatternRegex.lastIndex = 0;
|
|
||||||
renamedName = stem.replace(namePatternRegex, state.nameReplace);
|
|
||||||
renamedName = `${renamedName}.${file.extension}`;
|
|
||||||
}
|
|
||||||
renameTasks.push({
|
|
||||||
file,
|
|
||||||
name: renamedName
|
|
||||||
});
|
|
||||||
createElementTree(tbodyEl, {
|
|
||||||
tag: "tr",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
tag: "td",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
tag: "span",
|
|
||||||
text: file.name
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: "div",
|
|
||||||
text: file.path,
|
|
||||||
attr: {
|
|
||||||
class: "file-path"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: "td",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
tag: "span",
|
|
||||||
text: renamedName
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: "div",
|
|
||||||
text: path.join(file.parent.path, renamedName),
|
|
||||||
attr: {
|
|
||||||
class: "file-path"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
debugLog("new renameTasks", renameTasks);
|
|
||||||
state.renameTasks = renameTasks;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var ConfirmModal = class extends import_obsidian.Modal {
|
|
||||||
constructor(app, title, message, onConfirm) {
|
|
||||||
super(app);
|
|
||||||
this.title = title;
|
|
||||||
this.message = message;
|
|
||||||
this.onConfirm = onConfirm;
|
|
||||||
}
|
|
||||||
onOpen() {
|
|
||||||
const { contentEl, titleEl } = this;
|
|
||||||
titleEl.setText(this.title);
|
|
||||||
contentEl.createEl("p", {
|
|
||||||
text: this.message
|
|
||||||
});
|
|
||||||
new import_obsidian.Setting(contentEl).addButton((button) => {
|
|
||||||
button.setButtonText("Yes").setClass("mod-warning").onClick(() => {
|
|
||||||
this.onConfirm();
|
|
||||||
this.close();
|
|
||||||
});
|
|
||||||
}).addButton((button) => {
|
|
||||||
button.setButtonText("No").onClick(() => {
|
|
||||||
this.close();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// src/template.ts
|
|
||||||
var dateTmplRegex = /{{DATE:([^}]+)}}/gm;
|
|
||||||
var frontmatterTmplRegex = /{{frontmatter:([^}]+)}}/gm;
|
|
||||||
var replaceDateVar = (s, date) => {
|
|
||||||
const m = dateTmplRegex.exec(s);
|
|
||||||
if (!m)
|
|
||||||
return s;
|
|
||||||
return s.replace(m[0], date.format(m[1]));
|
|
||||||
};
|
|
||||||
var replaceFrontmatterVar = (s, frontmatter) => {
|
|
||||||
if (!frontmatter)
|
|
||||||
return s;
|
|
||||||
const m = frontmatterTmplRegex.exec(s);
|
|
||||||
if (!m)
|
|
||||||
return s;
|
|
||||||
return s.replace(m[0], frontmatter[m[1]] || "");
|
|
||||||
};
|
|
||||||
var renderTemplate = (tmpl, data, frontmatter) => {
|
|
||||||
const now = window.moment();
|
|
||||||
let text = tmpl;
|
|
||||||
let newtext;
|
|
||||||
while ((newtext = replaceDateVar(text, now)) != text) {
|
|
||||||
text = newtext;
|
|
||||||
}
|
|
||||||
while ((newtext = replaceFrontmatterVar(text, frontmatter)) != text) {
|
|
||||||
text = newtext;
|
|
||||||
}
|
|
||||||
text = text.replace(/{{imageNameKey}}/gm, data.imageNameKey).replace(/{{fileName}}/gm, data.fileName).replace(/{{dirName}}/gm, data.dirName).replace(/{{firstHeading}}/gm, data.firstHeading);
|
|
||||||
return text;
|
|
||||||
};
|
|
||||||
|
|
||||||
// src/main.ts
|
|
||||||
var DEFAULT_SETTINGS = {
|
|
||||||
imageNamePattern: "{{fileName}}",
|
|
||||||
dupNumberAtStart: false,
|
|
||||||
dupNumberDelimiter: "-",
|
|
||||||
dupNumberAlways: false,
|
|
||||||
autoRename: false,
|
|
||||||
handleAllAttachments: false,
|
|
||||||
excludeExtensionPattern: "",
|
|
||||||
disableRenameNotice: false
|
|
||||||
};
|
|
||||||
var PASTED_IMAGE_PREFIX = "Pasted image ";
|
|
||||||
var PasteImageRenamePlugin = class extends import_obsidian2.Plugin {
|
|
||||||
constructor() {
|
|
||||||
super(...arguments);
|
|
||||||
this.modals = [];
|
|
||||||
}
|
|
||||||
onload() {
|
|
||||||
return __async(this, null, function* () {
|
|
||||||
const pkg = require_package();
|
|
||||||
console.log(`Plugin loading: ${pkg.name} ${pkg.version} BUILD_ENV=${"production"}`);
|
|
||||||
yield this.loadSettings();
|
|
||||||
this.registerEvent(
|
|
||||||
this.app.vault.on("create", (file) => {
|
|
||||||
if (!(file instanceof import_obsidian2.TFile))
|
|
||||||
return;
|
|
||||||
const timeGapMs = new Date().getTime() - file.stat.ctime;
|
|
||||||
if (timeGapMs > 1e3)
|
|
||||||
return;
|
|
||||||
if (isMarkdownFile(file))
|
|
||||||
return;
|
|
||||||
if (isPastedImage(file)) {
|
|
||||||
debugLog("pasted image created", file);
|
|
||||||
this.startRenameProcess(file, this.settings.autoRename);
|
|
||||||
} else {
|
|
||||||
if (this.settings.handleAllAttachments) {
|
|
||||||
debugLog("handleAllAttachments for file", file);
|
|
||||||
if (this.testExcludeExtension(file)) {
|
|
||||||
debugLog("excluded file by ext", file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.startRenameProcess(file, this.settings.autoRename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
const startBatchRenameProcess = () => {
|
|
||||||
this.openBatchRenameModal();
|
|
||||||
};
|
|
||||||
this.addCommand({
|
|
||||||
id: "batch-rename-embeded-files",
|
|
||||||
name: "Batch rename embeded files (in the current file)",
|
|
||||||
callback: startBatchRenameProcess
|
|
||||||
});
|
|
||||||
if (DEBUG) {
|
|
||||||
this.addRibbonIcon("wand-glyph", "Batch rename embeded files", startBatchRenameProcess);
|
|
||||||
}
|
|
||||||
const batchRenameAllImages = () => {
|
|
||||||
this.batchRenameAllImages();
|
|
||||||
};
|
|
||||||
this.addCommand({
|
|
||||||
id: "batch-rename-all-images",
|
|
||||||
name: "Batch rename all images instantly (in the current file)",
|
|
||||||
callback: batchRenameAllImages
|
|
||||||
});
|
|
||||||
if (DEBUG) {
|
|
||||||
this.addRibbonIcon("wand-glyph", "Batch rename all images instantly (in the current file)", batchRenameAllImages);
|
|
||||||
}
|
|
||||||
this.addSettingTab(new SettingTab(this.app, this));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
startRenameProcess(file, autoRename = false) {
|
|
||||||
return __async(this, null, function* () {
|
|
||||||
const activeFile = this.getActiveFile();
|
|
||||||
if (!activeFile) {
|
|
||||||
new import_obsidian2.Notice("Error: No active file found.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { stem, newName, isMeaningful } = this.generateNewName(file, activeFile);
|
|
||||||
debugLog("generated newName:", newName, isMeaningful);
|
|
||||||
if (!isMeaningful || !autoRename) {
|
|
||||||
this.openRenameModal(file, isMeaningful ? stem : "", activeFile.path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.renameFile(file, newName, activeFile.path, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
renameFile(file, inputNewName, sourcePath, replaceCurrentLine) {
|
|
||||||
return __async(this, null, function* () {
|
|
||||||
const { name: newName } = yield this.deduplicateNewName(inputNewName, file);
|
|
||||||
debugLog("deduplicated newName:", newName);
|
|
||||||
const originName = file.name;
|
|
||||||
const linkText = this.app.fileManager.generateMarkdownLink(file, sourcePath);
|
|
||||||
const newPath = path.join(file.parent.path, newName);
|
|
||||||
try {
|
|
||||||
yield this.app.fileManager.renameFile(file, newPath);
|
|
||||||
} catch (err) {
|
|
||||||
new import_obsidian2.Notice(`Failed to rename ${newName}: ${err}`);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
if (!replaceCurrentLine) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const newLinkText = this.app.fileManager.generateMarkdownLink(file, sourcePath);
|
|
||||||
debugLog("replace text", linkText, newLinkText);
|
|
||||||
const editor = this.getActiveEditor();
|
|
||||||
if (!editor) {
|
|
||||||
new import_obsidian2.Notice(`Failed to rename ${newName}: no active editor`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const cursor = editor.getCursor();
|
|
||||||
const line = editor.getLine(cursor.line);
|
|
||||||
const replacedLine = line.replace(linkText, newLinkText);
|
|
||||||
debugLog("current line -> replaced line", line, replacedLine);
|
|
||||||
editor.transaction({
|
|
||||||
changes: [
|
|
||||||
{
|
|
||||||
from: __spreadProps(__spreadValues({}, cursor), { ch: 0 }),
|
|
||||||
to: __spreadProps(__spreadValues({}, cursor), { ch: line.length }),
|
|
||||||
text: replacedLine
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
if (!this.settings.disableRenameNotice) {
|
|
||||||
new import_obsidian2.Notice(`Renamed ${originName} to ${newName}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
openRenameModal(file, newName, sourcePath) {
|
|
||||||
const modal = new ImageRenameModal(
|
|
||||||
this.app,
|
|
||||||
file,
|
|
||||||
newName,
|
|
||||||
(confirmedName) => {
|
|
||||||
debugLog("confirmedName:", confirmedName);
|
|
||||||
this.renameFile(file, confirmedName, sourcePath, true);
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
this.modals.splice(this.modals.indexOf(modal), 1);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this.modals.push(modal);
|
|
||||||
modal.open();
|
|
||||||
debugLog("modals count", this.modals.length);
|
|
||||||
}
|
|
||||||
openBatchRenameModal() {
|
|
||||||
const activeFile = this.getActiveFile();
|
|
||||||
const modal = new ImageBatchRenameModal(
|
|
||||||
this.app,
|
|
||||||
activeFile,
|
|
||||||
(file, name) => __async(this, null, function* () {
|
|
||||||
yield this.renameFile(file, name, activeFile.path);
|
|
||||||
}),
|
|
||||||
() => {
|
|
||||||
this.modals.splice(this.modals.indexOf(modal), 1);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
this.modals.push(modal);
|
|
||||||
modal.open();
|
|
||||||
}
|
|
||||||
batchRenameAllImages() {
|
|
||||||
return __async(this, null, function* () {
|
|
||||||
const activeFile = this.getActiveFile();
|
|
||||||
const fileCache = this.app.metadataCache.getFileCache(activeFile);
|
|
||||||
if (!fileCache || !fileCache.embeds)
|
|
||||||
return;
|
|
||||||
const extPatternRegex = /jpe?g|png|gif|tiff|webp/i;
|
|
||||||
for (const embed of fileCache.embeds) {
|
|
||||||
const file = this.app.metadataCache.getFirstLinkpathDest(embed.link, activeFile.path);
|
|
||||||
if (!file) {
|
|
||||||
console.warn("file not found", embed.link);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const m0 = extPatternRegex.exec(file.extension);
|
|
||||||
if (!m0)
|
|
||||||
return;
|
|
||||||
const { newName, isMeaningful } = this.generateNewName(file, activeFile);
|
|
||||||
debugLog("generated newName:", newName, isMeaningful);
|
|
||||||
if (!isMeaningful) {
|
|
||||||
new import_obsidian2.Notice("Failed to batch rename images: the generated name is not meaningful");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
yield this.renameFile(file, newName, activeFile.path, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// returns a new name for the input file, with extension
|
|
||||||
generateNewName(file, activeFile) {
|
|
||||||
let imageNameKey = "";
|
|
||||||
let firstHeading = "";
|
|
||||||
let frontmatter;
|
|
||||||
const fileCache = this.app.metadataCache.getFileCache(activeFile);
|
|
||||||
if (fileCache) {
|
|
||||||
debugLog("frontmatter", fileCache.frontmatter);
|
|
||||||
frontmatter = fileCache.frontmatter;
|
|
||||||
imageNameKey = (frontmatter == null ? void 0 : frontmatter.imageNameKey) || "";
|
|
||||||
firstHeading = getFirstHeading(fileCache.headings);
|
|
||||||
} else {
|
|
||||||
console.warn("could not get file cache from active file", activeFile.name);
|
|
||||||
}
|
|
||||||
const stem = renderTemplate(
|
|
||||||
this.settings.imageNamePattern,
|
|
||||||
{
|
|
||||||
imageNameKey,
|
|
||||||
fileName: activeFile.basename,
|
|
||||||
dirName: activeFile.parent.name,
|
|
||||||
firstHeading
|
|
||||||
},
|
|
||||||
frontmatter
|
|
||||||
);
|
|
||||||
const meaninglessRegex = new RegExp(`[${this.settings.dupNumberDelimiter}\\s]`, "gm");
|
|
||||||
return {
|
|
||||||
stem,
|
|
||||||
newName: stem + "." + file.extension,
|
|
||||||
isMeaningful: stem.replace(meaninglessRegex, "") !== ""
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// newName: foo.ext
|
|
||||||
deduplicateNewName(newName, file) {
|
|
||||||
return __async(this, null, function* () {
|
|
||||||
const dir = file.parent.path;
|
|
||||||
const listed = yield this.app.vault.adapter.list(dir);
|
|
||||||
debugLog("sibling files", listed);
|
|
||||||
const newNameExt = path.extension(newName), newNameStem = newName.slice(0, newName.length - newNameExt.length - 1), newNameStemEscaped = escapeRegExp(newNameStem), delimiter = this.settings.dupNumberDelimiter, delimiterEscaped = escapeRegExp(delimiter);
|
|
||||||
let dupNameRegex;
|
|
||||||
if (this.settings.dupNumberAtStart) {
|
|
||||||
dupNameRegex = new RegExp(
|
|
||||||
`^(?<number>\\d+)${delimiterEscaped}(?<name>${newNameStemEscaped})\\.${newNameExt}$`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
dupNameRegex = new RegExp(
|
|
||||||
`^(?<name>${newNameStemEscaped})${delimiterEscaped}(?<number>\\d+)\\.${newNameExt}$`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
debugLog("dupNameRegex", dupNameRegex);
|
|
||||||
const dupNameNumbers = [];
|
|
||||||
let isNewNameExist = false;
|
|
||||||
for (let sibling of listed.files) {
|
|
||||||
sibling = path.basename(sibling);
|
|
||||||
if (sibling == newName) {
|
|
||||||
isNewNameExist = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const m = dupNameRegex.exec(sibling);
|
|
||||||
if (!m)
|
|
||||||
continue;
|
|
||||||
dupNameNumbers.push(parseInt(m.groups.number));
|
|
||||||
}
|
|
||||||
if (isNewNameExist || this.settings.dupNumberAlways) {
|
|
||||||
const newNumber = dupNameNumbers.length > 0 ? Math.max(...dupNameNumbers) + 1 : 1;
|
|
||||||
if (this.settings.dupNumberAtStart) {
|
|
||||||
newName = `${newNumber}${delimiter}${newNameStem}.${newNameExt}`;
|
|
||||||
} else {
|
|
||||||
newName = `${newNameStem}${delimiter}${newNumber}.${newNameExt}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
name: newName,
|
|
||||||
stem: newName.slice(0, newName.length - newNameExt.length - 1),
|
|
||||||
extension: newNameExt
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
getActiveFile() {
|
|
||||||
const view = this.app.workspace.getActiveViewOfType(import_obsidian2.MarkdownView);
|
|
||||||
const file = view == null ? void 0 : view.file;
|
|
||||||
debugLog("active file", file == null ? void 0 : file.path);
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
getActiveEditor() {
|
|
||||||
const view = this.app.workspace.getActiveViewOfType(import_obsidian2.MarkdownView);
|
|
||||||
return view == null ? void 0 : view.editor;
|
|
||||||
}
|
|
||||||
onunload() {
|
|
||||||
this.modals.map((modal) => modal.close());
|
|
||||||
}
|
|
||||||
testExcludeExtension(file) {
|
|
||||||
const pattern = this.settings.excludeExtensionPattern;
|
|
||||||
if (!pattern)
|
|
||||||
return false;
|
|
||||||
return new RegExp(pattern).test(file.extension);
|
|
||||||
}
|
|
||||||
loadSettings() {
|
|
||||||
return __async(this, null, function* () {
|
|
||||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, yield this.loadData());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
saveSettings() {
|
|
||||||
return __async(this, null, function* () {
|
|
||||||
yield this.saveData(this.settings);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
function getFirstHeading(headings) {
|
|
||||||
if (headings && headings.length > 0) {
|
|
||||||
for (const heading of headings) {
|
|
||||||
if (heading.level === 1) {
|
|
||||||
return heading.heading;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
function isPastedImage(file) {
|
|
||||||
if (file instanceof import_obsidian2.TFile) {
|
|
||||||
if (file.name.startsWith(PASTED_IMAGE_PREFIX)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
function isMarkdownFile(file) {
|
|
||||||
if (file instanceof import_obsidian2.TFile) {
|
|
||||||
if (file.extension === "md") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var ImageRenameModal = class extends import_obsidian2.Modal {
|
|
||||||
constructor(app, src, stem, renameFunc, onClose) {
|
|
||||||
super(app);
|
|
||||||
this.src = src;
|
|
||||||
this.stem = stem;
|
|
||||||
this.renameFunc = renameFunc;
|
|
||||||
this.onCloseExtra = onClose;
|
|
||||||
}
|
|
||||||
onOpen() {
|
|
||||||
this.containerEl.addClass("image-rename-modal");
|
|
||||||
const { contentEl, titleEl } = this;
|
|
||||||
titleEl.setText("Rename image");
|
|
||||||
const imageContainer = contentEl.createDiv({
|
|
||||||
cls: "image-container"
|
|
||||||
});
|
|
||||||
imageContainer.createEl("img", {
|
|
||||||
attr: {
|
|
||||||
src: this.app.vault.getResourcePath(this.src)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let stem = this.stem;
|
|
||||||
const ext = this.src.extension;
|
|
||||||
const getNewName = (stem2) => stem2 + "." + ext;
|
|
||||||
const getNewPath = (stem2) => path.join(this.src.parent.path, getNewName(stem2));
|
|
||||||
const infoET = createElementTree(contentEl, {
|
|
||||||
tag: "ul",
|
|
||||||
cls: "info",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
tag: "li",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
tag: "span",
|
|
||||||
text: "Origin path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: "span",
|
|
||||||
text: this.src.path
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: "li",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
tag: "span",
|
|
||||||
text: "New path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tag: "span",
|
|
||||||
text: getNewPath(stem)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
const doRename = () => __async(this, null, function* () {
|
|
||||||
debugLog("doRename", `stem=${stem}`);
|
|
||||||
this.renameFunc(getNewName(stem));
|
|
||||||
});
|
|
||||||
const nameSetting = new import_obsidian2.Setting(contentEl).setName("New name").setDesc("Please input the new name for the image (without extension)").addText((text) => text.setValue(stem).onChange(
|
|
||||||
(value) => __async(this, null, function* () {
|
|
||||||
stem = sanitizer.filename(value);
|
|
||||||
infoET.children[1].children[1].el.innerText = getNewPath(stem);
|
|
||||||
})
|
|
||||||
));
|
|
||||||
const nameInputEl = nameSetting.controlEl.children[0];
|
|
||||||
nameInputEl.focus();
|
|
||||||
const nameInputState = lockInputMethodComposition(nameInputEl);
|
|
||||||
nameInputEl.addEventListener("keydown", (e) => __async(this, null, function* () {
|
|
||||||
if (e.key === "Enter" && !nameInputState.lock) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!stem) {
|
|
||||||
errorEl.innerText = 'Error: "New name" could not be empty';
|
|
||||||
errorEl.style.display = "block";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
doRename();
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
const errorEl = contentEl.createDiv({
|
|
||||||
cls: "error",
|
|
||||||
attr: {
|
|
||||||
style: "display: none;"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
new import_obsidian2.Setting(contentEl).addButton((button) => {
|
|
||||||
button.setButtonText("Rename").onClick(() => {
|
|
||||||
doRename();
|
|
||||||
this.close();
|
|
||||||
});
|
|
||||||
}).addButton((button) => {
|
|
||||||
button.setButtonText("Cancel").onClick(() => {
|
|
||||||
this.close();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
onClose() {
|
|
||||||
const { contentEl } = this;
|
|
||||||
contentEl.empty();
|
|
||||||
this.onCloseExtra();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var imageNamePatternDesc = `
|
|
||||||
The pattern indicates how the new name should be generated.
|
|
||||||
|
|
||||||
Available variables:
|
|
||||||
- {{fileName}}: name of the active file, without ".md" extension.
|
|
||||||
- {{imageNameKey}}: this variable is read from the markdown file's frontmatter, from the same key "imageNameKey".
|
|
||||||
- {{DATE:$FORMAT}}: use "$FORMAT" to format the current date, "$FORMAT" must be a Moment.js format string, e.g. {{DATE:YYYY-MM-DD}}.
|
|
||||||
|
|
||||||
Here are some examples from pattern to image names (repeat in sequence), variables: fileName = "My note", imageNameKey = "foo":
|
|
||||||
- {{fileName}}: My note, My note-1, My note-2
|
|
||||||
- {{imageNameKey}}: foo, foo-1, foo-2
|
|
||||||
- {{imageNameKey}}-{{DATE:YYYYMMDD}}: foo-20220408, foo-20220408-1, foo-20220408-2
|
|
||||||
`;
|
|
||||||
var SettingTab = class extends import_obsidian2.PluginSettingTab {
|
|
||||||
constructor(app, plugin) {
|
|
||||||
super(app, plugin);
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
display() {
|
|
||||||
const { containerEl } = this;
|
|
||||||
containerEl.empty();
|
|
||||||
new import_obsidian2.Setting(containerEl).setName("Image name pattern").setDesc(imageNamePatternDesc).setClass("long-description-setting-item").addText((text) => text.setPlaceholder("{{imageNameKey}}").setValue(this.plugin.settings.imageNamePattern).onChange(
|
|
||||||
(value) => __async(this, null, function* () {
|
|
||||||
this.plugin.settings.imageNamePattern = value;
|
|
||||||
yield this.plugin.saveSettings();
|
|
||||||
})
|
|
||||||
));
|
|
||||||
new import_obsidian2.Setting(containerEl).setName("Duplicate number at start (or end)").setDesc(`If enabled, duplicate number will be added at the start as prefix for the image name, otherwise it will be added at the end as suffix for the image name.`).addToggle((toggle) => toggle.setValue(this.plugin.settings.dupNumberAtStart).onChange(
|
|
||||||
(value) => __async(this, null, function* () {
|
|
||||||
this.plugin.settings.dupNumberAtStart = value;
|
|
||||||
yield this.plugin.saveSettings();
|
|
||||||
})
|
|
||||||
));
|
|
||||||
new import_obsidian2.Setting(containerEl).setName("Duplicate number delimiter").setDesc(`The delimiter to generate the number prefix/suffix for duplicated names. For example, if the value is "-", the suffix will be like "-1", "-2", "-3", and the prefix will be like "1-", "2-", "3-". Only characters that are valid in file names are allowed.`).addText((text) => text.setValue(this.plugin.settings.dupNumberDelimiter).onChange(
|
|
||||||
(value) => __async(this, null, function* () {
|
|
||||||
this.plugin.settings.dupNumberDelimiter = sanitizer.delimiter(value);
|
|
||||||
yield this.plugin.saveSettings();
|
|
||||||
})
|
|
||||||
));
|
|
||||||
new import_obsidian2.Setting(containerEl).setName("Always add duplicate number").setDesc(`If enabled, duplicate number will always be added to the image name. Otherwise, it will only be added when the name is duplicated.`).addToggle((toggle) => toggle.setValue(this.plugin.settings.dupNumberAlways).onChange(
|
|
||||||
(value) => __async(this, null, function* () {
|
|
||||||
this.plugin.settings.dupNumberAlways = value;
|
|
||||||
yield this.plugin.saveSettings();
|
|
||||||
})
|
|
||||||
));
|
|
||||||
new import_obsidian2.Setting(containerEl).setName("Auto rename").setDesc(`By default, the rename modal will always be shown to confirm before renaming, if this option is set, the image will be auto renamed after pasting.`).addToggle((toggle) => toggle.setValue(this.plugin.settings.autoRename).onChange(
|
|
||||||
(value) => __async(this, null, function* () {
|
|
||||||
this.plugin.settings.autoRename = value;
|
|
||||||
yield this.plugin.saveSettings();
|
|
||||||
})
|
|
||||||
));
|
|
||||||
new import_obsidian2.Setting(containerEl).setName("Handle all attachments").setDesc(`By default, the plugin only handles images that starts with "Pasted image " in name,
|
|
||||||
which is the prefix Obsidian uses to create images from pasted content.
|
|
||||||
If this option is set, the plugin will handle all attachments that are created in the vault.`).addToggle((toggle) => toggle.setValue(this.plugin.settings.handleAllAttachments).onChange(
|
|
||||||
(value) => __async(this, null, function* () {
|
|
||||||
this.plugin.settings.handleAllAttachments = value;
|
|
||||||
yield this.plugin.saveSettings();
|
|
||||||
})
|
|
||||||
));
|
|
||||||
new import_obsidian2.Setting(containerEl).setName("Exclude extension pattern").setDesc(`This option is only useful when "Handle all attachments" is enabled.
|
|
||||||
Write a Regex pattern to exclude certain extensions from being handled. Only the first line will be used.`).setClass("single-line-textarea").addTextArea((text) => text.setPlaceholder("docx?|xlsx?|pptx?|zip|rar").setValue(this.plugin.settings.excludeExtensionPattern).onChange(
|
|
||||||
(value) => __async(this, null, function* () {
|
|
||||||
this.plugin.settings.excludeExtensionPattern = value;
|
|
||||||
yield this.plugin.saveSettings();
|
|
||||||
})
|
|
||||||
));
|
|
||||||
new import_obsidian2.Setting(containerEl).setName("Disable rename notice").setDesc(`Turn off this option if you don't want to see the notice when renaming images.
|
|
||||||
Note that Obsidian may display a notice when a link has changed, this option cannot disable that.`).addToggle((toggle) => toggle.setValue(this.plugin.settings.disableRenameNotice).onChange(
|
|
||||||
(value) => __async(this, null, function* () {
|
|
||||||
this.plugin.settings.disableRenameNotice = value;
|
|
||||||
yield this.plugin.saveSettings();
|
|
||||||
})
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "obsidian-paste-image-rename",
|
|
||||||
"name": "Paste image rename",
|
|
||||||
"version": "1.6.1",
|
|
||||||
"minAppVersion": "0.12.0",
|
|
||||||
"description": "Rename pasted images and all the other attchments added to the vault",
|
|
||||||
"author": "Reorx",
|
|
||||||
"authorUrl": "https://github.com/reorx",
|
|
||||||
"isDesktopOnly": false
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
/* src/styles.css */
|
|
||||||
:root {
|
|
||||||
--shadow-color: 0deg 0% 0%;
|
|
||||||
--shadow-elevation-medium:
|
|
||||||
0.5px 0.5px 0.7px hsl(var(--shadow-color) / 0.14),
|
|
||||||
1.1px 1.1px 1.5px -0.9px hsl(var(--shadow-color) / 0.12),
|
|
||||||
2.4px 2.5px 3.3px -1.8px hsl(var(--shadow-color) / 0.1),
|
|
||||||
5.3px 5.6px 7.3px -2.7px hsl(var(--shadow-color) / 0.09),
|
|
||||||
11px 11.4px 15.1px -3.6px hsl(var(--shadow-color) / 0.07);
|
|
||||||
}
|
|
||||||
.image-rename-modal .modal {
|
|
||||||
width: 65%;
|
|
||||||
min-width: 600px;
|
|
||||||
}
|
|
||||||
.image-rename-modal .modal-content {
|
|
||||||
padding: 10px 5px;
|
|
||||||
}
|
|
||||||
.image-rename-modal .image-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.image-rename-modal .info {
|
|
||||||
padding: 10px 0;
|
|
||||||
color: var(--text-muted);
|
|
||||||
user-select: text;
|
|
||||||
}
|
|
||||||
.image-rename-modal .info li > span:nth-of-type(1) {
|
|
||||||
display: inline-block;
|
|
||||||
width: 6em;
|
|
||||||
margin-right: .5em;
|
|
||||||
}
|
|
||||||
.image-rename-modal .info li > span:nth-of-type(1):after {
|
|
||||||
content: ":";
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
.image-rename-modal .image-container img {
|
|
||||||
display: block;
|
|
||||||
max-height: 300px;
|
|
||||||
box-shadow: var(--shadow-elevation-medium);
|
|
||||||
}
|
|
||||||
.image-rename-modal .setting-item-control input {
|
|
||||||
min-width: 300px;
|
|
||||||
}
|
|
||||||
.image-rename-modal .error {
|
|
||||||
border: 1px solid rgb(201, 90, 90);
|
|
||||||
color: rgb(134, 22, 22);
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
.image-rename-modal table {
|
|
||||||
font-size: .9em;
|
|
||||||
line-height: 1.8;
|
|
||||||
margin-bottom: 1.5em;
|
|
||||||
user-select: text;
|
|
||||||
}
|
|
||||||
.image-rename-modal table td {
|
|
||||||
padding-right: 1em;
|
|
||||||
}
|
|
||||||
.image-rename-modal table thead td {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
.image-rename-modal table tbody td .file-path {
|
|
||||||
font-size: .8em;
|
|
||||||
color: var(--text-faint);
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
.long-description-setting-item {
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
.long-description-setting-item .setting-item-description {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
line-height: 1.3em;
|
|
||||||
}
|
|
||||||
.long-description-setting-item .setting-item-control {
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
.long-description-setting-item .setting-item-control input {
|
|
||||||
min-width: 300px;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
@@ -1,140 +1,116 @@
|
|||||||
{
|
{
|
||||||
"choices": [
|
"choices": [
|
||||||
{
|
{
|
||||||
"id": "0462f0ba-4349-4531-b1a0-6634e5fc146f",
|
"id": "c31732d3-4333-4aa1-9963-4f2ee5580251",
|
||||||
"name": "日常记录-闪念笔记",
|
"name": "日常记录-闪念笔记",
|
||||||
"type": "Template",
|
"type": "Template",
|
||||||
"command": false,
|
"command": false,
|
||||||
"templatePath": "attachment/templates/时间戳笔记.md",
|
"templatePath": "attachment/templates/日常记录-闪念笔记.md",
|
||||||
"fileNameFormat": {
|
"fileNameFormat": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"format": "{{DATE:YYYYMMDDHHmmSS}}"
|
"format": "{{DATE:MM月DD日 HHmmss}}"
|
||||||
},
|
},
|
||||||
"folder": {
|
"folder": {
|
||||||
"enabled": true,
|
"enabled": false,
|
||||||
"folders": [
|
"folders": [],
|
||||||
"000-Inbox"
|
|
||||||
],
|
|
||||||
"chooseWhenCreatingNote": false,
|
"chooseWhenCreatingNote": false,
|
||||||
"createInSameFolderAsActiveFile": false,
|
"createInSameFolderAsActiveFile": false,
|
||||||
"chooseFromSubfolders": false
|
"chooseFromSubfolders": false
|
||||||
},
|
},
|
||||||
"appendLink": false,
|
"appendLink": false,
|
||||||
"openFileInNewTab": {
|
|
||||||
"enabled": true,
|
|
||||||
"direction": "vertical",
|
|
||||||
"focus": true
|
|
||||||
},
|
|
||||||
"openFile": true,
|
"openFile": true,
|
||||||
"openFileInMode": "default",
|
|
||||||
"fileExistsMode": "Increment the file name",
|
|
||||||
"setFileExistsBehavior": false,
|
|
||||||
"fileOpening": {
|
|
||||||
"location": "tab",
|
|
||||||
"direction": "vertical",
|
|
||||||
"focus": true,
|
|
||||||
"mode": "default"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "eccc9792-8b8b-42bd-a20d-8c061ece98cf",
|
|
||||||
"name": "移动杭研-问题记录",
|
|
||||||
"type": "Template",
|
|
||||||
"command": false,
|
|
||||||
"templatePath": "attachment/templates/移动杭研-问题记录.md",
|
|
||||||
"fileNameFormat": {
|
|
||||||
"enabled": true,
|
|
||||||
"format": "{{DATE:MMDD}}-{{value}}"
|
|
||||||
},
|
|
||||||
"folder": {
|
|
||||||
"enabled": true,
|
|
||||||
"folders": [
|
|
||||||
"000-Inbox"
|
|
||||||
],
|
|
||||||
"chooseWhenCreatingNote": false,
|
|
||||||
"createInSameFolderAsActiveFile": false,
|
|
||||||
"chooseFromSubfolders": false
|
|
||||||
},
|
|
||||||
"appendLink": false,
|
|
||||||
"openFileInNewTab": {
|
|
||||||
"enabled": true,
|
|
||||||
"direction": "vertical",
|
|
||||||
"focus": true
|
|
||||||
},
|
|
||||||
"openFile": true,
|
|
||||||
"openFileInMode": "default",
|
|
||||||
"fileExistsMode": "Increment the file name",
|
|
||||||
"setFileExistsBehavior": false,
|
|
||||||
"fileOpening": {
|
"fileOpening": {
|
||||||
"location": "split",
|
"location": "split",
|
||||||
"direction": "vertical",
|
"direction": "vertical",
|
||||||
"focus": true,
|
"mode": "default",
|
||||||
"mode": "default"
|
"focus": true
|
||||||
}
|
},
|
||||||
|
"fileExistsMode": "Increment the file name",
|
||||||
|
"setFileExistsBehavior": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "4ad23b51-d9fd-445e-aed8-f332e99f2350",
|
"id": "d9111ada-faa6-4d0f-8260-9c3cb97eade8",
|
||||||
"name": "移动杭研-开发笔记",
|
"name": "移动杭研-开发笔记",
|
||||||
"type": "Template",
|
"type": "Template",
|
||||||
"command": false,
|
"command": false,
|
||||||
"templatePath": "attachment/templates/移动杭研-开发笔记.md",
|
"templatePath": "attachment/templates/移动杭研-开发笔记.md",
|
||||||
"fileNameFormat": {
|
"fileNameFormat": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"format": "开发笔记"
|
"format": "开发笔记 {{VALUE:version}}"
|
||||||
},
|
},
|
||||||
"folder": {
|
"folder": {
|
||||||
"enabled": true,
|
"enabled": false,
|
||||||
"folders": [
|
"folders": [],
|
||||||
"000-Inbox"
|
|
||||||
],
|
|
||||||
"chooseWhenCreatingNote": false,
|
"chooseWhenCreatingNote": false,
|
||||||
"createInSameFolderAsActiveFile": false,
|
"createInSameFolderAsActiveFile": false,
|
||||||
"chooseFromSubfolders": false
|
"chooseFromSubfolders": false
|
||||||
},
|
},
|
||||||
"appendLink": false,
|
"appendLink": false,
|
||||||
"openFileInNewTab": {
|
|
||||||
"enabled": true,
|
|
||||||
"direction": "vertical",
|
|
||||||
"focus": true
|
|
||||||
},
|
|
||||||
"openFile": true,
|
"openFile": true,
|
||||||
"openFileInMode": "default",
|
|
||||||
"fileExistsMode": "Nothing",
|
|
||||||
"setFileExistsBehavior": true,
|
|
||||||
"fileOpening": {
|
"fileOpening": {
|
||||||
"location": "split",
|
"location": "split",
|
||||||
"direction": "vertical",
|
"direction": "vertical",
|
||||||
"focus": true,
|
"mode": "default",
|
||||||
"mode": "default"
|
"focus": true
|
||||||
}
|
},
|
||||||
|
"fileExistsMode": "Increment the file name",
|
||||||
|
"setFileExistsBehavior": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "83f6f73d-a391-4096-b07f-a9626f10980e",
|
"id": "9b9829e0-5cb5-4cc1-907c-7eb0626a2c3d",
|
||||||
"name": "openTerminal",
|
"name": "移动杭研-问题记录",
|
||||||
|
"type": "Template",
|
||||||
|
"command": false,
|
||||||
|
"templatePath": "attachment/templates/移动杭研-问题记录.md",
|
||||||
|
"fileNameFormat": {
|
||||||
|
"enabled": true,
|
||||||
|
"format": "{{DATE:MMDD}}-{{VALUE:info|{{DATE:HHmmss}}}}"
|
||||||
|
},
|
||||||
|
"folder": {
|
||||||
|
"enabled": false,
|
||||||
|
"folders": [],
|
||||||
|
"chooseWhenCreatingNote": false,
|
||||||
|
"createInSameFolderAsActiveFile": false,
|
||||||
|
"chooseFromSubfolders": false
|
||||||
|
},
|
||||||
|
"appendLink": false,
|
||||||
|
"openFile": true,
|
||||||
|
"fileOpening": {
|
||||||
|
"location": "split",
|
||||||
|
"direction": "vertical",
|
||||||
|
"mode": "default",
|
||||||
|
"focus": true
|
||||||
|
},
|
||||||
|
"fileExistsMode": "Increment the file name",
|
||||||
|
"setFileExistsBehavior": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "170cca5b-3037-4b3b-9f1c-82235ecde717",
|
||||||
|
"name": "打开控制台",
|
||||||
"type": "Macro",
|
"type": "Macro",
|
||||||
"command": false,
|
"command": false,
|
||||||
"runOnStartup": false,
|
"runOnStartup": false,
|
||||||
"macro": {
|
"macro": {
|
||||||
"name": "openTerminal",
|
"name": "打开控制台",
|
||||||
"id": "2417c310-3ca2-4a95-9962-761529241e84",
|
"id": "244eb44a-c362-440c-a21e-8065a482ab68",
|
||||||
"commands": [
|
"commands": [
|
||||||
{
|
{
|
||||||
"name": "openTerminal",
|
"name": "openTerminal",
|
||||||
"type": "UserScript",
|
"type": "UserScript",
|
||||||
"id": "22516ce4-03d7-4f5b-b210-b15dd82340e5",
|
"id": "2144335d-f088-4156-a81c-6fb4ab30bf55",
|
||||||
"path": "resource/工具/obsidian/scripts/openTerminal.js",
|
"path": "attachment/scripts/openTerminal.js",
|
||||||
"settings": {}
|
"settings": {}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"inputPrompt": "multi-line",
|
"inputPrompt": "single-line",
|
||||||
"persistInputPromptDrafts": true,
|
"persistInputPromptDrafts": true,
|
||||||
"useSelectionAsCaptureValue": true,
|
"useSelectionAsCaptureValue": true,
|
||||||
"devMode": false,
|
"devMode": false,
|
||||||
"templateFolderPath": "attachment/templates",
|
"templateFolderPath": "attachment/templates",
|
||||||
"announceUpdates": "all",
|
"announceUpdates": "major",
|
||||||
"version": "2.12.0",
|
"version": "2.12.0",
|
||||||
"globalVariables": {},
|
"globalVariables": {},
|
||||||
"onePageInputEnabled": false,
|
"onePageInputEnabled": false,
|
||||||
@@ -190,11 +166,41 @@
|
|||||||
"maxTokens": 128000
|
"maxTokens": 128000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "text-davinci-003",
|
"name": "gpt-4-turbo",
|
||||||
"maxTokens": 4096
|
"maxTokens": 128000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "gpt-4o",
|
||||||
|
"maxTokens": 128000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "gpt-4o-mini",
|
||||||
|
"maxTokens": 128000
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modelSource": "auto"
|
"autoSyncModels": false,
|
||||||
|
"modelSource": "modelsDev"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Gemini",
|
||||||
|
"endpoint": "https://generativelanguage.googleapis.com",
|
||||||
|
"apiKey": "",
|
||||||
|
"models": [
|
||||||
|
{
|
||||||
|
"name": "gemini-1.5-pro",
|
||||||
|
"maxTokens": 1000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "gemini-1.5-flash",
|
||||||
|
"maxTokens": 1000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "gemini-1.5-flash-8b",
|
||||||
|
"maxTokens": 1000000
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"autoSyncModels": false,
|
||||||
|
"modelSource": "modelsDev"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -207,8 +213,8 @@
|
|||||||
"addDefaultAIProviders": true,
|
"addDefaultAIProviders": true,
|
||||||
"removeMacroIndirection": true,
|
"removeMacroIndirection": true,
|
||||||
"migrateFileOpeningSettings": true,
|
"migrateFileOpeningSettings": true,
|
||||||
"setProviderModelDiscoveryMode": true,
|
|
||||||
"backfillFileOpeningDefaults": true,
|
"backfillFileOpeningDefaults": true,
|
||||||
|
"setProviderModelDiscoveryMode": true,
|
||||||
"migrateProviderApiKeysToSecretStorage": true
|
"migrateProviderApiKeysToSecretStorage": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,69 +1,65 @@
|
|||||||
{
|
{
|
||||||
"recentFiles": [
|
"recentFiles": [
|
||||||
{
|
|
||||||
"basename": "tg-bot",
|
|
||||||
"path": "000-inbox/tg-bot.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "Redis 安装",
|
|
||||||
"path": "000-inbox/Redis 安装.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "杭州服务器-AI",
|
|
||||||
"path": "000-inbox/杭州服务器-AI.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "常用命令",
|
|
||||||
"path": "resource/常用命令.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "20260316213546",
|
|
||||||
"path": "000-Inbox/20260316213546.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "IBS 智能体具体落实技术方案",
|
|
||||||
"path": "work/移动杭研/AI 项目/IBS 智能体具体落实技术方案.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "20260316160264",
|
|
||||||
"path": "000-Inbox/20260316160264.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "20260316102936",
|
|
||||||
"path": "000-Inbox/20260316102936.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "20260316105203",
|
|
||||||
"path": "000-Inbox/20260316105203.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "0316-配管任务限流问题",
|
|
||||||
"path": "work/移动杭研/问题处理/2026-03/0316-配管任务限流问题.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "0316-配管任务限流问题",
|
|
||||||
"path": "000-Inbox/0316-配管任务限流问题.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "1107-试用单删除问题",
|
|
||||||
"path": "work/移动杭研/问题处理/2025-11/1107-试用单删除问题.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "20260313151563",
|
|
||||||
"path": "000-Inbox/20260313151563.md"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"basename": "Prompt 02 COSMIC 功能过程",
|
"basename": "Prompt 02 COSMIC 功能过程",
|
||||||
"path": "resource/ai/prompts/cosmic/Prompt 02 COSMIC 功能过程.md"
|
"path": "resource/ai/prompts/cosmic/Prompt 02 COSMIC 功能过程.md"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"basename": "Prompt 03 COSMIC 子过程",
|
"basename": "开发笔记 7.21.0",
|
||||||
"path": "resource/ai/prompts/cosmic/Prompt 03 COSMIC 子过程.md"
|
"path": "work/移动杭研/开发记录/7.21.0/开发笔记 7.21.0.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "05月18日 154321 华为、中兴、自研解封禁联调",
|
||||||
|
"path": "000-inbox/05月18日 154321 华为、中兴、自研解封禁联调.md"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"basename": "Prompt 01 COSMIC 需求扩写",
|
"basename": "Prompt 01 COSMIC 需求扩写",
|
||||||
"path": "resource/ai/prompts/cosmic/Prompt 01 COSMIC 需求扩写.md"
|
"path": "resource/ai/prompts/cosmic/Prompt 01 COSMIC 需求扩写.md"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"basename": "Prompt 00 COSMIC v1",
|
||||||
|
"path": "resource/ai/prompts/cosmic/Prompt 00 COSMIC v1.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "05月19日 154820",
|
||||||
|
"path": "000-inbox/05月19日 154820.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "开发笔记 7.20.0",
|
||||||
|
"path": "work/移动杭研/开发记录/7.20.0/开发笔记 7.20.0.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "服务器-美国",
|
||||||
|
"path": "personal/服务器-美国.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "服务器-香港",
|
||||||
|
"path": "personal/服务器-香港.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "试用业务-时长、流量到期",
|
||||||
|
"path": "work/移动杭研/业务梳理/业务工单/试用业务-时长、流量到期.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "ubuntu",
|
||||||
|
"path": "resource/系统/ubuntu.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "001 大模型启动流程",
|
||||||
|
"path": "resource/ai/大模型安装笔记/001 大模型启动流程.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "Hermes 命令 cli",
|
||||||
|
"path": "resource/ai/Hermes 命令 cli.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "Hermes",
|
||||||
|
"path": "resource/ai/Hermes.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "个人债务测算",
|
||||||
|
"path": "000-inbox/个人债务测算.md"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"basename": "Prompt 06 COSMIC 锐评",
|
"basename": "Prompt 06 COSMIC 锐评",
|
||||||
"path": "resource/ai/prompts/cosmic/Prompt 06 COSMIC 锐评.md"
|
"path": "resource/ai/prompts/cosmic/Prompt 06 COSMIC 锐评.md"
|
||||||
@@ -77,24 +73,124 @@
|
|||||||
"path": "resource/ai/prompts/cosmic/Prompt 04 COSMIC PRD 文档.md"
|
"path": "resource/ai/prompts/cosmic/Prompt 04 COSMIC PRD 文档.md"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"basename": "Prompt 00 COSMIC v1",
|
"basename": "Prompt 03 COSMIC 子过程",
|
||||||
"path": "resource/ai/prompts/cosmic/Prompt 00 COSMIC v1.md"
|
"path": "resource/ai/prompts/cosmic/Prompt 03 COSMIC 子过程.md"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"basename": "BUSI_REQUIREMENT_COSMIC",
|
"basename": "数据库信息",
|
||||||
"path": "resource/ai/prompts/cosmic 业务版本/BUSI_REQUIREMENT_COSMIC.md"
|
"path": "work/移动杭研/项目备忘/数据库信息.md"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"basename": "BUSI_REQUIREMENT_FLOWCHART",
|
"basename": "Hermes 命令 slash",
|
||||||
"path": "resource/ai/prompts/cosmic 业务版本/BUSI_REQUIREMENT_FLOWCHART.md"
|
"path": "resource/ai/Hermes 命令 slash.md"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"basename": "Prompt 高级彩虹屁",
|
"basename": "数据库设计-监控系统",
|
||||||
"path": "resource/ai/prompts/Prompt 高级彩虹屁.md"
|
"path": "excalidraw/工作相关/数据库设计-监控系统.md"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"basename": "Prompt 考勤数据规整助手 小杨",
|
"basename": "VSCode 配置文件",
|
||||||
"path": "resource/ai/prompts/Prompt 考勤数据规整助手 小杨.md"
|
"path": "resource/配置/VSCode 配置文件.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "vim 快捷键与操作手册",
|
||||||
|
"path": "resource/系统/vim 快捷键与操作手册.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "openpalm",
|
||||||
|
"path": "openpalm/openpalm.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "Claude Code 配置文件",
|
||||||
|
"path": "resource/配置/Claude Code 配置文件.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "Codex 配置文件",
|
||||||
|
"path": "resource/配置/Codex 配置文件.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "ibs-ai-mcp",
|
||||||
|
"path": "000-inbox/ibs-ai-mcp.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "gitleaks.toml",
|
||||||
|
"path": "resource/工具/gitleaks.toml.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "ibs-export",
|
||||||
|
"path": "work/移动杭研/业务梳理/ibs-export.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "常用命令",
|
||||||
|
"path": "resource/常用命令.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "s-ui",
|
||||||
|
"path": "resource/系统/网络/s-ui.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "论文系统",
|
||||||
|
"path": "personal/专升本/毕业论文/论文系统.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "MCP 学习",
|
||||||
|
"path": "000-inbox/MCP 学习.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "labelImg",
|
||||||
|
"path": "resource/ai/labelImg.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "月报 202604",
|
||||||
|
"path": "work/移动杭研/项目备忘/月报 202604.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "CLAUDE",
|
||||||
|
"path": "work/移动杭研/AI 项目/CLAUDE.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "cache+域名配置工单",
|
||||||
|
"path": "work/移动杭研/业务梳理/运营工单/cache+域名配置工单.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "IBS 智能体具体落实技术方案",
|
||||||
|
"path": "work/移动杭研/AI 项目/IBS 智能体具体落实技术方案.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "AI 工具需求-MCP 请求数统计",
|
||||||
|
"path": "work/移动杭研/AI 项目/AI 工具需求-MCP 请求数统计.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "AI 工具需求-命中率计算",
|
||||||
|
"path": "work/移动杭研/AI 项目/AI 工具需求-命中率计算.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "图纸-IBS 智能体-v1-流量查询 demo",
|
||||||
|
"path": "work/移动杭研/AI 项目/图纸-IBS 智能体-v1-流量查询 demo.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "图纸-IBS 智能体-v1-流量查询 demo 对象存储",
|
||||||
|
"path": "work/移动杭研/AI 项目/图纸-IBS 智能体-v1-流量查询 demo 对象存储.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "图纸-IBS 智能体-v2-项目架构 原生 MCP",
|
||||||
|
"path": "work/移动杭研/AI 项目/图纸-IBS 智能体-v2-项目架构 原生 MCP.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "图纸-IBS 智能体-v3-项目架构",
|
||||||
|
"path": "work/移动杭研/AI 项目/图纸-IBS 智能体-v3-项目架构.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "getHitRatio接口分析",
|
||||||
|
"path": "work/移动杭研/AI 项目/getHitRatio接口分析.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "settings.local.json",
|
||||||
|
"path": "work/移动杭研/AI 项目/settings.local.json.md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"basename": "RAG Flow 部署",
|
||||||
|
"path": "work/移动杭研/AI 项目/RAG Flow 部署.md"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"basename": "RAG Flow",
|
"basename": "RAG Flow",
|
||||||
@@ -103,110 +199,13 @@
|
|||||||
{
|
{
|
||||||
"basename": "ibs-ai 项目梳理",
|
"basename": "ibs-ai 项目梳理",
|
||||||
"path": "work/移动杭研/AI 项目/ibs-ai 项目梳理.md"
|
"path": "work/移动杭研/AI 项目/ibs-ai 项目梳理.md"
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "开发备注",
|
|
||||||
"path": "work/移动杭研/AI 项目/开发备注.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "CLAUDE.md",
|
|
||||||
"path": "work/移动杭研/AI 项目/CLAUDE.md.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "RAG Flow 部署",
|
|
||||||
"path": "work/移动杭研/AI 项目/RAG Flow 部署.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "settings.local.json",
|
|
||||||
"path": "work/移动杭研/AI 项目/settings.local.json.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "Prompt 公司智能体",
|
|
||||||
"path": "resource/ai/prompts/临时/Prompt 公司智能体.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "Prompt 需求-考勤转换-小杨",
|
|
||||||
"path": "resource/ai/prompts/临时/Prompt 需求-考勤转换-小杨.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "Prompt 专家模板",
|
|
||||||
"path": "resource/ai/prompts/Prompt 专家模板.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "Prompt ChatBI 查询规划器",
|
|
||||||
"path": "resource/ai/prompts/Prompt ChatBI 查询规划器.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "Prompt Linus",
|
|
||||||
"path": "resource/ai/prompts/Prompt Linus.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "Prompt Lisp 语言",
|
|
||||||
"path": "resource/ai/prompts/Prompt Lisp 语言.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "Prompt PPT 大纲制作专家",
|
|
||||||
"path": "resource/ai/prompts/Prompt PPT 大纲制作专家.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "Prompt PPT 逐字稿编写专家",
|
|
||||||
"path": "resource/ai/prompts/Prompt PPT 逐字稿编写专家.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "开发笔记",
|
|
||||||
"path": "work/移动杭研/开发记录/7.19.0/开发笔记.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "20260312105716",
|
|
||||||
"path": "000-Inbox/20260312105716.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "服务器-香港",
|
|
||||||
"path": "personal/服务器-香港.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "20260311142461",
|
|
||||||
"path": "000-Inbox/20260311142461.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "K-V 功能",
|
|
||||||
"path": "work/移动杭研/项目备忘/K-V 功能.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "IBS NG 日志模板",
|
|
||||||
"path": "work/移动杭研/项目备忘/IBS NG 日志模板.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "项目杂记",
|
|
||||||
"path": "work/移动杭研/项目备忘/项目杂记.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "数据库信息",
|
|
||||||
"path": "work/移动杭研/项目备忘/数据库信息.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "获取企业 token 脚本",
|
|
||||||
"path": "work/移动杭研/项目备忘/获取企业 token 脚本.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "环境账号",
|
|
||||||
"path": "work/移动杭研/项目备忘/环境账号.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "工作账号",
|
|
||||||
"path": "work/移动杭研/项目备忘/工作账号.md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"basename": "20260311083458",
|
|
||||||
"path": "000-Inbox/20260311083458.md"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"omittedPaths": [
|
"omittedPaths": [
|
||||||
"^attachment/",
|
"^calendar/",
|
||||||
"^calendar/"
|
"^attachment/"
|
||||||
],
|
],
|
||||||
"omittedTags": [],
|
"omittedTags": [],
|
||||||
"updateOn": "file-open",
|
"updateOn": "file-open",
|
||||||
"omitBookmarks": false,
|
"omitBookmarks": false
|
||||||
"maxLength": null
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"id": "recent-files-obsidian",
|
"id": "recent-files-obsidian",
|
||||||
"name": "Recent Files",
|
"name": "Recent Files",
|
||||||
"version": "1.7.6",
|
"version": "1.7.9",
|
||||||
"minAppVersion": "0.16.3",
|
"minAppVersion": "0.16.3",
|
||||||
"description": "List files by most recently opened",
|
"description": "List files by most recently opened.",
|
||||||
"author": "Tony Grosinger",
|
"author": "Tony Grosinger",
|
||||||
"authorUrl": "https://grosinger.net",
|
"authorUrl": "https://grosinger.net",
|
||||||
"isDesktopOnly": false,
|
"isDesktopOnly": false,
|
||||||
@@ -11,6 +11,5 @@
|
|||||||
"Github Sponsor": "https://github.com/sponsors/tgrosinger",
|
"Github Sponsor": "https://github.com/sponsors/tgrosinger",
|
||||||
"Buy me a Coffee": "https://buymeacoffee.com/tgrosinger",
|
"Buy me a Coffee": "https://buymeacoffee.com/tgrosinger",
|
||||||
"Paypal": "https://paypal.me/tgrosinger"
|
"Paypal": "https://paypal.me/tgrosinger"
|
||||||
},
|
}
|
||||||
"donation": "https://buymeacoffee.com/tgrosinger"
|
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,17 @@
|
|||||||
{
|
{
|
||||||
"id": "templater-obsidian",
|
"id": "templater-obsidian",
|
||||||
"name": "Templater",
|
"name": "Templater",
|
||||||
"version": "2.18.1",
|
"version": "2.20.4",
|
||||||
"description": "Create and use templates",
|
"description": "Advanced templating and automation using handlebars-like syntax.",
|
||||||
"minAppVersion": "1.5.0",
|
"minAppVersion": "1.12.2",
|
||||||
"author": "SilentVoid",
|
"author": "SilentVoid",
|
||||||
"authorUrl": "https://github.com/SilentVoid13",
|
"authorUrl": "https://github.com/SilentVoid13",
|
||||||
|
"fundingUrl": {
|
||||||
|
"GitHub Sponser (Zachatoo, maintainer)": "https://github.com/sponsors/Zachatoo",
|
||||||
|
"Ko-fi (Zachatoo, maintainer)": "https://ko-fi.com/zachatoo",
|
||||||
|
"GitHub Sponser (SilentVoid13, creator)": "https://github.com/sponsors/SilentVoid13",
|
||||||
|
"Paypal (SilentVoid13, creator)": "https://www.paypal.com/donate?hosted_button_id=U2SRGAFYXT32Q"
|
||||||
|
},
|
||||||
"helpUrl": "https://silentvoid13.github.io/Templater/",
|
"helpUrl": "https://silentvoid13.github.io/Templater/",
|
||||||
"isDesktopOnly": false
|
"isDesktopOnly": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -221,6 +221,6 @@ textarea.templater-prompt-input:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
li.CodeMirror-hint-active {
|
li.CodeMirror-hint-active {
|
||||||
background: #08f;
|
background: #0088ff;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
/* === 颜色变量 === */
|
|
||||||
:root {
|
|
||||||
--h1-color: #172a4f;
|
|
||||||
--h2-color: #4a5c7a;
|
|
||||||
--h3-color: #6b7280;
|
|
||||||
|
|
||||||
--bold-color: var(--h1-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* === 标题样式 === */
|
|
||||||
h1 {
|
|
||||||
color: var(--h1-color);
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
color: var(--h2-color);
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
color: var(--h3-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* === 粗体样式 === */
|
|
||||||
div.cm-line span.cm-strong,
|
|
||||||
div p strong {
|
|
||||||
color: var(--bold-color) !important;
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
"excalidraw-export-padding": "number",
|
"excalidraw-export-padding": "number",
|
||||||
"excalidraw-export-pngscale": "number",
|
"excalidraw-export-pngscale": "number",
|
||||||
"excalidraw-export-embed-scene": "checkbox",
|
"excalidraw-export-embed-scene": "checkbox",
|
||||||
|
"excalidraw-export-internal-links": "checkbox",
|
||||||
"excalidraw-link-prefix": "text",
|
"excalidraw-link-prefix": "text",
|
||||||
"excalidraw-url-prefix": "text",
|
"excalidraw-url-prefix": "text",
|
||||||
"excalidraw-link-brackets": "checkbox",
|
"excalidraw-link-brackets": "checkbox",
|
||||||
@@ -23,7 +24,6 @@
|
|||||||
"excalidraw-autoexport": "text",
|
"excalidraw-autoexport": "text",
|
||||||
"excalidraw-embeddable-theme": "text",
|
"excalidraw-embeddable-theme": "text",
|
||||||
"excalidraw-open-md": "checkbox",
|
"excalidraw-open-md": "checkbox",
|
||||||
"excalidraw-embed-md": "checkbox",
|
"excalidraw-embed-md": "checkbox"
|
||||||
"excalidraw-export-internal-links": "checkbox"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,20 +4,71 @@
|
|||||||
"type": "split",
|
"type": "split",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": "b29f1eccf46b0980",
|
"id": "64438af04f4b395c",
|
||||||
"type": "tabs",
|
"type": "tabs",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": "431eb252187a1635",
|
"id": "7642168f74ef821a",
|
||||||
"type": "leaf",
|
"type": "leaf",
|
||||||
"state": {
|
"state": {
|
||||||
"type": "empty",
|
"type": "markdown",
|
||||||
"state": {},
|
"state": {
|
||||||
|
"file": "resource/ai/Hermes 命令 cli.md",
|
||||||
|
"mode": "source",
|
||||||
|
"source": false,
|
||||||
|
"backlinks": false
|
||||||
|
},
|
||||||
"icon": "lucide-file",
|
"icon": "lucide-file",
|
||||||
"title": "新标签页"
|
"title": "Hermes 命令 cli"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6fa615aca5f7108e",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "markdown",
|
||||||
|
"state": {
|
||||||
|
"file": "personal/服务器-香港.md",
|
||||||
|
"mode": "source",
|
||||||
|
"source": false,
|
||||||
|
"backlinks": false
|
||||||
|
},
|
||||||
|
"icon": "lucide-file",
|
||||||
|
"title": "服务器-香港"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e366a44a66017faf",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "markdown",
|
||||||
|
"state": {
|
||||||
|
"file": "personal/服务器-美国.md",
|
||||||
|
"mode": "source",
|
||||||
|
"source": false,
|
||||||
|
"backlinks": false
|
||||||
|
},
|
||||||
|
"icon": "lucide-file",
|
||||||
|
"title": "服务器-美国"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3e12e0f7fca28148",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "markdown",
|
||||||
|
"state": {
|
||||||
|
"file": "calendar/diary/2026-05-19.md",
|
||||||
|
"mode": "source",
|
||||||
|
"source": false,
|
||||||
|
"backlinks": false
|
||||||
|
},
|
||||||
|
"icon": "lucide-file",
|
||||||
|
"title": "2026-05-19"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"currentTab": 3
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"direction": "vertical"
|
"direction": "vertical"
|
||||||
@@ -29,7 +80,7 @@
|
|||||||
{
|
{
|
||||||
"id": "6836caf2765d7139",
|
"id": "6836caf2765d7139",
|
||||||
"type": "tabs",
|
"type": "tabs",
|
||||||
"dimension": 69.27016645326505,
|
"dimension": 73.04860088365243,
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": "c8718c0c63702202",
|
"id": "c8718c0c63702202",
|
||||||
@@ -50,7 +101,7 @@
|
|||||||
"state": {
|
"state": {
|
||||||
"type": "search",
|
"type": "search",
|
||||||
"state": {
|
"state": {
|
||||||
"query": "培训",
|
"query": "file: ubu",
|
||||||
"matchingCase": false,
|
"matchingCase": false,
|
||||||
"explainSearch": true,
|
"explainSearch": true,
|
||||||
"collapseAll": false,
|
"collapseAll": false,
|
||||||
@@ -74,25 +125,25 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "729e97c586d5eaa9",
|
"id": "2184ba625789cef2",
|
||||||
"type": "tabs",
|
"type": "tabs",
|
||||||
"dimension": 30.72983354673495,
|
"dimension": 26.95139911634757,
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": "4c0b938080320781",
|
"id": "910ff34a625157e6",
|
||||||
"type": "leaf",
|
"type": "leaf",
|
||||||
"state": {
|
"state": {
|
||||||
"type": "recent-files",
|
"type": "recent-files",
|
||||||
"state": {},
|
"state": {},
|
||||||
"icon": "clock",
|
"icon": "clock",
|
||||||
"title": "Recent Files"
|
"title": "Recent files"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"direction": "horizontal",
|
"direction": "horizontal",
|
||||||
"width": 222.5
|
"width": 261.5
|
||||||
},
|
},
|
||||||
"right": {
|
"right": {
|
||||||
"id": "ca733f6d5936ae40",
|
"id": "ca733f6d5936ae40",
|
||||||
@@ -101,7 +152,7 @@
|
|||||||
{
|
{
|
||||||
"id": "1f21045435bfa327",
|
"id": "1f21045435bfa327",
|
||||||
"type": "tabs",
|
"type": "tabs",
|
||||||
"dimension": 57.8883495145631,
|
"dimension": 57.03883495145632,
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": "43a2e8e1d9201229",
|
"id": "43a2e8e1d9201229",
|
||||||
@@ -109,12 +160,13 @@
|
|||||||
"state": {
|
"state": {
|
||||||
"type": "outline",
|
"type": "outline",
|
||||||
"state": {
|
"state": {
|
||||||
|
"file": "calendar/diary/2026-05-19.md",
|
||||||
"followCursor": true,
|
"followCursor": true,
|
||||||
"showSearch": false,
|
"showSearch": false,
|
||||||
"searchQuery": ""
|
"searchQuery": ""
|
||||||
},
|
},
|
||||||
"icon": "lucide-list",
|
"icon": "lucide-list",
|
||||||
"title": "大纲"
|
"title": "2026-05-19 的大纲"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -123,7 +175,7 @@
|
|||||||
"state": {
|
"state": {
|
||||||
"type": "backlink",
|
"type": "backlink",
|
||||||
"state": {
|
"state": {
|
||||||
"file": "000-Inbox/20260305103657.md",
|
"file": "attachment/templates/查找未引用图片.md",
|
||||||
"collapseAll": false,
|
"collapseAll": false,
|
||||||
"extraContext": false,
|
"extraContext": false,
|
||||||
"sortOrder": "alphabetical",
|
"sortOrder": "alphabetical",
|
||||||
@@ -133,7 +185,7 @@
|
|||||||
"unlinkedCollapsed": false
|
"unlinkedCollapsed": false
|
||||||
},
|
},
|
||||||
"icon": "links-coming-in",
|
"icon": "links-coming-in",
|
||||||
"title": "20260305103657 的反向链接列表"
|
"title": "查找未引用图片 的反向链接列表"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -142,12 +194,11 @@
|
|||||||
"state": {
|
"state": {
|
||||||
"type": "outgoing-link",
|
"type": "outgoing-link",
|
||||||
"state": {
|
"state": {
|
||||||
"file": "000-Inbox/20260305103657.md",
|
|
||||||
"linksCollapsed": false,
|
"linksCollapsed": false,
|
||||||
"unlinkedCollapsed": true
|
"unlinkedCollapsed": true
|
||||||
},
|
},
|
||||||
"icon": "links-going-out",
|
"icon": "links-going-out",
|
||||||
"title": "20260305103657 的出链列表"
|
"title": "出链"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -184,9 +235,7 @@
|
|||||||
"type": "leaf",
|
"type": "leaf",
|
||||||
"state": {
|
"state": {
|
||||||
"type": "footnotes",
|
"type": "footnotes",
|
||||||
"state": {
|
"state": {},
|
||||||
"file": "000-Inbox/20260305103657.md"
|
|
||||||
},
|
|
||||||
"icon": "lucide-file-signature",
|
"icon": "lucide-file-signature",
|
||||||
"title": "脚注"
|
"title": "脚注"
|
||||||
}
|
}
|
||||||
@@ -194,12 +243,12 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "ea8a40d57ef90e62",
|
"id": "e2c44b9c5d0747bb",
|
||||||
"type": "tabs",
|
"type": "tabs",
|
||||||
"dimension": 42.1116504854369,
|
"dimension": 42.96116504854368,
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": "499dbd491c6fb256",
|
"id": "736f0891b55bede0",
|
||||||
"type": "leaf",
|
"type": "leaf",
|
||||||
"state": {
|
"state": {
|
||||||
"type": "calendar",
|
"type": "calendar",
|
||||||
@@ -212,7 +261,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"direction": "horizontal",
|
"direction": "horizontal",
|
||||||
"width": 287.5
|
"width": 291.5
|
||||||
},
|
},
|
||||||
"left-ribbon": {
|
"left-ribbon": {
|
||||||
"hiddenItems": {
|
"hiddenItems": {
|
||||||
@@ -223,57 +272,56 @@
|
|||||||
"canvas:新建白板": false,
|
"canvas:新建白板": false,
|
||||||
"bases:新建数据库": false,
|
"bases:新建数据库": false,
|
||||||
"templater-obsidian:Templater": true,
|
"templater-obsidian:Templater": true,
|
||||||
"obsidian-excalidraw-plugin:New drawing": false
|
"obsidian-excalidraw-plugin:新建绘图文件": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"active": "c8718c0c63702202",
|
"active": "c8718c0c63702202",
|
||||||
"lastOpenFiles": [
|
"lastOpenFiles": [
|
||||||
"calendar/diary/2026-03-17.md",
|
"resource/ai/prompts/cosmic/Prompt 02 COSMIC 功能过程.md",
|
||||||
"calendar/diary/2026-03-16.md",
|
"work/移动杭研/开发记录/7.21.0/开发笔记 7.21.0.md",
|
||||||
"000-inbox/tg-bot.md",
|
"000-inbox/05月18日 154321 华为、中兴、自研解封禁联调.md",
|
||||||
"000-inbox/Redis 安装.md",
|
"attachment/Pasted image 20260519174702.png",
|
||||||
"000-inbox/杭州服务器-AI.md",
|
"calendar/diary/2026-05-19.md",
|
||||||
"000-inbox/20260316213546.md",
|
"attachment/Pasted image 20260519170953.png",
|
||||||
"resource/常用命令.md",
|
"attachment/Pasted image 20260519170006.png",
|
||||||
"000-Inbox/20260316213546.md",
|
"attachment/Pasted image 20260519163906.png",
|
||||||
"work/移动杭研/AI 项目/IBS 智能体具体落实技术方案.md",
|
"attachment/Pasted image 20260519163145.png",
|
||||||
"000-Inbox/20260316160264.md",
|
"resource/ai/prompts/cosmic/Prompt 01 COSMIC 需求扩写.md",
|
||||||
"000-inbox/20260316105203.md",
|
"resource/ai/prompts/cosmic/Prompt 00 COSMIC v1.md",
|
||||||
"000-Inbox/20260316102936.md",
|
"000-inbox/05月19日 154820.md",
|
||||||
"000-Inbox/20260316105203.md",
|
"attachment/Pasted image 20260519140133.png",
|
||||||
"calendar/diary/2026-03-13.md",
|
"calendar/diary/2026-05-18.md",
|
||||||
"calendar/diary/2026-03-15.md",
|
"work/移动杭研/开发记录/7.20.0/开发笔记 7.20.0.md",
|
||||||
"calendar/weeks/2026-W11.md",
|
"personal/服务器-美国.md",
|
||||||
"calendar/diary/2026-03-14.md",
|
"personal/服务器-香港.md",
|
||||||
"calendar/diary/2026-03-12.md",
|
"calendar/diary/2026-05-17.md",
|
||||||
"calendar/diary/2026-03-11.md",
|
"calendar/weeks/2026-W20.md",
|
||||||
"calendar/diary/2026-03-10.md",
|
"calendar/diary/2026-05-16.md",
|
||||||
"calendar/diary/2026-03-09.md",
|
"calendar/diary/2026-05-15.md",
|
||||||
"work/移动杭研/问题处理/2026-03/0316-配管任务限流问题.md",
|
"calendar/diary/2026-05-14.md",
|
||||||
"work/移动杭研/问题处理/2026-03",
|
"calendar/diary/2026-05-13.md",
|
||||||
"000-inbox/20260313151563.md",
|
"calendar/diary/2026-05-12.md",
|
||||||
"000-Inbox/0316-配管任务限流问题.md",
|
"calendar/diary/2026-05-11.md",
|
||||||
"work/移动杭研/问题处理/2025-11/1107-试用单删除问题.md",
|
"work/移动杭研/业务梳理/业务工单/试用业务-时长、流量到期.md",
|
||||||
"000-Inbox/20260313151563.md",
|
"calendar/diary/2025年/2025-12-30.md",
|
||||||
"resource/ai/prompts/cosmic 业务版本",
|
"resource/系统/ubuntu.md",
|
||||||
"resource/ai/prompts/cosmic",
|
"resource/ai/大模型安装笔记/001 大模型启动流程.md",
|
||||||
"resource/ai/prompts/临时",
|
"attachment/b0a6584f6636a36fa59bd0c5054c8c97.png",
|
||||||
"resource/工具/rime",
|
"resource/ai/Hermes 命令 cli.md",
|
||||||
"attachment/images-paste/image-20260304153545122.png",
|
"resource/ai/Hermes.md",
|
||||||
"work/移动杭研/开发记录/7.19.0",
|
"attachment/Pasted image 20260513222338.png",
|
||||||
"work/移动杭研/AI 项目",
|
"attachment/7e1e741c766535189b32065c7a64eac1.png",
|
||||||
"resource/前端",
|
"attachment/Pasted image 20260512224844.png",
|
||||||
"resource/英语",
|
"work/移动杭研/开发记录/7.21.0",
|
||||||
"attachment/images-paste/image-20260305082403915.png",
|
"openpalm",
|
||||||
"attachment/image-20260305103708566.png",
|
"resource/rust",
|
||||||
"attachment/Pasted image 20260305103645.png",
|
"kids-coding",
|
||||||
"attachment/Pasted image 20260305103633.png",
|
"resource/python",
|
||||||
"000-inbox/未命名.canvas",
|
"work/移动杭研/问题处理/2026-04",
|
||||||
"attachment/images-uuid/dd32291e71724c52a904aa80ce1fc59a.png",
|
"work/移动杭研/开发记录/7.20.0",
|
||||||
"attachment/images-uuid/e9d6d33cd6a44bd0ab62b5999e04c7be.png",
|
"attachment/scripts/openTerminal.js",
|
||||||
"attachment/image-20260302225213302.png",
|
"attachment/scripts",
|
||||||
"attachment/Pasted image 20260302225154.png",
|
"attachment/jsscripts/openTerminal.js",
|
||||||
"resource/mermaid",
|
"000-inbox/未命名.canvas"
|
||||||
"attachment/images-uuid/1b6c8b1c84064481ac7c72347ba8e259.png"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# 华为
|
||||||
|
|
||||||
|
```
|
||||||
|
http://test.okkc.top/heb.jpg
|
||||||
|
http://test.okkc.top/nmgdx.jpg
|
||||||
|
http://test.okkc.top/wkc.png
|
||||||
|
```
|
||||||
|
|
||||||
|
## 封禁
|
||||||
|
|
||||||
|
### 下发
|
||||||
|
|
||||||
|
```
|
||||||
|
2026-05-18 08:38:46 [e694222eb3c14d19] [e694222eb3c14d19] [] [ibs-portal] [http-nio-8080-exec-9] INFO com.cmcc.cdn.platform.selfservice.service.impl.URLBanLiftServiceImpl 104 urlBanOrLift - URL封禁/解封下发配管参数:{"cp_id":"83537947","operate":0,"task_id":"WG_84e454ab445df9ad78bdecf7a2c6c","urls":[{"areas":["ZJ"],"forbid_share_cache":false,"method":"exact","plain":[1],"url":"http://test.okkc.top/heb.jpg"}]}
|
||||||
|
2026-05-18 08:38:47 [e694222eb3c14d19] [e694222eb3c14d19] [] [ibs-portal] [http-nio-8080-exec-9] INFO com.cmcc.cdn.platform.selfservice.service.impl.URLBanLiftServiceImpl 106 urlBanOrLift - 向统一网管下发URL封禁/解禁返回参数e:{"error_code":0,"error_msg":"success"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 回调
|
||||||
|
|
||||||
|
```
|
||||||
|
2026-05-19 08:38:29 [5af0086375c91839] [5af0086375c91839] [] [ibs-portal] [http-nio-8080-exec-3] INFO com.cmcc.cdn.platform.selfservice.controller.OmsApiDomainController 1466 urlBanLiftCallback - 统一网管URL封禁/解禁回调参数,urlBanLiftCallBackVO:{"tasks":[{"operate":0,"status":1,"task_id":"WG_84e454ab445df9ad78bdecf7a2c6c"}]}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 解禁
|
||||||
|
|
||||||
|
### 下发
|
||||||
|
|
||||||
|
```
|
||||||
|
2026-05-19 08:58:13 [40cc38f9c31c4b22] [40cc38f9c31c4b22] [] [ibs-portal] [http-nio-8080-exec-7] INFO com.cmcc.cdn.platform.selfservice.service.impl.URLBanLiftServiceImpl 104 urlBanOrLift - URL封禁/解封下发配管参数:{"cp_id":"83537947","operate":1,"task_id":"WG_af52d3c8f4890ae8c313c68b84d27","urls":[{"areas":["ZJ"],"forbid_share_cache":false,"method":"exact","plain":[1],"url":"http://test.okkc.top/heb.jpg"}]}
|
||||||
|
2026-05-19 08:58:13 [40cc38f9c31c4b22] [40cc38f9c31c4b22] [] [ibs-portal] [http-nio-8080-exec-7] INFO com.cmcc.cdn.platform.selfservice.service.impl.URLBanLiftServiceImpl 106 urlBanOrLift - 向统一网管下发URL封禁/解禁返回参数e:{"error_code":0,"error_msg":"success"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 回调
|
||||||
|
|
||||||
|
```
|
||||||
|
2026-05-19 09:04:29 [5e61bcc746cd302d] [5e61bcc746cd302d] [] [ibs-portal] [http-nio-8080-exec-3] INFO com.cmcc.cdn.platform.selfservice.controller.OmsApiDomainController 1466 urlBanLiftCallback - 统一网管URL封禁/解禁回调参数,urlBanLiftCallBackVO:{"tasks":[{"operate":1,"status":1,"task_id":"WG_af52d3c8f4890ae8c313c68b84d27"}]}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 多 URL
|
||||||
|
|
||||||
|
### 下发
|
||||||
|
|
||||||
|
```
|
||||||
|
2026-05-19 09:09:36 [09b677d484384b4f] [09b677d484384b4f] [] [ibs-portal] [http-nio-8080-exec-8] INFO com.cmcc.cdn.platform.selfservice.service.impl.URLBanLiftServiceImpl 104 urlBanOrLift - URL封禁/解封下发配管参数:{"cp_id":"83537947","operate":0,"task_id":"WG_0a4e9ca06497b883ab2c1435851f0","urls":[{"areas":["ZJ"],"forbid_share_cache":false,"method":"exact","plain":[1],"url":"http://test.okkc.top/nmgdx.jpg"},{"areas":["ZJ"],"forbid_share_cache":false,"method":"exact","plain":[1],"url":"http://test.okkc.top/wkc.png"}]}
|
||||||
|
2026-05-19 09:09:36 [09b677d484384b4f] [09b677d484384b4f] [] [ibs-portal] [http-nio-8080-exec-8] INFO com.cmcc.cdn.platform.selfservice.service.impl.URLBanLiftServiceImpl 106 urlBanOrLift - 向统一网管下发URL封禁/解禁返回参数e:{"error_code":0,"error_msg":"success"}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### 回调
|
||||||
|
|
||||||
|
```
|
||||||
|
2026-05-19 09:14:29 [3df12b80589f53fa] [3df12b80589f53fa] [] [ibs-portal] [http-nio-8080-exec-2] INFO com.cmcc.cdn.platform.selfservice.service.impl.URLBanLiftServiceImpl 164 urlBanLiftUpdate - URL封禁/解封回调参数:param:{"tasks":[{"operate":0,"status":1,"task_id":"WG_0a4e9ca06497b883ab2c1435851f0"}]}
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 中兴
|
||||||
|
|
||||||
|
```
|
||||||
|
https://sms-drcn.hihonorcdn.com/sms/20260512_1778575271708/patch_20260512161.zip
|
||||||
|
https://push.hihonorcdn.com/103412129/95ec15bc36347c636815599f863438ac1.png
|
||||||
|
```
|
||||||
|
|
||||||
|
## 下发
|
||||||
|
|
||||||
|
```
|
||||||
|
2026-05-19 09:51:54 [37d64e05cd954f5e] [37d64e05cd954f5e] [] [ibs-portal] [http-nio-8080-exec-7] INFO com.cmcc.cdn.platform.selfservice.service.impl.URLBanLiftServiceImpl 104 urlBanOrLift - URL封禁/解封下发配管参数:{"cp_id":"83537947","operate":0,"task_id":"WG_20cb8e5274608a97651b261e07491","urls":[{"areas":["ZJ"],"forbid_share_cache":false,"method":"exact","plain":[2],"url":"https://sms-drcn.hihonorcdn.com/sms/20260512_1778575271708/patch_20260512161.zip"},{"areas":["ZJ"],"forbid_share_cache":false,"method":"exact","plain":[2],"url":"https://push.hihonorcdn.com/103412129/95ec15bc36347c636815599f863438ac1.png"}]}
|
||||||
|
2026-05-19 09:51:54 [37d64e05cd954f5e] [37d64e05cd954f5e] [] [ibs-portal] [http-nio-8080-exec-7] INFO com.cmcc.cdn.platform.selfservice.service.impl.URLBanLiftServiceImpl 106 urlBanOrLift - 向统一网管下发URL封禁/解禁返回参数e:{"error_code":0,"error_msg":"success"}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 回调
|
||||||
|
|
||||||
|
```
|
||||||
|
2026-05-19 09:52:29 [cb930538344a7daf] [cb930538344a7daf] [] [ibs-portal] [http-nio-8080-exec-9] INFO com.cmcc.cdn.platform.selfservice.controller.OmsApiDomainController 1466 urlBanLiftCallback - 统一网管URL封禁/解禁回调参数,urlBanLiftCallBackVO:{"tasks":[{"operate":0,"status":0,"task_id":"WG_20cb8e5274608a97651b261e07491"}]}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## 下发
|
||||||
|
|
||||||
|
```
|
||||||
|
2026-05-19 09:54:10 [6723e252542e4d27] [6723e252542e4d27] [] [ibs-portal] [http-nio-8080-exec-10] INFO com.cmcc.cdn.platform.selfservice.service.impl.URLBanLiftServiceImpl 104 urlBanOrLift - URL封禁/解封下发配管参数:{"cp_id":"83537947","operate":1,"task_id":"WG_364e5c36c43e9bd58c96151f66bbc","urls":[{"areas":["ZJ"],"forbid_share_cache":false,"method":"exact","plain":[2],"url":"https://sms-drcn.hihonorcdn.com/sms/20260512_1778575271708/patch_20260512161.zip"},{"areas":["ZJ"],"forbid_share_cache":false,"method":"exact","plain":[2],"url":"https://push.hihonorcdn.com/103412129/95ec15bc36347c636815599f863438ac1.png"}]}
|
||||||
|
2026-05-19 09:54:10 [6723e252542e4d27] [6723e252542e4d27] [] [ibs-portal] [http-nio-8080-exec-10] INFO com.cmcc.cdn.platform.selfservice.service.impl.URLBanLiftServiceImpl 106 urlBanOrLift - 向统一网管下发URL封禁/解禁返回参数e:{"error_code":0,"error_msg":"success"}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 回调
|
||||||
|
|
||||||
|
```
|
||||||
|
2026-05-19 09:54:29 [873eaa002f421e4b] [873eaa002f421e4b] [] [ibs-portal] [http-nio-8080-exec-2] INFO com.cmcc.cdn.platform.selfservice.controller.OmsApiDomainController 1466 urlBanLiftCallback - 统一网管URL封禁/解禁回调参数,urlBanLiftCallBackVO:{"tasks":[{"operate":1,"status":0,"task_id":"WG_364e5c36c43e9bd58c96151f66bbc"}]}
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 自研
|
||||||
|
|
||||||
|
```
|
||||||
|
http://yanlian.e.cdn.chinamobile.com/video.ts
|
||||||
|
http://yanlian.e.cdn.chinamobile.com/intro.html
|
||||||
|
http://yanlian.e.cdn.chinamobile.com/image.jpg
|
||||||
|
http://yanlian.e.cdn.chinamobile.com/index.html
|
||||||
|
```
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
我梳理了一下,这块是“URL 封禁/解封”能力,入口是 [ProhibitionController.java](D:/MyCode/Work/yd-hy/ibs-portal/cdn-web/src/main/java/com/cmcc/cdn/platform/selfservice/controller/ProhibitionController.java:217) 的 `POST /api/url/ban`,回调是 [OmsApiDomainController.java](D:/MyCode/Work/yd-hy/ibs-portal/cdn-web/src/main/java/com/cmcc/cdn/platform/selfservice/controller/OmsApiDomainController.java:1462) 的 `POST /action?commandType=banCallBack&version=1`。
|
||||||
|
|
||||||
|
**主流程**
|
||||||
|
|
||||||
|
`/api/url/ban` 根据两个配置开关分流:
|
||||||
|
|
||||||
|
`WG_TOTAL_SWITCH=true`,或当前企业在 `WG_ENTERPRISE_ID_SWITCH` 里时,走统一网管新链路:
|
||||||
|
|
||||||
|
1. 从请求头 `Tenant-Id` 查企业。
|
||||||
|
2. 把 `URLDenyAllowTaskVO.urls` 转成统一网管 `UrlBanLiftTaskVO.urls`。
|
||||||
|
3. 校验 URL、域名归属、操作类型、重复封禁等。
|
||||||
|
4. 生成 `WG_` 开头的 `taskId`。
|
||||||
|
5. 调统一网管/BPM:`bpmService.urlBanOrLiftOperate(…)`。
|
||||||
|
6. 下发成功后落库,返回 `taskId`。
|
||||||
|
|
||||||
|
不命中开关时,走旧链路:
|
||||||
|
|
||||||
|
1. 生成 `WS_` 开头的 `itemId`。
|
||||||
|
2. 校验 URL、域名归属、操作类型、重复封禁等。
|
||||||
|
3. 调旧配管/各平面下发。
|
||||||
|
4. 落库任务、URL 明细、配管记录、平面下发记录。
|
||||||
|
|
||||||
|
**统一网管新链路写入的表**
|
||||||
|
|
||||||
|
在 [URLBanLiftServiceImpl.java](D:/MyCode/Work/yd-hy/ibs-portal/cdn-service/src/main/java/com/cmcc/cdn/platform/selfservice/service/impl/URLBanLiftServiceImpl.java:76) 里,下发成功后写:
|
||||||
|
|
||||||
|
- `ban_lift_task`
|
||||||
|
对应实体 `NetworkURLBanLiftTask`。记录任务主表:`task_id`、`cp_id`、`operate`。
|
||||||
|
|
||||||
|
- `ban_lift_url`
|
||||||
|
对应实体 `NetworkBanLiftURL`。记录每个 URL:`url`、`task_id`、`areas`、`method`、`forbid_share_cache`、`operate`、`status`、`start_time`。初始 `status=11`,表示进行中。
|
||||||
|
|
||||||
|
- `ban_lift_task_url`
|
||||||
|
`ban_lift_task` 和 `ban_lift_url` 的关联表。
|
||||||
|
|
||||||
|
统一网管链路下发失败时,代码里没有保存这几张表,只返回失败。
|
||||||
|
|
||||||
|
**旧链路写入的表**
|
||||||
|
|
||||||
|
旧链路在 [ProhibitionServiceImpl.java](D:/MyCode/Work/yd-hy/ibs-portal/cdn-service/src/main/java/com/cmcc/cdn/platform/selfservice/service/impl/ProhibitionServiceImpl.java:253) 的 `pipeUrlDenyAllowed` 里写:
|
||||||
|
|
||||||
|
- `deny_allow_task`
|
||||||
|
任务主表,记录 `item_id`、`type`、`status`、`start_time`、`cp_id`、`task_state` 等。
|
||||||
|
|
||||||
|
- `deny_allow_url`
|
||||||
|
URL 明细表,记录 `url`、`domain`、`method`、`areas`。
|
||||||
|
|
||||||
|
- `deny_allow_task_url`
|
||||||
|
任务和 URL 的关联表。
|
||||||
|
|
||||||
|
- `pipe_config_entity`
|
||||||
|
配管消息记录,保存 `message_id`、`target_name=itemId`、`type=WANG_SU_DENY/WANG_SU_DENY_RESET` 等,用于后续配管回调匹配。
|
||||||
|
|
||||||
|
- `ban_or_filter_record`
|
||||||
|
平面下发记录,保存 `task_id`、`distribution_plane`、`success_plane`、`error_plane`、`reason`、`urls`、`type=ban/unban`。
|
||||||
|
|
||||||
|
另外,旧链路会更新 Redis 里的企业每日操作次数限制,不是数据库表。
|
||||||
|
|
||||||
|
**统一网管回调改哪些表**
|
||||||
|
|
||||||
|
你贴的 `banCallBack` 回调只处理统一网管新链路。逻辑在 [URLBanLiftServiceImpl.java](D:/MyCode/Work/yd-hy/ibs-portal/cdn-service/src/main/java/com/cmcc/cdn/platform/selfservice/service/impl/URLBanLiftServiceImpl.java:163):
|
||||||
|
|
||||||
|
1. 按回调里的 `taskId` 查 `ban_lift_task`。
|
||||||
|
2. 找到该任务下所有 `ban_lift_url`。
|
||||||
|
3. 更新每条 URL 的:
|
||||||
|
- `status`
|
||||||
|
- `operate`
|
||||||
|
- `start_time`
|
||||||
|
- `end_time`
|
||||||
|
4. 再通过 `urlBanLiftTaskDao.save(networkURLBanLiftTask)` 保存。
|
||||||
|
|
||||||
|
所以业务上主要修改的是:
|
||||||
|
|
||||||
|
- `ban_lift_url`
|
||||||
|
|
||||||
|
同时因为是通过父对象 `ban_lift_task` 保存,JPA 可能会触发 `ban_lift_task.updated_time` 更新;但任务主表没有业务字段被主动改,关联表 `ban_lift_task_url` 也不会变。
|
||||||
|
|
||||||
|
旧链路的回调不是这个接口,它会更新 `deny_allow_task` 的 `success_rate`、`end_time`、`status`、`task_state`;但你贴的 `banCallBack` 不会改 `deny_allow_task`。
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
MCP 协议的实现,可以有2种方式,
|
||||||
|
|
||||||
|
1. stdio,把 jar 提供出去,别人本地配置引入即可使用,但这样的方式不太适合做统一网关服务。
|
||||||
|
2. sse
|
||||||
|
|
||||||
|
项目采用 [DDD](DDD.md) 架构,首先聚焦 domain 层。
|
||||||
|
|
||||||
|
### 会话管理服务
|
||||||
|
|
||||||
|
```java
|
||||||
|
private final ScheduledExecutorService cleanupScheduler = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
```
|
||||||
|
|
||||||
|
其中 private 说明这个变量这能在类内部访问,声明为 final 则此变量只能赋值一次。`newSingleThreadScheduledExecutor` 代表只有一个工作线程。
|
||||||
|
|
||||||
|
> 一个带有定时功能的单线程定时队列。
|
||||||
|
|
||||||
|
```java
|
||||||
|
private final Map<String, SessionConfigVO> activeSessions = new ConcurrentHashMap<>();
|
||||||
|
```
|
||||||
|
|
||||||
|
考虑点在于这个会话是否会被多个线程操作处理。
|
||||||
|
|
||||||
|
```java
|
||||||
|
public SessionManagementService() {
|
||||||
|
cleanupScheduler.scheduleAtFixedRate(this::cleanupExpiredSessions, 5, 5, TimeUnit.MINUTES);
|
||||||
|
log.info("会话管理服务已启动,会话超时时间: {} 分钟", SESSION_TIMEOUT_MINUTES);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
可以在构造中执行一些方法。
|
||||||
|
|
||||||
|
### 实时通讯
|
||||||
|
|
||||||
|
> 对立面轮训
|
||||||
|
|
||||||
|
SSE(Server sent Events)
|
||||||
|
|
||||||
|
基于普通的 HTTP 长连接,Content-type: text/event-stream
|
||||||
|
|
||||||
|
消息格式,每条消息以 \n\n 结尾,
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```
|
||||||
|
SSE(协议)
|
||||||
|
↑
|
||||||
|
需要一个好的服务器框架来实现它
|
||||||
|
↓
|
||||||
|
Spring WebFlux(响应式 Web 框架) ← 最适合实现 SSE 的框架
|
||||||
|
↑
|
||||||
|
因为它是响应式(非阻塞),非常适合长连接
|
||||||
|
↓
|
||||||
|
在 WebFlux 里,用 Sinks + Flux<ServerSentEvent> 来产生和推送 SSE 事件
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
客户端发起 SSE 请求
|
||||||
|
↓
|
||||||
|
Netty EventLoop 接收请求(线程A)
|
||||||
|
↓
|
||||||
|
Controller 返回 sink.asFlux() → 注册“有新事件时推送”的回调
|
||||||
|
↓
|
||||||
|
线程A 立刻释放,去处理其他请求
|
||||||
|
↓
|
||||||
|
...(连接保持打开,线程A 忙别的)
|
||||||
|
|
||||||
|
你的服务层调用 sink.tryEmitNext(新消息)
|
||||||
|
↓
|
||||||
|
Reactor 通知 Netty:“这个连接有数据要写”
|
||||||
|
↓
|
||||||
|
EventLoop 线程(可能是线程B)被唤醒
|
||||||
|
↓
|
||||||
|
执行回调:把消息转成 "data: xxx\n\n" 格式 → 写入 Socket
|
||||||
|
↓
|
||||||
|
写完后,线程B 立刻释放,继续干别的
|
||||||
|
```
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
| 方法 | 接口名称 | 入参描述 | 一句话作用 |
|
||||||
|
| ------ | ----------------------------------------------- | ------------------------------------------------ | ---------------------- |
|
||||||
|
| GET | `/api/mcp-tools` | `toolGroupId`、`displayName`、`pageNum`、`pageSize` | 分页查询动态工具列表 |
|
||||||
|
| GET | `/api/mcp-tools/{id}` | 工具 ID | 查询动态工具详情 |
|
||||||
|
| POST | `/api/mcp-tools` | `McpToolRequest` | 创建动态工具并注册到 MCP Server |
|
||||||
|
| PUT | `/api/mcp-tools/{id}` | 工具 ID、`McpToolRequest` | 更新动态工具并重新注册 |
|
||||||
|
| DELETE | `/api/mcp-tools/{id}` | 工具 ID | 删除动态工具并从 MCP Server 移除 |
|
||||||
|
| POST | `/api/mcp-tools/{id}/toggle` | 工具 ID | 启用或禁用动态工具 |
|
||||||
|
| POST | `/api/mcp-tools/{id}/tool-groups/{groupId}` | 工具 ID、工具组 ID | 将工具绑定到工具组 |
|
||||||
|
| DELETE | `/api/mcp-tools/{id}/tool-groups/{groupId}` | 工具 ID、工具组 ID | 解除工具与工具组绑定 |
|
||||||
|
| | | | |
|
||||||
|
| GET | `/api/tool-groups` | `pageNum`、`pageSize` | 分页查询工具组列表 |
|
||||||
|
| GET | `/api/tool-groups/{id}` | 工具组 ID | 查询工具组详情 |
|
||||||
|
| GET | `/api/tool-groups/{id}/tools` | 工具组 ID、`displayName` | 查询工具组关联的全部工具 |
|
||||||
|
| POST | `/api/tool-groups` | `McpToolGroupRequest` | 创建工具组并生成 API Key |
|
||||||
|
| PUT | `/api/tool-groups/{id}` | 工具组 ID、`McpToolGroupRequest` | 更新工具组名称或描述 |
|
||||||
|
| DELETE | `/api/tool-groups/{id}` | 工具组 ID | 删除工具组并解除相关关联 |
|
||||||
|
| POST | `/api/tool-groups/{id}/toggle` | 工具组 ID | 启用或禁用工具组 |
|
||||||
|
| POST | `/api/tool-groups/{id}/reset-api-key` | 工具组 ID | 重置工具组 API Key |
|
||||||
|
| | | | |
|
||||||
|
| GET | `/api/roles` | `pageNum`、`pageSize` | 分页查询角色列表 |
|
||||||
|
| GET | `/api/roles/{id}` | 角色 ID | 查询角色详情 |
|
||||||
|
| POST | `/api/roles` | `RoleRequest` | 创建角色并生成角色 Key |
|
||||||
|
| PUT | `/api/roles/{id}` | 角色 ID、`RoleRequest` | 更新角色名称或描述 |
|
||||||
|
| DELETE | `/api/roles/{id}` | 角色 ID | 删除角色并解除工具组关联 |
|
||||||
|
| POST | `/api/roles/{id}/toggle` | 角色 ID | 启用或禁用角色 |
|
||||||
|
| POST | `/api/roles/{id}/reset-key` | 角色 ID | 重置角色 Key |
|
||||||
|
| GET | `/api/roles/{id}/tool-groups` | 角色 ID | 查询角色可用工具组 |
|
||||||
|
| POST | `/api/roles/tool-groups` | `roleId`、`toolGroupId` | 给角色关联工具组 |
|
||||||
|
| DELETE | `/api/roles/{roleId}/tool-groups/{toolGroupId}` | 角色 ID、工具组 ID | 移除角色的工具组关联 |
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
```java
|
||||||
|
package cn.bugstack.ai.infrastructure.gateway;
|
||||||
|
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import okhttp3.ResponseBody;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.Body;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.HeaderMap;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
import retrofit2.http.QueryMap;
|
||||||
|
import retrofit2.http.Url;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资料:<a href="https://bugstack.cn/md/road-map/http.html">HTTP 框架案例</a>
|
||||||
|
*/
|
||||||
|
public interface GenericHttpGateway {
|
||||||
|
|
||||||
|
@POST
|
||||||
|
Call<ResponseBody> post(
|
||||||
|
@Url String url,
|
||||||
|
@HeaderMap Map<String, Object> headers,
|
||||||
|
@Body RequestBody body
|
||||||
|
);
|
||||||
|
|
||||||
|
@GET
|
||||||
|
Call<ResponseBody> get(
|
||||||
|
@Url String url,
|
||||||
|
@HeaderMap Map<String, Object> headers,
|
||||||
|
@QueryMap Map<String, Object> queryParams
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
```
|
|
||||||
telegrambots 相关源码路径:D:\MyCode\Study\TelegramBots
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.telegram</groupId>
|
|
||||||
<artifactId>telegrambots-longpolling</artifactId>
|
|
||||||
<version>${telegrambots.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
因为我是 Spring Boot 项目,那这里是不是用官方推荐的 telegrambots-springboot-longpolling-starter 更方便呢?
|
|
||||||
|
|
||||||
现在请帮我实现一个最基础的机器人的功能:
|
|
||||||
|
|
||||||
1. 双向机器人的功能,当有人给这个机器人发消息的时候,可以通知到我。可以参考下比较成熟的双项机器人实现的手段。
|
|
||||||
2. 给我提供一个前台页面,让我可以在上面发布通知,使得所有使用我这个机器人的用户都能收到消息
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
7786028912:AAGxQ1jmD4a7j6k-KGsSainVpsFGplSXp-0
|
|
||||||
```
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
```
|
|
||||||
测试云主机信息 172.21.30.23 用户名:root 密码:Melo@3023
|
|
||||||
测试mysql: 172.21.33.224 用户名:cosmic_ai 密码:Cosmic@3306
|
|
||||||
测试redis: 172.21.43.68:6379 密码:chRdw@redis
|
|
||||||
|
|
||||||
1.DeepSeek-671B http://36.137.208.165:5020/v1/chat/completions
|
|
||||||
2.Qwen3-8B http://172.21.9.104:31554/v1/chat/completions
|
|
||||||
|
|
||||||
redis-cli -h 127.0.0.1 -p 6379 -a '5sTb7fHFbsYl6KmI6pvC^XMw!7Y^Pbc1'
|
|
||||||
```
|
|
||||||
|
|
||||||
| 项目 | 配置 |
|
|
||||||
| --- | ------------------ |
|
|
||||||
| 系统 | Anolis OS 8.6 |
|
|
||||||
| 内核 | 4.18 |
|
|
||||||
| CPU | Intel Xeon Skylake |
|
|
||||||
| 核心 | **8 vCPU** |
|
|
||||||
| 主频 | 2.2 GHz |
|
|
||||||
| 虚拟化 | KVM |
|
|
||||||
| 架构 | x86_64 |
|
|
||||||
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 637 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 962 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 683 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 309 KiB |
|
Before Width: | Height: | Size: 343 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 637 KiB |
|
Before Width: | Height: | Size: 588 KiB |
|
Before Width: | Height: | Size: 7.3 MiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 285 KiB |
|
Before Width: | Height: | Size: 512 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 704 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 216 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 99 KiB |