diff --git a/cache/timer.yaml b/cache/timer.yaml index ff0c11e..c484443 100644 --- a/cache/timer.yaml +++ b/cache/timer.yaml @@ -1 +1 @@ -licenseupdate: 1746647584 +licenseupdate: 1747254971 diff --git a/settings/kixote.yaml b/settings/kixote.yaml index b4771d1..9a6e076 100644 --- a/settings/kixote.yaml +++ b/settings/kixote.yaml @@ -39,3 +39,11 @@ promptlist: content: 'Return pure mermaid syntax for a [pie, x, y] diagram.' active: true system: true + example: + title: example + content: 'Please use the following and write an article.' + active: true + system: false + errors: + title: false + body: false diff --git a/system/typemill/Controllers/ControllerApiAuthorMeta.php b/system/typemill/Controllers/ControllerApiAuthorMeta.php index 9465712..4036586 100644 --- a/system/typemill/Controllers/ControllerApiAuthorMeta.php +++ b/system/typemill/Controllers/ControllerApiAuthorMeta.php @@ -34,17 +34,15 @@ class ControllerApiAuthorMeta extends Controller $metadata = $meta->getMetaData($item); - if(!$metadata or !isset($metadata['meta']['owner']) OR !$metadata['meta']['owner']) + if( + !$metadata or + !isset($metadata['meta']['owner']) OR + !$metadata['meta']['owner'] + ) { $metadata = $meta->addMetaDefaults($metadata, $item, $this->settings['author'], $request->getAttribute('c_username')); } - - #fix for version 1 because owner in meta is often 'false' - if(!isset($metadata['meta']['owner']) OR !$metadata['meta']['owner']) - { - $metadata = $meta->addMetaDefaults($metadata, $item, $this->settings['author'], $request->getAttribute('c_username')); - } - + # if user is not allowed to perform this action (e.g. not admin) if(!$this->userroleIsAllowed($request->getAttribute('c_userrole'), 'content', 'read')) { diff --git a/system/typemill/Controllers/ControllerApiKixote.php b/system/typemill/Controllers/ControllerApiKixote.php index 2133a21..d744527 100644 --- a/system/typemill/Controllers/ControllerApiKixote.php +++ b/system/typemill/Controllers/ControllerApiKixote.php @@ -5,6 +5,8 @@ namespace Typemill\Controllers; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ResponseInterface as Response; use Typemill\Models\Validation; +use Typemill\Models\Navigation; +use Typemill\Models\Content; use Typemill\Models\License; use Typemill\Models\Settings; use Typemill\Models\User; @@ -19,7 +21,7 @@ class ControllerApiKixote extends Controller { $system = 'You are a content editor and writing assistant.' . ' If the user prompt does not explicitly specify otherwise,' - . ' apply the prompt to the provided article and return only the updated article in Markdown syntax,' + . ' apply the prompt to the provided article inside the
tag and return only the updated article in Markdown syntax,' . ' without any extra comments or explanations.' . ' If you find the tag ,' . ' modify only the content inside these tags and leave everything else unchanged.' @@ -261,7 +263,7 @@ class ControllerApiKixote extends Controller public function prompt(Request $request, Response $response) { - $params = $request->getParsedBody(); + $params = $request->getParsedBody(); if (empty($params['prompt']) || !is_string($params['prompt'])) { @@ -282,8 +284,35 @@ class ControllerApiKixote extends Controller $promptname = $params['name'] ?? ''; $prompt = $params['prompt'] ?? ''; $article = $params['article'] ?? ''; - $tone = $params['tone'] ?? ''; - + $example = $params['link'] ?? false; + + if($example) + { + $validate = new Validation(); + $validInput = $validate->articleUrl(['url' => $params['link']]); + if($validInput === true) + { + $urlinfo = $this->c->get('urlinfo'); + $langattr = $this->settings['langattr']; + $navigation = new Navigation(); + $item = $navigation->getItemForUrl($params['url'], $urlinfo, $langattr); + if($item) + { + $content = new Content($urlinfo['baseurl'], $this->settings, $this->c->get('dispatcher')); + $markdown = $content->getDraftMarkdown($item); + if($markdown) + { + if(is_array($markdown)) + { + $markdown = $content->markdownArrayToText($markdown); + } + + $example = $markdown; + } + } + } + } + $aiservice = $this->settings['aiservice'] ?? false; if(!$aiservice) { @@ -295,11 +324,11 @@ class ControllerApiKixote extends Controller switch ($aiservice) { case 'chatgpt': - $answer = $this->promptChatGPT($promptname, $prompt, $article, $tone); + $answer = $this->promptChatGPT($promptname, $prompt, $article, $example); break; case 'claude': - $answer = $this->promptClaude($promptname, $prompt, $article, $tone); + $answer = $this->promptClaude($promptname, $prompt, $article, $example); break; default: @@ -324,7 +353,7 @@ class ControllerApiKixote extends Controller return $response->withHeader('Content-Type', 'application/json')->withStatus(200); } - public function promptChatGPT($promptname, $prompt, $article, $tone) + public function promptChatGPT($promptname, $prompt, $article, $example) { # check if user has accepted @@ -341,6 +370,13 @@ class ControllerApiKixote extends Controller $url = 'https://api.openai.com/v1/chat/completions'; $authHeader = "Authorization: Bearer $apikey"; + $content = $prompt; + if($example) + { + $content .= "\n" . $example . ""; + } + $content .= "\n
" . $article . "
"; + $postdata = [ 'model' => $model, 'messages' => [ @@ -350,7 +386,7 @@ class ControllerApiKixote extends Controller ], [ 'role' => 'user', - 'content' => $prompt . "\n" . $article + 'content' => $content ], ], 'temperature' => 0.7, @@ -391,7 +427,7 @@ class ControllerApiKixote extends Controller return $answer; } - public function promptClaude($promptname, $prompt, $article, $tone) + public function promptClaude($promptname, $prompt, $article, $example) { # Check if user has accepted $settingsModel = new Settings(); @@ -410,13 +446,20 @@ class ControllerApiKixote extends Controller "anthropic-version: 2023-06-01" ]; + $content = $prompt; + if($example) + { + $content .= "\n" . $example . ""; + } + $content .= "\n
" . $article . "
"; + $postdata = [ 'model' => $model, 'system' => $this->getSystemMessage(), 'messages' => [ [ 'role' => 'user', - 'content' => $prompt . "\n" . $article + 'content' => $content ], ], 'temperature' => 0.7, diff --git a/system/typemill/author/js/vue-kixote.js b/system/typemill/author/js/vue-kixote.js index d40ea3d..e9c4ff9 100644 --- a/system/typemill/author/js/vue-kixote.js +++ b/system/typemill/author/js/vue-kixote.js @@ -39,6 +39,7 @@ const kixote = Vue.createApp({ :is = "currentTabComponent" :command = "command" :content = "content" + :navigation = "navigation" :item = "item" :useragreement = "useragreement" :aiservice = "aiservice" @@ -149,6 +150,7 @@ const kixote = Vue.createApp({ loading: false, item: data.item, content: data.content, + navigation: data.navigation, urlinfo: data.urlinfo, labels: data.labels, settings: data.settings, @@ -319,19 +321,19 @@ const kixote = Vue.createApp({ }) kixote.component('tab-generate', { - props: ['content', 'item', 'labels', 'urlinfo', 'settings', 'kixoteSettings', 'settingsSaved', 'aiservice', 'useragreement', 'tokenstats'], + props: ['content', 'navigation', 'item', 'labels', 'urlinfo', 'settings', 'kixoteSettings', 'settingsSaved', 'aiservice', 'useragreement', 'tokenstats'], data: function () { return { tabs: [ { value: 'article', name: 'Article' }, { value: 'prompts', name: 'Prompts' }, -/* { value: 'tone', name: 'Tone' }, */ ], currentTab: 'article', originalmd: '', activeversion: 0, versions: [], prompt: '', + promptlink: null, promptError: false, showFocusButton: false, buttonPosition: { top: 0, left: 0 }, @@ -342,13 +344,15 @@ kixote.component('tab-generate', { title: '', content: '', active: true, - system: false + system: false, + link: null, }, titleError: false, bodyError: false, currentFilter: 'all', article: '', index: '', + flatnavi: false, }; }, template: `
@@ -508,7 +512,7 @@ kixote.component('tab-generate', {
Filter: @@ -545,6 +549,7 @@ kixote.component('tab-generate', { {{ titleError }} {{ bodyError }} + +
+ +
+
-
-

Mimic your tone tab content here.

-
+
+
+
+ +
+ + +
+
+ + + + + + +
+ + + + + + + +
+ +
+
+
+ +
+
@@ -636,6 +712,8 @@ kixote.component('tab-generate', { { this.initAutosize(); + this.createFlatNavi(); + if(this.versions.length == 0) { this.initializeContent() @@ -668,22 +746,27 @@ kixote.component('tab-generate', { Object.entries(this.kixoteSettings?.promptlist || {}).filter(([key, prompt]) => !prompt.system) ); }, - filteredPrompts() - { - if(this.currentFilter === 'system') - { - return this.promptlistsystem; - } - else if (this.currentFilter === 'user') - { - return this.promptlistuser; - } - else - { - return this.kixoteSettings.promptlist; - } - } + filteredPrompts() + { + const list = + this.currentFilter === 'system' + ? this.promptlistsystem + : this.currentFilter === 'user' + ? this.promptlistuser + : this.kixoteSettings.promptlist; + // Normalize `link` to `null` if not present + const normalized = {}; + for (const [key, prompt] of Object.entries(list)) + { + normalized[key] = { + ...prompt, + link: prompt.link ?? null + }; + } + + return normalized; + } }, methods: { initAutosize() @@ -737,6 +820,25 @@ kixote.component('tab-generate', { this.versions.push(markdown); this.resizeAiEditor(); }, + createFlatNavi() { + if (this.navigation && !this.flatnavi) { + const nestedNavi = []; + + const recurse = (items) => { + items.forEach(item => { + if (item.urlRelWoF) { + nestedNavi.push(item.urlRelWoF); + } + if (item.folderContent && item.folderContent.length > 0) { + recurse(item.folderContent); + } + }); + }; + + recurse(this.navigation); + this.flatnavi = nestedNavi; + } + }, resizeAiEditor() { this.$nextTick(() => { @@ -760,6 +862,7 @@ kixote.component('tab-generate', { usePrompt(index) { this.prompt = this.promptlistactive[index].content; + this.promptlink = this.promptlistactive[index].link; this.resizePromptEditor(); }, switchVersion(index) @@ -776,7 +879,8 @@ kixote.component('tab-generate', { tmaxios.post('/api/v1/prompt',{ 'prompt': this.prompt, - 'article': this.versions[this.activeversion] + 'article': this.versions[this.activeversion], + 'link': this.promptlink }) .then(function (response) { @@ -788,6 +892,7 @@ kixote.component('tab-generate', { self.versions.push(answer); self.activeversion = self.versions.length-1; self.prompt = ''; + self.promptlink = null; self.resizePromptEditor(); self.resizeAiEditor(); } @@ -997,6 +1102,10 @@ kixote.component('tab-generate', { this.addNewPrompt = false; this.updateSettings(promptlist); eventBus.$emit('storeKixoteSettings'); + }, + getArticleMarkdown(url) + { + }, exit() {