diff --git a/content/01-publish-status/index.md b/content/01-publish-status/index.md
index 1500d60..0105137 100644
--- a/content/01-publish-status/index.md
+++ b/content/01-publish-status/index.md
@@ -1,14 +1,13 @@
-# Publish Status
+# Publish Status
-In Typemill you can save drafts, publish pages, unpublish pages, and delete pages with a sticky publish panel at the bottom of each page. For published pages, you can also save modifications, discard modifications and publish modifications.
+In Typemill, you can save drafts, publish pages, unpublish pages, and delete pages using a sticky publish panel at the bottom of each page. For published pages, you can also save modifications, discard modifications, and publish modifications.
The current status of the page is indicated with colors in the publish panel and in the navigation. This way, an author can always see the status of each page and the current state of the whole website.
-* **Save a draft**: The page is not published and not accessible in the frontend. The status color is red.
-* **Publish a page**: The page is published and accessible in the frontend. The status color is green.
-* **Unpublish a page**: The page is not accessible in the frontend and only accessible in the author interface. The status color is red again.
-* **Delete a page**: The page is completely deleted in frontend and in the author interface.
-* **Save modivications**: The page is published and accessible in the frontend. The modified draft is only visible in the author interface. The status color is orange.
-* **Discard modifications**: The modifications are deleted and the live version is restored in the author interface. The status color is green again.
-* **Publish modifications**: The modifications are published to the live version. The status color is green again.
-
+- **Save a draft**: The page is not published and not accessible on the frontend. The status color is red.
+- **Publish a page**: The page is published and accessible on the frontend. The status color is green.
+- **Unpublish a page**: The page is not accessible on the frontend and only accessible in the author interface. The status color is red again.
+- **Delete a page**: The page is completely deleted from the frontend and the author interface.
+- **Save modifications**: The page is published and accessible on the frontend. The modified draft is only visible in the author interface. The status color is orange.
+- **Discard modifications**: The modifications are deleted, and the live version is restored in the author interface. The status color returns to green.
+- **Publish modifications**: The modifications are published to the live version. The status color returns to green.
\ No newline at end of file
diff --git a/system/typemill/Controllers/ControllerApiKixote.php b/system/typemill/Controllers/ControllerApiKixote.php
new file mode 100644
index 0000000..94ea913
--- /dev/null
+++ b/system/typemill/Controllers/ControllerApiKixote.php
@@ -0,0 +1,377 @@
+getKixoteSettings();
+
+ if(!$kixoteSettings)
+ {
+ $response->getBody()->write(json_encode([
+ 'message' => 'could not load kixote settings.'
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ # send to Kixote
+ $response->getBody()->write(json_encode([
+ 'settings' => $kixoteSettings
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
+
+ }
+
+ public function updateKixoteSettings(Request $request, Response $response)
+ {
+ $params = $request->getParsedBody();
+ $kixoteSettings = $params['kixotesettings'] ?? false;
+ $validate = new Validation();
+
+ if(isset($kixoteSettings['promptlist']))
+ {
+ $promptErrors = false;
+ foreach($kixoteSettings['promptlist'] as $name => $values)
+ {
+ $validInput = $validate->kixotePrompt($values);
+ if($validInput !== true)
+ {
+ $promptErrors = true;
+ $kixoteSettings['promptlist'][$name]['errors'] = $validInput;
+ }
+ else
+ {
+ unset($kixoteSettings['promptlist'][$name]['errors']);
+ }
+ }
+
+ if($promptErrors)
+ {
+ $response->getBody()->write(json_encode([
+ 'message' => 'please correct the errors in the form',
+ 'kixotesettings' => $kixoteSettings
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ }
+
+ $settingsModel = new Settings();
+ $result = $settingsModel->updateKixoteSettings($kixoteSettings);
+
+ if(!$result)
+ {
+ # restore the current kixote-settings
+ $kixoteSettings = $settingsModel->getKixoteSettings();
+
+ $response->getBody()->write(json_encode([
+ 'message' => 'error while saving settings.',
+ 'kixotesettings' => $kixoteSettings
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ # send to Kixote
+ $response->getBody()->write(json_encode([
+ 'kixotesettings' => $kixoteSettings
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
+ }
+
+ # initial token statistics
+ public function getTokenStats(Request $request, Response $response): Response
+ {
+ $aiservice = false;
+ $tokenstats = 0;
+ $useragreement = false;
+ $user = new User();
+ $username = $request->getAttribute('c_username');
+
+ if(!$user->setUser($username))
+ {
+ $response->getBody()->write(json_encode([
+ 'message' => Translations::translate('We did not find the a user.')
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ if(isset($this->settings['aiservice']) && $this->settings['aiservice'] !== 'none')
+ {
+ $aiservice = $this->settings['aiservice'];
+ }
+
+ if($aiservice)
+ {
+ $userdata = $user->getUserData();
+ if(isset($userdata['aiservices']) && in_array($aiservice, $userdata['aiservices']))
+ {
+ $useragreement = true;
+ }
+ }
+
+ # get toke stats for AI service
+ if($aiservice && $useragreement)
+ {
+ switch ($aiservice)
+ {
+ case 'chatgpt':
+ $tokenstats = [
+ 'url' => 'https://platform.openai.com/settings/organization/billing/overview',
+ 'label' => 'ChatGPT Billing'
+ ];
+ break;
+
+ default:
+ $tokenstats = 0;
+ break;
+ }
+ }
+
+ if($tokenstats === false)
+ {
+ $response->getBody()->write(json_encode([
+ 'message' => Translations::translate('Could not get tokenstats.')
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ $response->getBody()->write(json_encode([
+ 'message' => 'Success',
+ 'aiservice' => $aiservice,
+ 'useragreement' => $useragreement,
+ 'tokenstats' => $tokenstats
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
+ }
+
+ # initial token statistics
+ public function agreeToAiService(Request $request, Response $response): Response
+ {
+ $aiservice = false;
+ $user = new User();
+ $username = $request->getAttribute('c_username');
+
+ if(!$user->setUserWithPassword($username))
+ {
+ $response->getBody()->write(json_encode([
+ 'message' => Translations::translate('We did not find the a user or usermail.')
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ if(isset($this->settings['aiservice']) && $this->settings['aiservice'] !== 'none')
+ {
+ $aiservice = $this->settings['aiservice'];
+ }
+ else
+ {
+ $response->getBody()->write(json_encode([
+ 'message' => Translations::translate('No valid ai service has been selected.')
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ $agreements = $user->getValue('aiservices');
+
+ if(!$agreements)
+ {
+ $agreements = [$aiservice];
+ }
+ elseif(!isset($agreements[$aiservice]))
+ {
+ $agreements[] = $aiservice;
+ }
+
+ $user->setValue('aiservices', $agreements);
+ if($user->updateUser() !== true)
+ {
+ $response->getBody()->write(json_encode([
+ 'message' => Translations::translate('We could not update your user settings, please try again or agree to ' . $aiservice . ' in your user profile.')
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ $response->getBody()->write(json_encode([
+ 'message' => 'Success'
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
+ }
+
+ private function getKixoteJWT(Request $request, Response $response)
+ {
+ # this will authenticate from service.typemill.net (e.g. for template service)
+ $license = new License();
+ $jwt = $license->getToken();
+ if($jwt)
+ {
+ $this->error = $license->getMessage();
+ return false;
+ }
+
+ # if no agb-confirmation
+ $confirm = $settings['kixote_confirm'] ?? false;
+ if(!$confirm)
+ {
+ $this->error = 'Please read and accept the AGB before you start with our service.';
+ return false;
+ }
+
+ return $jwt;
+ }
+
+ public function promptKixote(Request $request, Response $response)
+ {
+ $jwt = $this->getKixoteJWT();
+ if(!$jwt)
+ {
+ $response->getBody()->write(json_encode([
+ 'message' => $this->error
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ $params = $request->getParsedBody();
+
+ $params['name'] = ''; # will trigger some cool stuff in kixote
+ $params['prompt'] = ''; # the prompt itself
+ $params['article'] = ''; # the current article
+ $params['tone'] = ''; # the tone
+
+ if(!isset($params['prompt']) OR !is_array($params['article']))
+ {
+ $response->getBody()->write(json_encode([
+ 'message' => Translations::translate('Prompt or article missing.')
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ # validate input
+ $validate = new Validation();
+ $validationresult = $validate->newLicense($params['license']);
+ if($validationresult !== true)
+ {
+ $response->getBody()->write(json_encode([
+ 'message' => Translations::translate('Please correct your input.'),
+ 'errors' => $validate->returnFirstValidationErrors($validationresult)
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ # send to Kixote
+ $response->getBody()->write(json_encode([
+ 'message' => Translations::translate('Licence has been stored'),
+ 'licensedata' => $licensedata
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
+ }
+
+ public function promptChatGPT(Request $request, Response $response): Response
+ {
+ # check if user has accepted
+
+ $params = $request->getParsedBody();
+
+ $params['name'] = $params['name'] ?? '';
+ $params['prompt'] = $params['prompt'] ?? '';
+ $params['article'] = $params['article'] ?? '';
+ $params['tone'] = $params['tone'] ?? '';
+
+ $settingsModel = new Settings();
+ $model = $this->settings['chatgptModel'] ?? false;
+ $apikey = $settingsModel->getSecret('chatgptKey');
+
+ if (empty($params['prompt']) || !is_string($params['prompt']))
+ {
+ $response->getBody()->write(json_encode([
+ 'message' => 'Prompt is missing or invalid.'
+ ]));
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ if (empty($params['article']) || !is_string($params['article']))
+ {
+ $response->getBody()->write(json_encode([
+ 'message' => 'Article is missing or invalid.'
+ ]));
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ if (!$model || !$apikey)
+ {
+ $response->getBody()->write(json_encode([
+ 'message' => 'Model or api key for chatgpt is missing, please add it in the system settings.'
+ ]));
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ $url = 'https://api.openai.com/v1/chat/completions';
+ $authHeader = "Authorization: Bearer $apikey";
+
+ $postdata = [
+ 'model' => $model,
+ 'messages' => [
+ [
+ 'role' => 'system',
+ 'content' => '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, without any extra comments or explanations. If you find the tag , modify only the content inside these tags and leave everything else unchanged. Always return the full article.'
+ ],
+ [
+ 'role' => 'user',
+ 'content' => $params['prompt'] . "\n" . $params['article']
+ ],
+ ],
+ 'temperature' => 0.7,
+ 'max_tokens' => 2000,
+ ];
+
+ $apiservice = new ApiCalls();
+ $apiResponse = $apiservice->makePostCall($url, $postdata, $authHeader);
+
+ if (!$apiResponse)
+ {
+ $response->getBody()->write(json_encode([
+ 'message' => 'Failed to communicate with ChatGPT',
+ 'error' => $apiservice->getError()
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(400);
+ }
+
+ $data = json_decode($apiResponse, true);
+ $response->getBody()->write(json_encode([
+ 'message' => 'Success',
+ 'data' => $data,
+ ]));
+
+ return $response->withHeader('Content-Type', 'application/json')->withStatus(200);
+ }
+}
\ No newline at end of file
diff --git a/system/typemill/Controllers/ControllerApiSystemPlugins.php b/system/typemill/Controllers/ControllerApiSystemPlugins.php
index 8940f93..d79fb7e 100644
--- a/system/typemill/Controllers/ControllerApiSystemPlugins.php
+++ b/system/typemill/Controllers/ControllerApiSystemPlugins.php
@@ -20,7 +20,7 @@ class ControllerApiSystemPlugins extends Controller
$extension = new Extension();
$formdefinitions = $extension->getPluginDefinition($pluginname);
$formdefinitions = $this->addDatasets($formdefinitions['forms']['fields']);
- $plugindata = [];
+# $plugindata = [];
# validate input
$validator = new Validation();
@@ -43,8 +43,20 @@ class ControllerApiSystemPlugins extends Controller
}
# store updated settings here
- $settings = new Settings();
- $updatedSettings = $settings->updateSettings($validatedOutput, 'plugins', $pluginname);
+ $settingsModel = new Settings();
+ $securityFields = $settingsModel->findSecurityDefinitions($formdefinitions);
+ if(!empty($securityFields))
+ {
+ $splitSettings = $settingsModel->extractSecuritySettings($validatedOutput, $securityFields);
+ $validatedOutput = $splitSettings['settings'];
+
+ if($splitSettings['securitySettings'] && !empty($splitSettings['securitySettings']))
+ {
+ $settingsModel->updateSecuritySettings($splitSettings['securitySettings'], 'plugins', $pluginname);
+ }
+ }
+
+ $updatedSettings = $settingsModel->updateSettings($validatedOutput, 'plugins', $pluginname);
$response->getBody()->write(json_encode([
'message' => Translations::translate('settings have been saved')
diff --git a/system/typemill/Controllers/ControllerApiSystemSettings.php b/system/typemill/Controllers/ControllerApiSystemSettings.php
index 6f11877..043b600 100644
--- a/system/typemill/Controllers/ControllerApiSystemSettings.php
+++ b/system/typemill/Controllers/ControllerApiSystemSettings.php
@@ -64,7 +64,19 @@ class ControllerApiSystemSettings extends Controller
}
}
- # store updated settings here
+ $securityFields = $settingsModel->findSecurityDefinitions($formdefinitions);
+
+ if(!empty($securityFields))
+ {
+ $splitSettings = $settingsModel->extractSecuritySettings($validatedOutput, $securityFields);
+ $validatedOutput = $splitSettings['settings'];
+
+ if($splitSettings['securitySettings'] && !empty($splitSettings['securitySettings']))
+ {
+ $settingsModel->updateSecuritySettings($splitSettings['securitySettings']);
+ }
+ }
+
$updatedSettings = $settingsModel->updateSettings($validatedOutput);
$response->getBody()->write(json_encode([
diff --git a/system/typemill/Controllers/ControllerApiSystemThemes.php b/system/typemill/Controllers/ControllerApiSystemThemes.php
index e8bc5bf..9048e74 100644
--- a/system/typemill/Controllers/ControllerApiSystemThemes.php
+++ b/system/typemill/Controllers/ControllerApiSystemThemes.php
@@ -40,8 +40,20 @@ class ControllerApiSystemThemes extends Controller
unset($validatedOutput['customcss']);
# store updated settings here
- $settings = new Settings();
- $updatedSettings = $settings->updateSettings($validatedOutput, 'themes', $themename);
+ $settingsModel = new Settings();
+ $securityFields = $settingsModel->findSecurityDefinitions($formdefinitions);
+ if(!empty($securityFields))
+ {
+ $splitSettings = $settingsModel->extractSecuritySettings($validatedOutput, $securityFields);
+ $validatedOutput = $splitSettings['settings'];
+
+ if($splitSettings['securitySettings'] && !empty($splitSettings['securitySettings']))
+ {
+ $settingsModel->updateSecuritySettings($splitSettings['securitySettings'], 'themes', $themename);
+ }
+ }
+
+ $updatedSettings = $settingsModel->updateSettings($validatedOutput, 'themes', $themename);
$response->getBody()->write(json_encode([
'message' => Translations::translate('settings have been saved')
diff --git a/system/typemill/Models/ApiCalls.php b/system/typemill/Models/ApiCalls.php
new file mode 100644
index 0000000..70cdf59
--- /dev/null
+++ b/system/typemill/Models/ApiCalls.php
@@ -0,0 +1,106 @@
+error;
+ }
+
+ public function makePostCall(string $url, array $data, $authHeader = '')
+ {
+ if (in_array('curl', get_loaded_extensions())) {
+ return $this->makeCurlCall($url, 'POST', $data, $authHeader);
+ }
+
+ return $this->makeFileGetContentsCall($url, 'POST', $data, $authHeader);
+ }
+
+ public function makeGetCall($url, $authHeader = '')
+ {
+ if (in_array('curl', get_loaded_extensions())) {
+ return $this->makeCurlCall($url, 'GET', null, $authHeader);
+ }
+
+ return $this->makeFileGetContentsCall($url, 'GET', null, $authHeader);
+ }
+
+ private function makeCurlCall($url, $method, $data = false, $authHeader = '')
+ {
+ $this->error = null;
+
+ $headers = [
+ "Content-Type: application/json",
+ ];
+
+ if (!empty($authHeader)) {
+ $headers[] = $authHeader;
+ }
+
+ $curl = curl_init($url);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+ if ($method === 'POST' && $data) {
+ $postdata = json_encode($data);
+ if ($postdata === false) {
+ $this->error = "JSON encoding error: " . json_last_error_msg();
+ return false;
+ }
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $postdata);
+ curl_setopt($curl, CURLOPT_POST, true);
+ }
+ curl_setopt($curl, CURLOPT_FAILONERROR, true);
+
+ $response = curl_exec($curl);
+
+ if ($response === false) {
+ $this->error = curl_error($curl);
+ }
+ curl_close($curl);
+
+ return $response !== false ? $response : false;
+ }
+
+ private function makeFileGetContentsCall($url, $method, $data = null, $authHeader = '')
+ {
+ $this->error = null;
+
+ $headers = [
+ "Content-Type: application/json"
+ ];
+
+ if (!empty($authHeader)) {
+ $headers[] = $authHeader;
+ }
+
+ $options = [
+ 'http' => [
+ 'method' => $method,
+ 'ignore_errors' => true,
+ 'header' => implode("\r\n", $headers),
+ ]
+ ];
+
+ if ($method === 'POST' && $data !== null) {
+ $postdata = json_encode($data);
+ if ($postdata === false) {
+ $this->error = "JSON encoding error: " . json_last_error_msg();
+ return false;
+ }
+ $options['http']['content'] = $postdata;
+ }
+
+ $context = stream_context_create($options);
+ $response = file_get_contents($url, false, $context);
+
+ if ($response === false) {
+ $this->error = 'file_get_contents failed for ' . $method . ' request.';
+ }
+
+ return $response !== false ? $response : false;
+ }
+}
diff --git a/system/typemill/Models/License.php b/system/typemill/Models/License.php
index 86ace69..0141ad2 100644
--- a/system/typemill/Models/License.php
+++ b/system/typemill/Models/License.php
@@ -87,7 +87,6 @@ class License
# check if license data are valid and not manipulated
$licenseStatus = $this->validateLicense($licensedata);
-
if($licenseStatus !== true)
{
$this->message = Translations::translate('The license data are invalid. ') . $this->message;
@@ -97,7 +96,6 @@ class License
# check if website uses licensed domain
$licenseDomain = $this->checkLicenseDomain($licensedata['domain'], $urlinfo);
-
if(!$licenseDomain)
{
$this->message = Translations::translate('The website is running not under the domain of your license.');
@@ -107,7 +105,6 @@ class License
# check if subscription period is paid
$subscriptionPaid = $this->checkLicenseDate($licensedata['payed_until']);
-
if(!$subscriptionPaid)
{
$storage = new StorageWrapper('\Typemill\Models\Storage');
diff --git a/system/typemill/Models/Settings.php b/system/typemill/Models/Settings.php
index dc611ec..44be506 100644
--- a/system/typemill/Models/Settings.php
+++ b/system/typemill/Models/Settings.php
@@ -107,6 +107,47 @@ class Settings
return false;
}
+ public function getKixoteSettings()
+ {
+ $defaultSettings = $this->storage->getYaml('systemSettings', '', 'kixote.yaml');
+ $userSettings = $this->storage->getYaml('settingsFolder', '', 'kixote.yaml');
+
+ if ($userSettings)
+ {
+ foreach ($defaultSettings['promptlist'] as $key => $prompt)
+ {
+ if (isset($userSettings['promptlist'][$key]))
+ {
+ # Use active setting from user but keep system settings intact
+ $active = $userSettings['promptlist'][$key]['active'];
+ $userSettings['promptlist'][$key] = $prompt;
+ $userSettings['promptlist'][$key]['active'] = $active;
+ }
+ else
+ {
+ # New prompt from system settings, add it to user settings
+ $userSettings['promptlist'][$key] = $prompt;
+ }
+ }
+ }
+ else
+ {
+ $userSettings = $defaultSettings;
+ }
+
+ return $userSettings;
+ }
+
+ public function updateKixoteSettings($kixoteSettings)
+ {
+ if($this->storage->updateYaml('settingsFolder', '', 'kixote.yaml', $kixoteSettings))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
public function getObjectSettings($objectType, $objectName)
{
$objectSettings = $this->storage->getYaml($objectType, $objectName, $objectName . '.yaml');
@@ -245,4 +286,101 @@ class Settings
return false;
}
+ public function findSecurityDefinitions($definitions, $securityDefinitions = [])
+ {
+ foreach ($definitions as $fieldname => $definition)
+ {
+ if (isset($definition['fields']))
+ {
+ $securityDefinitions = $this->findSecurityDefinitions($definition['fields'], $securityDefinitions);
+ }
+
+ if (isset($definition['type']) && $definition['type'] === 'password')
+ {
+ $securityDefinitions[] = $fieldname;
+ }
+ }
+
+ return $securityDefinitions;
+ }
+
+ public function extractSecuritySettings($settings, $securityFields)
+ {
+ $securitySettings = [];
+
+ foreach ($securityFields as $fieldname)
+ {
+ if (isset($settings[$fieldname]))
+ {
+ $securitySettings[$fieldname] = $settings[$fieldname];
+ unset($settings[$fieldname]);
+ }
+ }
+
+ return [
+ 'settings' => $settings,
+ 'securitySettings' => $securitySettings
+ ];
+ }
+
+ public function updateSecuritySettings($newSecuritySettings, $themeorplugin = null, $themeorpluginname = null)
+ {
+ # problem that settings with same name will overwrite (e.g. from theme and plugins)
+ $securitySettings = $this->getSecuritySettings();
+ foreach($newSecuritySettings as $fieldname => $value)
+ {
+ if($themeorplugin && $themeorpluginname)
+ {
+ $securitySettings[$themeorplugin][$themeorpluginname][$fieldname] = $value;
+ }
+ else
+ {
+ $securitySettings[$fieldname] = $value;
+ }
+ }
+
+ $secrets = $this->storage->updateYaml('settingsFolder', '', 'secrets.yaml', $securitySettings);
+ if($secrets)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private function getSecuritySettings()
+ {
+ $secrets = $this->storage->getYaml('settingsFolder', '', 'secrets.yaml');
+
+ if($secrets)
+ {
+ return $secrets;
+ }
+ return [];
+ }
+
+ public function getSecret(string $fieldname, $objecttype = null, $objectname = null)
+ {
+ $secrets = $this->storage->getYaml('settingsFolder', '', 'secrets.yaml');
+
+ if(!$secrets)
+ {
+ return false;
+ }
+
+ if($fieldname && $objecttype && $objectname)
+ {
+ if(isset($secrets[$objecttype][$objectname][$fieldname]))
+ {
+ return $secrets[$objecttype][$objectname][$fieldname];
+ }
+ }
+
+ if($fieldname && isset($secrets[$fieldname]))
+ {
+ return $secrets[$fieldname];
+ }
+
+ return false;
+ }
}
\ No newline at end of file
diff --git a/system/typemill/Models/Validation.php b/system/typemill/Models/Validation.php
index c786a43..0e14332 100644
--- a/system/typemill/Models/Validation.php
+++ b/system/typemill/Models/Validation.php
@@ -682,6 +682,26 @@ class Validation
return $v->errors();
}
+ public function kixotePrompt(array $params)
+ {
+ $v = new Validator($params);
+
+ $v->rule('required', 'title', 'content', 'active', 'system');
+ $v->rule('regex', 'title', '/^[a-z0-9 ]+$/i');
+ $v->rule('lengthBetween', 'title', 2,20);
+ $v->rule('noHTML', 'content');
+ $v->rule('lengthBetween', 'content',2,5000);
+ $v->rule('boolean', 'active');
+ $v->rule('boolean', 'system');
+
+ if($v->validate())
+ {
+ return true;
+ }
+
+ return $v->errors();
+ }
+
/**
* validation for password recovery
*
@@ -848,7 +868,7 @@ class Validation
$v->rule('lengthMax', $fieldName, 10000);
break;
case "password":
- $v->rule('lengthMax', $fieldName, 100);
+ $v->rule('lengthMax', $fieldName, 500);
break;
case "radio":
$v->rule('in', $fieldName, $fieldDefinitions['options']);
diff --git a/system/typemill/author/css/custom.css b/system/typemill/author/css/custom.css
index 95f52ec..12c18b5 100644
--- a/system/typemill/author/css/custom.css
+++ b/system/typemill/author/css/custom.css
@@ -1,5 +1,79 @@
+.iconwrapper {
+ position:relative;
+ height:60px;
+ width:60px;
+ overflow: hidden;
+ border-radius: 30%;
+}
+.magicicon{
+ position: absolute;
+ top:10px;
+ left: 10px;
+ bottom: 10px;
+ right: 10px;
+ color: white;
+ animation: glow .5s infinite alternate;
+}
+@keyframes glow {
+ 0% {
+ fill: black;
+ filter: drop-shadow(0 0 5px black);
+ }
+ 100% {
+ fill: rgb(15, 50, 46);
+ filter: drop-shadow(0 0 15px rgb(15, 50, 46));
+ }
+}
+
+.loader {
+ width: 1px;
+ height:80px;
+ background: #ccc;
+ box-shadow: 0 0 60px 10px #eee;
+ transform: translate(-20px);
+ clip-path: inset(0);
+ animation:
+ l4-1 1s ease-in-out infinite alternate,
+ l4-2 2s ease-in-out infinite;
+}
+@keyframes l4-1 {
+ 100% {transform: translateX(80px)}
+}
+@keyframes l4-2 {
+ 33% {clip-path: inset(0 0 0 -100px)}
+ 50% {clip-path: inset(0 0 0 0) }
+ 83% {clip-path: inset(0 -100px 0 0)}
+}
+
+
+@keyframes wobble-line-with-trailing-shadow {
+ 0% {
+ transform: translateY(0);
+ box-shadow: 0 0 15px 5px rgba(255, 255, 255, 0.8),
+ 0 20px 30px rgba(255, 255, 255, 0.6),
+ 0 40px 60px rgba(255, 255, 255, 0.4);
+ }
+ 50% {
+ transform: translateY(60px);
+ box-shadow: 0 0 15px 5px rgba(255, 255, 255, 0.8),
+ 0 20px 30px rgba(255, 255, 255, 0.6),
+ 0 40px 60px rgba(255, 255, 255, 0.4);
+ }
+ 100% {
+ transform: translateY(0);
+ box-shadow: 0 0 15px 5px rgba(255, 255, 255, 0.8),
+ 0 20px 30px rgba(255, 255, 255, 0.6),
+ 0 40px 60px rgba(255, 255, 255, 0.4);
+ }
+}
+
+#loading-overlay .animate-wobble-line-with-trailing-shadow {
+ animation: wobble-line-with-trailing-shadow 2s ease-in-out infinite;
+ background: white;
+}
+
/********************
-* SVG ICONS *
+* SVG ICONS *
********************/
.icon {
@@ -12,7 +86,7 @@
}
.icon.baseline{
top: 0.125em;
- position: relative;
+ position: relative;
}
diff --git a/system/typemill/author/css/output.css b/system/typemill/author/css/output.css
index 453aded..3e52b95 100644
--- a/system/typemill/author/css/output.css
+++ b/system/typemill/author/css/output.css
@@ -862,6 +862,10 @@ video {
margin-top: 0.5rem;
}
+.mt-20 {
+ margin-top: 5rem;
+}
+
.mt-3 {
margin-top: 0.75rem;
}
@@ -1026,18 +1030,10 @@ video {
width: 5rem;
}
-.w-24 {
- width: 6rem;
-}
-
.w-3\/4 {
width: 75%;
}
-.w-3\/5 {
- width: 60%;
-}
-
.w-32 {
width: 8rem;
}
@@ -1054,10 +1050,6 @@ video {
width: 83.333333%;
}
-.w-54rem {
- width: 54rem;
-}
-
.w-6 {
width: 1.5rem;
}
@@ -1082,10 +1074,6 @@ video {
width: 100%;
}
-.w-48 {
- width: 12rem;
-}
-
.max-w-4xl {
max-width: 56rem;
}
@@ -1153,6 +1141,10 @@ video {
list-style-position: inside;
}
+.list-decimal {
+ list-style-type: decimal;
+}
+
.list-disc {
list-style-type: disc;
}
@@ -1201,12 +1193,24 @@ video {
justify-content: space-around;
}
+.space-x-2 > :not([hidden]) ~ :not([hidden]) {
+ --tw-space-x-reverse: 0;
+ margin-right: calc(0.5rem * var(--tw-space-x-reverse));
+ margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
+}
+
.space-x-4 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(1rem * var(--tw-space-x-reverse));
margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
}
+.space-y-2 > :not([hidden]) ~ :not([hidden]) {
+ --tw-space-y-reverse: 0;
+ margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
+ margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
+}
+
.overflow-auto {
overflow: auto;
}
@@ -1225,6 +1229,14 @@ video {
white-space: nowrap;
}
+.rounded {
+ border-radius: 0.25rem;
+}
+
+.rounded-lg {
+ border-radius: 0.5rem;
+}
+
.border {
border-width: 1px;
}
@@ -1309,6 +1321,11 @@ video {
border-color: rgb(209 213 219 / var(--tw-border-opacity));
}
+.border-gray-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(107 114 128 / var(--tw-border-opacity));
+}
+
.border-red-500 {
--tw-border-opacity: 1;
border-color: rgb(239 68 68 / var(--tw-border-opacity));
@@ -1349,6 +1366,11 @@ video {
border-color: rgb(250 250 249 / var(--tw-border-opacity));
}
+.border-stone-500 {
+ --tw-border-opacity: 1;
+ border-color: rgb(120 113 108 / var(--tw-border-opacity));
+}
+
.border-stone-700 {
--tw-border-opacity: 1;
border-color: rgb(68 64 60 / var(--tw-border-opacity));
@@ -1399,6 +1421,11 @@ video {
border-top-color: rgb(255 255 255 / var(--tw-border-opacity));
}
+.bg-black {
+ --tw-bg-opacity: 1;
+ background-color: rgb(0 0 0 / var(--tw-bg-opacity));
+}
+
.bg-black\/75 {
background-color: rgb(0 0 0 / 0.75);
}
@@ -1453,6 +1480,11 @@ video {
background-color: rgb(13 148 136 / var(--tw-bg-opacity));
}
+.bg-teal-700 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(15 118 110 / var(--tw-bg-opacity));
+}
+
.bg-transparent {
background-color: transparent;
}
@@ -1467,6 +1499,10 @@ video {
background-color: rgb(234 179 8 / var(--tw-bg-opacity));
}
+.bg-opacity-80 {
+ --tw-bg-opacity: 0.8;
+}
+
.bg-opacity-90 {
--tw-bg-opacity: 0.9;
}
@@ -1517,11 +1553,6 @@ video {
padding-right: 3rem;
}
-.px-16 {
- padding-left: 4rem;
- padding-right: 4rem;
-}
-
.px-2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
@@ -1562,16 +1593,6 @@ video {
padding-bottom: 0.375rem;
}
-.py-10 {
- padding-top: 2.5rem;
- padding-bottom: 2.5rem;
-}
-
-.py-16 {
- padding-top: 4rem;
- padding-bottom: 4rem;
-}
-
.py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
@@ -1597,6 +1618,10 @@ video {
padding-bottom: 2rem;
}
+.pb-0 {
+ padding-bottom: 0px;
+}
+
.pb-3 {
padding-bottom: 0.75rem;
}
@@ -1753,6 +1778,10 @@ video {
font-weight: 400;
}
+.font-semibold {
+ font-weight: 600;
+}
+
.uppercase {
text-transform: uppercase;
}
@@ -1929,6 +1958,18 @@ video {
transition-duration: 150ms;
}
+.transition-all {
+ transition-property: all;
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+ transition-duration: 150ms;
+}
+
+.transition-colors {
+ transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+ transition-duration: 150ms;
+}
+
.transition-opacity {
transition-property: opacity;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
@@ -1993,6 +2034,11 @@ video {
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
}
+.hover\:bg-stone-600:hover {
+ --tw-bg-opacity: 1;
+ background-color: rgb(87 83 78 / var(--tw-bg-opacity));
+}
+
.hover\:bg-stone-700:hover {
--tw-bg-opacity: 1;
background-color: rgb(68 64 60 / var(--tw-bg-opacity));
@@ -2028,6 +2074,11 @@ video {
background-color: rgb(202 138 4 / var(--tw-bg-opacity));
}
+.hover\:text-rose-500:hover {
+ --tw-text-opacity: 1;
+ color: rgb(244 63 94 / var(--tw-text-opacity));
+}
+
.hover\:text-stone-100:hover {
--tw-text-opacity: 1;
color: rgb(245 245 244 / var(--tw-text-opacity));
@@ -2048,6 +2099,16 @@ video {
color: rgb(41 37 36 / var(--tw-text-opacity));
}
+.hover\:text-teal-300:hover {
+ --tw-text-opacity: 1;
+ color: rgb(94 234 212 / var(--tw-text-opacity));
+}
+
+.hover\:text-teal-500:hover {
+ --tw-text-opacity: 1;
+ color: rgb(20 184 166 / var(--tw-text-opacity));
+}
+
.hover\:text-white:hover {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
@@ -2314,27 +2375,22 @@ video {
right: 0px;
}
- .lg\:my-8 {
- margin-top: 2rem;
- margin-bottom: 2rem;
+ .lg\:ml-2 {
+ margin-left: 0.5rem;
}
.lg\:mr-2 {
margin-right: 0.5rem;
}
- .lg\:mt-0 {
- margin-top: 0px;
- }
-
- .lg\:ml-2 {
- margin-left: 0.5rem;
- }
-
.lg\:mr-3 {
margin-right: 0.75rem;
}
+ .lg\:mt-0 {
+ margin-top: 0px;
+ }
+
.lg\:block {
display: block;
}
@@ -2359,14 +2415,42 @@ video {
width: 50%;
}
+ .lg\:w-1\/3 {
+ width: 33.333333%;
+ }
+
.lg\:w-1\/4 {
width: 25%;
}
+ .lg\:w-2\/5 {
+ width: 40%;
+ }
+
+ .lg\:w-24 {
+ width: 6rem;
+ }
+
.lg\:w-3\/4 {
width: 75%;
}
+ .lg\:w-3\/5 {
+ width: 60%;
+ }
+
+ .lg\:w-32 {
+ width: 8rem;
+ }
+
+ .lg\:w-48 {
+ width: 12rem;
+ }
+
+ .lg\:w-54rem {
+ width: 54rem;
+ }
+
.lg\:w-80 {
width: 20rem;
}
@@ -2375,34 +2459,6 @@ video {
width: 48%;
}
- .lg\:w-48 {
- width: 12rem;
- }
-
- .lg\:w-2\/5 {
- width: 40%;
- }
-
- .lg\:w-3\/5 {
- width: 60%;
- }
-
- .lg\:w-1\/3 {
- width: 33.333333%;
- }
-
- .lg\:w-24 {
- width: 6rem;
- }
-
- .lg\:w-32 {
- width: 8rem;
- }
-
- .lg\:w-54rem {
- width: 54rem;
- }
-
.lg\:flex-row {
flex-direction: row;
}
@@ -2420,11 +2476,6 @@ video {
padding: 0.75rem;
}
- .lg\:px-4 {
- padding-left: 1rem;
- padding-right: 1rem;
- }
-
.lg\:px-12 {
padding-left: 3rem;
padding-right: 3rem;
@@ -2435,9 +2486,9 @@ video {
padding-right: 4rem;
}
- .lg\:py-16 {
- padding-top: 4rem;
- padding-bottom: 4rem;
+ .lg\:px-4 {
+ padding-left: 1rem;
+ padding-right: 1rem;
}
.lg\:px-6 {
@@ -2445,22 +2496,27 @@ video {
padding-right: 1.5rem;
}
- .lg\:pb-3 {
- padding-bottom: 0.75rem;
- }
-
- .lg\:pt-4 {
- padding-top: 1rem;
+ .lg\:py-16 {
+ padding-top: 4rem;
+ padding-bottom: 4rem;
}
.lg\:pb-0 {
padding-bottom: 0px;
}
+ .lg\:pb-3 {
+ padding-bottom: 0.75rem;
+ }
+
.lg\:pr-3 {
padding-right: 0.75rem;
}
+ .lg\:pt-4 {
+ padding-top: 1rem;
+ }
+
.lg\:text-black {
--tw-text-opacity: 1;
color: rgb(0 0 0 / var(--tw-text-opacity));
diff --git a/system/typemill/author/js/vue-forms.js b/system/typemill/author/js/vue-forms.js
index 7f2c849..6a66d7f 100644
--- a/system/typemill/author/js/vue-forms.js
+++ b/system/typemill/author/js/vue-forms.js
@@ -1,3 +1,12 @@
+app.component('component-paragraph', {
+ props: ['id', 'description', 'maxlength', 'hidden', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'css', 'errors'],
+ template: `
+
{{ $filters.translate(label) }}
+
+
`,
+})
+
+
app.component('component-text', {
props: ['id', 'description', 'maxlength', 'hidden', 'readonly', 'required', 'disabled', 'placeholder', 'label', 'name', 'type', 'value', 'css', 'errors'],
template: `
@@ -15,7 +24,7 @@ app.component('component-text', {
:value="value"
@input="update($event, name)">
{{ errors[name] }}
-
{{ $filters.translate(description) }}
+
`,
methods: {
update: function($event, name)
@@ -40,7 +49,7 @@ app.component('component-textarea', {
:value="value"
@input="update($event, name)">
{{ errors[name] }}
- {{ $filters.translate(description) }}
+
`,
methods: {
update: function($event, name)
@@ -84,7 +93,7 @@ app.component('component-codearea', {
{{ errors[name] }}
- {{ $filters.translate(description) }}
+
`,
mounted: function()
{
@@ -152,7 +161,7 @@ app.component('component-select', {
{{ errors[name] }}
- {{ $filters.translate(description) }}
+
`,
methods: {
update: function($event, name)
@@ -184,7 +193,7 @@ app.component('component-checkbox', {
{{ $filters.translate(checkboxlabel) }}
{{ errors[name] }}
- {{ $filters.translate(description) }}
+
`,
mounted: function()
{
@@ -220,7 +229,7 @@ app.component('component-checkboxlist', {
{{ $filters.translate(option) }}
{{ errors[name] }}
- {{ $filters.translate(description) }}
+
`,
mounted: function()
{
@@ -260,7 +269,7 @@ app.component('component-radio', {
{{ $filters.translate(option) }}
{{ errors[name] }}
- {{ $filters.translate(description) }}
+
`,
methods: {
update: function(picked, name)
@@ -288,7 +297,7 @@ app.component('component-number', {
:value="value"
@input="update($event, name)">
{{ errors[name] }}
- {{ $filters.translate(description) }}
+
`,
methods: {
update: function($event, name)
@@ -320,7 +329,7 @@ app.component('component-date', {
@input="update($event, name)">
{{ errors[name] }}
- {{ $filters.translate(description) }}
+
`,
methods: {
update: function($event, name)
@@ -354,7 +363,7 @@ app.component('component-email', {
@input="update($event, name)">
{{ errors[name] }}
- {{ $filters.translate(description) }}
+
`,
methods: {
update: function($event, name)
@@ -387,7 +396,7 @@ app.component('component-tel', {
@input="update($event, name)">
{{ errors[name] }}
- {{ $filters.translate(description) }}
+
`,
methods: {
update: function($event, name)
@@ -421,7 +430,7 @@ app.component('component-url', {
@input="update($event, name)">
{{ errors[name] }}
- {{ $filters.translate(description) }}
+
`,
methods: {
update: function($event, name)
@@ -454,7 +463,7 @@ app.component('component-color', {
@input="update($event, name)">
{{ errors[name] }}
- {{ $filters.translate(description) }}
+
`,
methods: {
update: function($event, name)
@@ -509,7 +518,7 @@ app.component('component-password', {
{{ errors[name] }}
-
{{ $filters.translate(description) }}
+
@@ -791,7 +800,7 @@ app.component('component-image', {
{{ errors[name] }}
- {{ $filters.translate(description) }}
+
@@ -1026,7 +1035,7 @@ app.component('component-file', {
{{ error }}
- {{ $filters.translate(description) }}
+
diff --git a/system/typemill/author/js/vue-kixote.js b/system/typemill/author/js/vue-kixote.js
index 0debc74..4bc1b02 100644
--- a/system/typemill/author/js/vue-kixote.js
+++ b/system/typemill/author/js/vue-kixote.js
@@ -1,7 +1,5 @@
const getKixoteError = function(error)
{
- console.info(error);
-
if(error.response.data.error != undefined)
{
if(Array.isArray(error.response.data.error))
@@ -17,6 +15,981 @@ const getKixoteError = function(error)
return ['something went wrong'];
}
+/*
++ If you change page and open kixote, then the old version without changes is loaded
++ store kixoteSettings
+*/
+
+const kixote = Vue.createApp({
+ template: `
+
+
+
+
+
+
+
Generating, please be patient ...
+
+
+
`,
+ data() {
+ return {
+ showKixote: false,
+ currentTab: "Admin",
+ tabs: [
+ "Admin",
+ "Generate",
+/* "Automate",
+ "Translate",
+ "SEO",
+ "RAG" */
+ ],
+ icons: {
+ Admin: "M11.366 22.564l1.291-1.807-1.414-1.414-1.807 1.291c-0.335-0.187-0.694-0.337-1.071-0.444l-0.365-2.19h-2l-0.365 2.19c-0.377 0.107-0.736 0.256-1.071 0.444l-1.807-1.291-1.414 1.414 1.291 1.807c-0.187 0.335-0.337 0.694-0.443 1.071l-2.19 0.365v2l2.19 0.365c0.107 0.377 0.256 0.736 0.444 1.071l-1.291 1.807 1.414 1.414 1.807-1.291c0.335 0.187 0.694 0.337 1.071 0.444l0.365 2.19h2l0.365-2.19c0.377-0.107 0.736-0.256 1.071-0.444l1.807 1.291 1.414-1.414-1.291-1.807c0.187-0.335 0.337-0.694 0.444-1.071l2.19-0.365v-2l-2.19-0.365c-0.107-0.377-0.256-0.736-0.444-1.071zM7 27c-1.105 0-2-0.895-2-2s0.895-2 2-2 2 0.895 2 2-0.895 2-2 2zM32 12v-2l-2.106-0.383c-0.039-0.251-0.088-0.499-0.148-0.743l1.799-1.159-0.765-1.848-2.092 0.452c-0.132-0.216-0.273-0.426-0.422-0.629l1.219-1.761-1.414-1.414-1.761 1.219c-0.203-0.149-0.413-0.29-0.629-0.422l0.452-2.092-1.848-0.765-1.159 1.799c-0.244-0.059-0.492-0.109-0.743-0.148l-0.383-2.106h-2l-0.383 2.106c-0.251 0.039-0.499 0.088-0.743 0.148l-1.159-1.799-1.848 0.765 0.452 2.092c-0.216 0.132-0.426 0.273-0.629 0.422l-1.761-1.219-1.414 1.414 1.219 1.761c-0.149 0.203-0.29 0.413-0.422 0.629l-2.092-0.452-0.765 1.848 1.799 1.159c-0.059 0.244-0.109 0.492-0.148 0.743l-2.106 0.383v2l2.106 0.383c0.039 0.251 0.088 0.499 0.148 0.743l-1.799 1.159 0.765 1.848 2.092-0.452c0.132 0.216 0.273 0.426 0.422 0.629l-1.219 1.761 1.414 1.414 1.761-1.219c0.203 0.149 0.413 0.29 0.629 0.422l-0.452 2.092 1.848 0.765 1.159-1.799c0.244 0.059 0.492 0.109 0.743 0.148l0.383 2.106h2l0.383-2.106c0.251-0.039 0.499-0.088 0.743-0.148l1.159 1.799 1.848-0.765-0.452-2.092c0.216-0.132 0.426-0.273 0.629-0.422l1.761 1.219 1.414-1.414-1.219-1.761c0.149-0.203 0.29-0.413 0.422-0.629l2.092 0.452 0.765-1.848-1.799-1.159c0.059-0.244 0.109-0.492 0.148-0.743l2.106-0.383zM21 15.35c-2.402 0-4.35-1.948-4.35-4.35s1.948-4.35 4.35-4.35 4.35 1.948 4.35 4.35c0 2.402-1.948 4.35-4.35 4.35z",
+ Generate: "M8 6l-4-4h-2v2l4 4zM10 0h2v4h-2zM18 10h4v2h-4zM20 4v-2h-2l-4 4 2 2zM0 10h4v2h-4zM10 18h2v4h-2zM2 18v2h2l4-4-2-2zM31.563 27.563l-19.879-19.879c-0.583-0.583-1.538-0.583-2.121 0l-1.879 1.879c-0.583 0.583-0.583 1.538 0 2.121l19.879 19.879c0.583 0.583 1.538 0.583 2.121 0l1.879-1.879c0.583-0.583 0.583-1.538 0-2.121zM15 17l-6-6 2-2 6 6-2 2z",
+ Automate: "M0.001 16.051l-0.001 0c0 0 0 0.003 0.001 0.007 0.003 0.121 0.017 0.24 0.041 0.355 0.006 0.055 0.013 0.114 0.021 0.18 0.007 0.059 0.014 0.122 0.022 0.19 0.012 0.080 0.024 0.165 0.037 0.256 0.027 0.18 0.056 0.379 0.091 0.592 0.042 0.201 0.088 0.419 0.136 0.652 0.022 0.116 0.055 0.235 0.087 0.356s0.065 0.247 0.099 0.375c0.018 0.064 0.032 0.129 0.053 0.194s0.041 0.131 0.062 0.197 0.085 0.268 0.129 0.406c0.011 0.035 0.022 0.069 0.033 0.104 0.013 0.034 0.025 0.069 0.038 0.104 0.026 0.069 0.052 0.139 0.078 0.21 0.053 0.14 0.107 0.284 0.162 0.429 0.061 0.143 0.124 0.288 0.188 0.435 0.032 0.073 0.064 0.147 0.096 0.222s0.071 0.147 0.107 0.221c0.073 0.147 0.146 0.297 0.221 0.448 0.077 0.15 0.163 0.297 0.245 0.448 0.042 0.075 0.084 0.15 0.126 0.226s0.091 0.148 0.136 0.223c0.092 0.148 0.185 0.298 0.279 0.448 0.395 0.59 0.834 1.174 1.319 1.727 0.491 0.549 1.023 1.070 1.584 1.55 0.568 0.473 1.165 0.903 1.773 1.285 0.613 0.376 1.239 0.697 1.856 0.973 0.156 0.064 0.311 0.127 0.465 0.19 0.077 0.030 0.152 0.064 0.229 0.091s0.154 0.054 0.23 0.081 0.302 0.108 0.453 0.156c0.151 0.045 0.3 0.089 0.447 0.133 0.074 0.021 0.146 0.045 0.219 0.063s0.146 0.036 0.218 0.053c0.144 0.035 0.286 0.069 0.425 0.103 0.141 0.027 0.279 0.054 0.415 0.080 0.068 0.013 0.135 0.026 0.201 0.038 0.033 0.006 0.066 0.012 0.099 0.019 0.033 0.005 0.066 0.009 0.099 0.014 0.131 0.018 0.259 0.036 0.384 0.053 0.062 0.009 0.124 0.017 0.185 0.026s0.122 0.012 0.182 0.018c0.119 0.011 0.236 0.021 0.349 0.031s0.222 0.021 0.329 0.023c0.007 0 0.014 0 0.021 0.001 0.019 1.088 0.906 1.964 1.999 1.964 0.017 0 0.034-0.001 0.051-0.001v0.001c0 0 0.003-0 0.007-0.001 0.121-0.003 0.24-0.017 0.355-0.041 0.055-0.006 0.114-0.013 0.18-0.021 0.059-0.007 0.122-0.014 0.19-0.022 0.080-0.012 0.165-0.024 0.256-0.037 0.18-0.027 0.379-0.056 0.592-0.091 0.201-0.042 0.419-0.088 0.652-0.136 0.116-0.022 0.235-0.056 0.356-0.087s0.247-0.065 0.375-0.099c0.064-0.018 0.129-0.032 0.194-0.053s0.13-0.041 0.197-0.062 0.268-0.085 0.406-0.129c0.035-0.011 0.069-0.022 0.104-0.033 0.034-0.013 0.069-0.025 0.104-0.038 0.069-0.026 0.139-0.052 0.21-0.078 0.14-0.053 0.284-0.107 0.429-0.162 0.143-0.061 0.288-0.124 0.436-0.188 0.073-0.032 0.147-0.064 0.222-0.096s0.147-0.071 0.221-0.107c0.147-0.073 0.297-0.146 0.448-0.221 0.15-0.077 0.297-0.163 0.448-0.245 0.075-0.042 0.15-0.084 0.226-0.126s0.148-0.091 0.223-0.136c0.148-0.092 0.298-0.185 0.448-0.279 0.59-0.395 1.174-0.834 1.727-1.319 0.549-0.491 1.070-1.023 1.55-1.584 0.473-0.568 0.903-1.165 1.285-1.773 0.376-0.613 0.697-1.239 0.973-1.855 0.064-0.156 0.127-0.311 0.19-0.465 0.030-0.077 0.064-0.152 0.091-0.229s0.054-0.154 0.081-0.23 0.108-0.302 0.156-0.453c0.045-0.151 0.089-0.3 0.133-0.447 0.021-0.074 0.045-0.146 0.063-0.219s0.036-0.146 0.053-0.218c0.035-0.144 0.069-0.286 0.103-0.425 0.027-0.141 0.054-0.279 0.080-0.415 0.013-0.068 0.026-0.135 0.038-0.201 0.006-0.033 0.012-0.066 0.019-0.099 0.005-0.033 0.009-0.066 0.014-0.099 0.018-0.131 0.036-0.259 0.053-0.384 0.009-0.062 0.017-0.124 0.026-0.185s0.012-0.122 0.018-0.182c0.011-0.119 0.021-0.236 0.031-0.349s0.021-0.222 0.023-0.329c0.001-0.017 0.001-0.033 0.002-0.049 1.101-0.005 1.992-0.898 1.992-2 0-0.017-0.001-0.034-0.001-0.051h0.001c0 0-0-0.003-0.001-0.007-0.003-0.121-0.017-0.24-0.041-0.355-0.006-0.055-0.013-0.114-0.021-0.181-0.007-0.059-0.014-0.122-0.022-0.19-0.012-0.080-0.024-0.165-0.037-0.255-0.027-0.18-0.056-0.379-0.091-0.592-0.042-0.201-0.088-0.419-0.136-0.652-0.022-0.116-0.055-0.235-0.087-0.357s-0.065-0.247-0.099-0.375c-0.018-0.064-0.032-0.129-0.053-0.194s-0.041-0.13-0.062-0.197-0.085-0.268-0.129-0.406c-0.011-0.034-0.022-0.069-0.033-0.104-0.013-0.034-0.025-0.069-0.038-0.104-0.026-0.069-0.052-0.139-0.078-0.21-0.053-0.141-0.107-0.284-0.162-0.429-0.061-0.143-0.124-0.288-0.188-0.435-0.032-0.073-0.064-0.147-0.096-0.222s-0.071-0.147-0.107-0.221c-0.073-0.147-0.146-0.297-0.221-0.448-0.077-0.15-0.163-0.297-0.245-0.448-0.042-0.075-0.084-0.15-0.126-0.226s-0.091-0.148-0.136-0.223c-0.092-0.148-0.185-0.298-0.279-0.448-0.395-0.59-0.834-1.174-1.319-1.727-0.491-0.549-1.023-1.070-1.584-1.55-0.568-0.473-1.165-0.903-1.773-1.285-0.613-0.376-1.239-0.697-1.855-0.973-0.156-0.064-0.311-0.127-0.465-0.19-0.077-0.030-0.152-0.063-0.229-0.091s-0.154-0.054-0.23-0.081-0.302-0.108-0.453-0.156c-0.151-0.045-0.3-0.089-0.447-0.133-0.074-0.021-0.146-0.045-0.219-0.063s-0.146-0.036-0.218-0.053c-0.144-0.035-0.286-0.069-0.425-0.103-0.141-0.027-0.279-0.054-0.415-0.080-0.068-0.013-0.135-0.026-0.201-0.038-0.033-0.006-0.066-0.012-0.099-0.019-0.033-0.005-0.066-0.009-0.099-0.014-0.131-0.018-0.259-0.036-0.384-0.053-0.062-0.009-0.124-0.017-0.185-0.026s-0.122-0.012-0.182-0.018c-0.119-0.010-0.236-0.021-0.349-0.031s-0.222-0.021-0.329-0.023c-0.027-0.001-0.052-0.002-0.078-0.003-0.020-1.087-0.907-1.962-1.999-1.962-0.017 0-0.034 0.001-0.051 0.001l-0-0.001c0 0-0.003 0-0.007 0.001-0.121 0.003-0.24 0.017-0.355 0.041-0.055 0.006-0.114 0.013-0.181 0.021-0.059 0.007-0.122 0.014-0.19 0.022-0.080 0.012-0.165 0.024-0.255 0.037-0.18 0.027-0.379 0.056-0.592 0.091-0.201 0.042-0.419 0.088-0.652 0.136-0.116 0.022-0.235 0.056-0.356 0.087s-0.247 0.065-0.375 0.099c-0.064 0.018-0.129 0.032-0.194 0.053s-0.13 0.041-0.197 0.062-0.268 0.085-0.406 0.129c-0.034 0.011-0.069 0.022-0.104 0.033-0.034 0.013-0.069 0.025-0.104 0.038-0.069 0.026-0.139 0.052-0.21 0.078-0.14 0.053-0.284 0.107-0.429 0.162-0.143 0.061-0.288 0.124-0.435 0.188-0.073 0.032-0.147 0.064-0.222 0.096s-0.147 0.071-0.221 0.107c-0.147 0.073-0.297 0.146-0.448 0.221-0.15 0.077-0.297 0.163-0.448 0.245-0.075 0.042-0.15 0.084-0.226 0.126s-0.148 0.091-0.223 0.136c-0.148 0.092-0.298 0.185-0.448 0.279-0.59 0.395-1.174 0.834-1.727 1.319-0.549 0.491-1.070 1.023-1.55 1.584-0.473 0.568-0.903 1.165-1.285 1.773-0.376 0.613-0.697 1.239-0.973 1.855-0.064 0.156-0.127 0.311-0.19 0.465-0.030 0.077-0.063 0.152-0.091 0.229s-0.054 0.154-0.081 0.23-0.108 0.302-0.156 0.453c-0.045 0.151-0.089 0.3-0.132 0.447-0.021 0.074-0.045 0.146-0.063 0.219s-0.036 0.146-0.053 0.218c-0.035 0.144-0.069 0.286-0.103 0.425-0.027 0.141-0.054 0.279-0.080 0.415-0.013 0.068-0.026 0.135-0.038 0.201-0.006 0.033-0.012 0.066-0.019 0.099-0.005 0.033-0.009 0.066-0.014 0.099-0.018 0.131-0.036 0.259-0.053 0.384-0.009 0.062-0.017 0.124-0.026 0.185s-0.012 0.122-0.018 0.182c-0.010 0.119-0.021 0.236-0.031 0.349s-0.021 0.222-0.023 0.329c-0.001 0.017-0.001 0.034-0.002 0.051-1.074 0.035-1.934 0.916-1.934 1.998 0 0.017 0.001 0.034 0.001 0.051zM2.297 14.022c0.001-0.006 0.003-0.012 0.004-0.018 0.020-0.101 0.051-0.204 0.080-0.311s0.059-0.215 0.090-0.327c0.016-0.056 0.029-0.113 0.048-0.169s0.038-0.113 0.057-0.171 0.077-0.233 0.117-0.353c0.010-0.030 0.020-0.060 0.030-0.090 0.012-0.030 0.023-0.060 0.035-0.090 0.023-0.060 0.047-0.121 0.071-0.182 0.047-0.122 0.096-0.246 0.145-0.373 0.055-0.124 0.111-0.25 0.168-0.377 0.028-0.064 0.057-0.128 0.086-0.192s0.064-0.127 0.095-0.191c0.065-0.128 0.13-0.257 0.197-0.388 0.069-0.129 0.145-0.257 0.219-0.387 0.037-0.065 0.074-0.13 0.112-0.195s0.081-0.128 0.121-0.193c0.082-0.128 0.164-0.257 0.247-0.388 0.351-0.509 0.739-1.012 1.167-1.489 0.434-0.472 0.901-0.919 1.394-1.33 0.499-0.404 1.021-0.77 1.552-1.094 0.535-0.319 1.081-0.589 1.617-0.821 0.136-0.053 0.271-0.106 0.404-0.158 0.067-0.025 0.132-0.053 0.199-0.076s0.134-0.045 0.2-0.067 0.262-0.090 0.392-0.129c0.131-0.037 0.26-0.073 0.387-0.109 0.064-0.017 0.126-0.037 0.189-0.052s0.126-0.029 0.189-0.043c0.124-0.028 0.247-0.056 0.367-0.084 0.121-0.021 0.241-0.043 0.358-0.063 0.058-0.010 0.116-0.021 0.173-0.031 0.029-0.005 0.057-0.010 0.085-0.015 0.029-0.003 0.057-0.007 0.085-0.010 0.113-0.014 0.223-0.028 0.331-0.041 0.054-0.007 0.107-0.013 0.159-0.020s0.105-0.008 0.157-0.013c0.103-0.007 0.203-0.015 0.3-0.022s0.191-0.016 0.283-0.016c0.183-0.004 0.354-0.008 0.512-0.012 0.146 0.005 0.28 0.010 0.401 0.014 0.060 0.002 0.116 0.003 0.17 0.005 0.066 0.004 0.128 0.008 0.186 0.012 0.067 0.004 0.127 0.008 0.182 0.012 0.102 0.016 0.206 0.024 0.312 0.024 0.015 0 0.029-0.001 0.044-0.001 0.004 0 0.007 0 0.007 0v-0.001c0.973-0.024 1.773-0.743 1.924-1.68 0.017 0.004 0.033 0.007 0.050 0.011 0.101 0.020 0.204 0.051 0.311 0.080s0.215 0.059 0.327 0.090c0.056 0.016 0.113 0.029 0.169 0.048s0.113 0.038 0.171 0.057 0.233 0.077 0.353 0.117c0.030 0.010 0.060 0.020 0.090 0.030 0.030 0.012 0.060 0.023 0.090 0.035 0.060 0.023 0.121 0.047 0.182 0.071 0.122 0.047 0.246 0.096 0.373 0.145 0.124 0.055 0.25 0.111 0.378 0.168 0.064 0.028 0.128 0.057 0.192 0.086s0.127 0.064 0.191 0.095c0.128 0.065 0.257 0.13 0.388 0.197 0.13 0.069 0.257 0.145 0.387 0.219 0.065 0.037 0.13 0.074 0.195 0.112s0.128 0.081 0.193 0.121c0.128 0.082 0.257 0.164 0.388 0.247 0.509 0.351 1.012 0.739 1.489 1.167 0.472 0.434 0.919 0.901 1.33 1.394 0.404 0.499 0.77 1.021 1.094 1.552 0.319 0.535 0.589 1.081 0.821 1.617 0.053 0.136 0.106 0.271 0.158 0.404 0.025 0.067 0.053 0.132 0.076 0.199s0.045 0.134 0.067 0.2 0.090 0.262 0.129 0.392c0.037 0.131 0.073 0.26 0.109 0.387 0.017 0.064 0.037 0.126 0.052 0.189s0.029 0.126 0.043 0.189c0.028 0.124 0.056 0.247 0.084 0.367 0.021 0.121 0.043 0.241 0.063 0.358 0.010 0.058 0.020 0.116 0.031 0.173 0.005 0.029 0.010 0.057 0.015 0.085 0.003 0.029 0.007 0.057 0.010 0.085 0.014 0.113 0.028 0.223 0.041 0.331 0.007 0.054 0.014 0.107 0.020 0.159s0.008 0.105 0.013 0.157c0.007 0.103 0.015 0.203 0.022 0.3s0.016 0.191 0.016 0.283c0.004 0.183 0.008 0.354 0.012 0.512-0.005 0.146-0.010 0.28-0.014 0.401-0.002 0.060-0.003 0.116-0.005 0.17-0.004 0.066-0.008 0.128-0.012 0.186-0.004 0.067-0.008 0.127-0.012 0.182-0.016 0.102-0.024 0.206-0.024 0.312 0 0.015 0.001 0.029 0.001 0.044-0 0.004-0 0.007-0 0.007h0.001c0.024 0.961 0.726 1.754 1.646 1.918-0.002 0.009-0.004 0.018-0.006 0.028-0.020 0.102-0.051 0.204-0.080 0.311s-0.059 0.215-0.090 0.327c-0.016 0.056-0.029 0.113-0.048 0.169s-0.038 0.113-0.057 0.171-0.077 0.233-0.117 0.353c-0.010 0.030-0.020 0.060-0.030 0.090-0.012 0.030-0.023 0.060-0.035 0.090-0.023 0.060-0.047 0.121-0.071 0.182-0.047 0.122-0.096 0.246-0.145 0.373-0.055 0.124-0.111 0.25-0.169 0.378-0.028 0.064-0.057 0.128-0.086 0.192s-0.064 0.127-0.095 0.191c-0.065 0.128-0.13 0.257-0.197 0.388-0.069 0.129-0.145 0.257-0.219 0.387-0.037 0.065-0.074 0.13-0.112 0.195s-0.081 0.128-0.121 0.193c-0.082 0.128-0.164 0.257-0.247 0.388-0.351 0.509-0.738 1.012-1.167 1.489-0.434 0.472-0.901 0.919-1.394 1.33-0.499 0.404-1.021 0.77-1.552 1.094-0.535 0.319-1.081 0.589-1.617 0.821-0.136 0.053-0.271 0.106-0.404 0.158-0.067 0.025-0.132 0.053-0.199 0.076s-0.134 0.045-0.2 0.067-0.262 0.090-0.392 0.129c-0.131 0.037-0.26 0.073-0.387 0.109-0.064 0.017-0.126 0.037-0.189 0.052s-0.126 0.029-0.189 0.043c-0.124 0.028-0.247 0.056-0.367 0.084-0.122 0.021-0.241 0.043-0.358 0.063-0.058 0.010-0.116 0.021-0.173 0.031-0.029 0.005-0.057 0.010-0.085 0.015-0.029 0.003-0.057 0.007-0.085 0.010-0.113 0.014-0.223 0.028-0.331 0.041-0.054 0.007-0.107 0.014-0.159 0.020s-0.105 0.008-0.157 0.013c-0.103 0.007-0.203 0.015-0.3 0.022s-0.191 0.016-0.283 0.016c-0.183 0.004-0.354 0.008-0.512 0.012-0.146-0.005-0.28-0.010-0.401-0.014-0.060-0.002-0.116-0.003-0.17-0.005-0.066-0.004-0.128-0.008-0.186-0.012-0.067-0.004-0.127-0.008-0.182-0.012-0.102-0.016-0.206-0.024-0.312-0.024-0.015 0-0.029 0.001-0.044 0.001-0.004-0-0.007-0-0.007-0v0.001c-0.969 0.024-1.766 0.737-1.921 1.668-0.1-0.020-0.201-0.050-0.306-0.079-0.106-0.029-0.215-0.059-0.327-0.090-0.056-0.016-0.113-0.029-0.169-0.048s-0.113-0.038-0.171-0.057-0.233-0.077-0.353-0.117c-0.030-0.010-0.060-0.020-0.090-0.030-0.030-0.012-0.060-0.023-0.090-0.035-0.060-0.023-0.121-0.047-0.182-0.071-0.122-0.048-0.246-0.096-0.373-0.145-0.124-0.055-0.25-0.111-0.377-0.168-0.064-0.028-0.128-0.057-0.192-0.086s-0.127-0.064-0.191-0.095c-0.128-0.065-0.257-0.13-0.388-0.197-0.13-0.069-0.257-0.145-0.387-0.219-0.065-0.037-0.13-0.074-0.195-0.112s-0.128-0.081-0.193-0.121c-0.128-0.082-0.257-0.164-0.388-0.247-0.509-0.351-1.012-0.738-1.489-1.166-0.472-0.434-0.919-0.901-1.33-1.394-0.404-0.499-0.77-1.021-1.094-1.552-0.319-0.535-0.589-1.081-0.821-1.617-0.053-0.136-0.106-0.271-0.158-0.404-0.025-0.067-0.053-0.132-0.076-0.199s-0.045-0.134-0.067-0.2-0.090-0.262-0.129-0.392c-0.037-0.131-0.073-0.26-0.109-0.387-0.017-0.064-0.037-0.126-0.052-0.189s-0.029-0.126-0.043-0.189c-0.028-0.124-0.056-0.247-0.084-0.367-0.021-0.121-0.043-0.241-0.063-0.358-0.010-0.058-0.021-0.116-0.031-0.173-0.005-0.029-0.010-0.057-0.015-0.085-0.003-0.029-0.007-0.057-0.010-0.085-0.014-0.113-0.028-0.223-0.041-0.331-0.007-0.054-0.013-0.107-0.020-0.159s-0.008-0.105-0.013-0.157c-0.007-0.103-0.015-0.203-0.022-0.3s-0.016-0.191-0.016-0.283c-0.004-0.183-0.008-0.354-0.012-0.512 0.005-0.146 0.010-0.28 0.014-0.401 0.002-0.060 0.003-0.116 0.005-0.17 0.004-0.066 0.008-0.128 0.012-0.186 0.004-0.067 0.008-0.127 0.012-0.182 0.015-0.102 0.024-0.206 0.024-0.312 0-0.015-0.001-0.029-0.001-0.044 0-0.004 0.001-0.007 0.001-0.007h-0.001c-0.024-0.981-0.754-1.786-1.701-1.927z",
+ Translate: "M16 0c-8.837 0-16 7.163-16 16s7.163 16 16 16 16-7.163 16-16-7.163-16-16-16zM16 30c-1.967 0-3.84-0.407-5.538-1.139l7.286-8.197c0.163-0.183 0.253-0.419 0.253-0.664v-3c0-0.552-0.448-1-1-1-3.531 0-7.256-3.671-7.293-3.707-0.188-0.188-0.442-0.293-0.707-0.293h-4c-0.552 0-1 0.448-1 1v6c0 0.379 0.214 0.725 0.553 0.894l3.447 1.724v5.871c-3.627-2.53-6-6.732-6-11.489 0-2.147 0.484-4.181 1.348-6h3.652c0.265 0 0.52-0.105 0.707-0.293l4-4c0.188-0.188 0.293-0.442 0.293-0.707v-2.419c1.268-0.377 2.61-0.581 4-0.581 2.2 0 4.281 0.508 6.134 1.412-0.13 0.109-0.256 0.224-0.376 0.345-1.133 1.133-1.757 2.64-1.757 4.243s0.624 3.109 1.757 4.243c1.139 1.139 2.663 1.758 4.239 1.758 0.099 0 0.198-0.002 0.297-0.007 0.432 1.619 1.211 5.833-0.263 11.635-0.014 0.055-0.022 0.109-0.026 0.163-2.541 2.596-6.084 4.208-10.004 4.208z",
+ SEO: "M16 2c8.837 0 16 7.163 16 16 0 6.025-3.331 11.271-8.25 14h-15.499c-4.92-2.729-8.25-7.975-8.25-14 0-8.837 7.163-16 16-16zM25.060 27.060c2.42-2.42 3.753-5.637 3.753-9.060h-2.813v-2h2.657c-0.219-1.406-0.668-2.755-1.33-4h-3.327v-2h2.009c-0.295-0.368-0.611-0.722-0.949-1.060-1.444-1.444-3.173-2.501-5.060-3.119v2.178h-2v-2.658c-0.656-0.102-1.324-0.155-2-0.155s-1.344 0.053-2 0.155v2.658h-2v-2.178c-1.887 0.617-3.615 1.674-5.060 3.119-0.338 0.338-0.654 0.692-0.949 1.060h2.009v2h-3.327c-0.662 1.245-1.111 2.594-1.33 4h2.657v2h-2.813c0 3.422 1.333 6.64 3.753 9.060 0.335 0.335 0.685 0.648 1.049 0.94h6.011l1.143-16h1.714l1.143 16h6.011c0.364-0.292 0.714-0.606 1.049-0.94z",
+ RAG: "M32 10l-16-8-16 8 16 8 16-8zM16 4.655l10.689 5.345-10.689 5.345-10.689-5.345 10.689-5.345zM28.795 14.398l3.205 1.602-16 8-16-8 3.205-1.602 12.795 6.398zM28.795 20.398l3.205 1.602-16 8-16-8 3.205-1.602 12.795 6.398z",
+ },
+ aiservice: false,
+ tokenstats: 0,
+ useragreement: false,
+ loading: false,
+ item: data.item,
+ content: data.content,
+ urlinfo: data.urlinfo,
+ labels: data.labels,
+ settings: data.settings,
+ kixoteSettings: {},
+ settingsSaved: false,
+ command: '',
+ }
+ },
+ mounted() {
+
+ eventBus.$on('kiExit', this.stopKixote);
+
+ eventBus.$on('kiScrollBottom', this.scrollToBottom);
+
+ eventBus.$on('switchLoading', this.switchLoading);
+
+ eventBus.$on('updateKixoteSettings', this.updateKixoteSettings);
+
+ eventBus.$on('storeKixoteSettings', this.storeKixoteSettings);
+
+ eventBus.$on('agreetoservice', this.switchAgreement);
+ },
+ watch: {
+ showKixote(newValue) {
+ if (newValue) {
+ this.loadKixoteSettings();
+ this.loadContent();
+ this.loadTokenStats();
+ }
+ }
+ },
+ computed: {
+ currentTabComponent: function ()
+ {
+ return 'tab-' + this.currentTab.toLowerCase()
+ }
+ },
+ methods: {
+ loadKixoteSettings()
+ {
+ self = this;
+
+ tmaxios.get('/api/v1/kixotesettings',{
+ params: {
+ 'url': data.urlinfo.route
+ }
+ })
+ .then(function (response)
+ {
+ if (response.data.settings)
+ {
+ self.kixoteSettings = response.data.settings;
+ }
+ })
+ .catch(function (error)
+ {
+ if(error.response)
+ {
+ }
+ });
+ },
+ loadContent()
+ {
+ self = this;
+
+ tmaxios.get('/api/v1/article/content',{
+ params: {
+ 'url': data.urlinfo.route,
+ 'draft': true
+ }
+ })
+ .then(function (response)
+ {
+ if (response.data.content)
+ {
+ self.content = response.data.content;
+ }
+ })
+ .catch(function (error)
+ {
+ if(error.response)
+ {
+ }
+ });
+ },
+ loadTokenStats()
+ {
+ self = this;
+
+ tmaxios.get('/api/v1/tokenstats',{
+ params: {
+ 'url': data.urlinfo.route,
+ }
+ })
+ .then(function (response)
+ {
+ if (response.data)
+ {
+ self.aiservice = response.data.aiservice;
+ self.tokenstats = response.data.tokenstats;
+ self.useragreement = response.data.useragreement;
+ }
+ })
+ .catch(function (error)
+ {
+ if(error.response)
+ {
+ console.info(response);
+ }
+ });
+ },
+ startKixote()
+ {
+ this.showKixote = true;
+ },
+ stopKixote()
+ {
+ this.showKixote = false;
+ this.currentTab = 'Admin';
+ this.content = '';
+ },
+ switchLoading()
+ {
+ this.loading = !this.loading;
+ },
+ switchAgreement()
+ {
+ this.useragreement = true;
+ },
+ selectTab(tab)
+ {
+ alert("Select Tab")
+ },
+ scrollToBottom()
+ {
+ this.$nextTick(() => {
+ const displayRef = this.$refs.kdisplay;
+ displayRef.scrollTop = displayRef.scrollHeight;
+ });
+ },
+ updateKixoteSettings(newSettings)
+ {
+ this.settingsSaved = false;
+ this.kixoteSettings = { ...this.kixoteSettings, ...newSettings }; // ✅ Merge settings
+ },
+ storeKixoteSettings()
+ {
+ self = this;
+
+ tmaxios.put('/api/v1/kixotesettings',{
+ 'url': data.urlinfo.route,
+ 'kixotesettings': this.kixoteSettings
+ })
+ .then(function (response)
+ {
+ self.settingsSaved = true;
+ self.kixoteSettings = response.data.kixotesettings;
+ })
+ .catch(function (error)
+ {
+ if(error.response)
+ {
+ self.kixoteSettings = error.response.data.kixotesettings;
+ }
+ });
+ }
+ },
+})
+
+kixote.component('tab-generate', {
+ props: ['content', '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: '',
+ showFocusButton: false,
+ buttonPosition: { top: 0, left: 0 },
+ selection: { start: 0, end: 0, text: '' },
+ editPrompt: '',
+ addNewPrompt: false,
+ newPrompt: {
+ title: '',
+ content: '',
+ active: true,
+ system: false
+ },
+ titleError: false,
+ bodyError: false,
+ currentFilter: 'all',
+ article: '',
+ index: '',
+ };
+ },
+ template: `
+
+
+
+
Your AI Assistant for Typemill
+
+ To get started with AI-powered assistance, go to System Settings, open the AI tab, and follow these steps:
+
+
+ - Select an AI service.
+ - Choose a model.
+ - Enter your API key.
+
+
Once set up, you can start using AI assistance right away!
+
+
+
+
+
+
+
+
+
+
Activate {{aiservice}}
+
+
+
+
+
+
+
+
+
+ -
+ {{ action.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Ki>
+
+
+ |
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+ Filter:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Mimic your tone tab content here.
+
+
+
+
+
+
+
Content Generation only works on content pages. You are currently in the settings area.
+
+
+ `,
+ mounted: function()
+ {
+ this.initAutosize();
+
+ if(this.versions.length == 0)
+ {
+ this.initializeContent()
+ }
+ },
+ computed: {
+ promptlistactive()
+ {
+ return Object.values(this.kixoteSettings?.promptlist || {}).filter(prompt => prompt.active);
+ },
+ promptlistsystem()
+ {
+ return Object.fromEntries(
+ Object.entries(this.kixoteSettings?.promptlist || {}).filter(([key, prompt]) => prompt.system)
+ );
+ },
+ promptlistuser()
+ {
+ return Object.fromEntries(
+ 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;
+ }
+ }
+
+ },
+ methods: {
+ initAutosize()
+ {
+ let kieditor = this.$refs["kieditor"];
+ let prompteditor = this.$refs["prompteditor"];
+
+ if (kieditor)
+ {
+ autosize(kieditor);
+ }
+ if (prompteditor)
+ {
+ autosize(prompteditor);
+ }
+ },
+ agreeTo(aiservice)
+ {
+ var self = this;
+
+ tmaxios.post('/api/v1/agreetoaiservice',{
+ 'aiservice': aiservice
+ })
+ .then(function (response)
+ {
+ eventBus.$emit('agreetoservice');
+ self.$nextTick(() => {
+ self.resizeAiEditor();
+ });
+ })
+ },
+ setCurrentTab(tabValue)
+ {
+ this.currentTab = tabValue;
+
+ if(tabValue == 'article')
+ {
+ this.resizeAiEditor();
+ }
+ },
+ initializeContent()
+ {
+ let markdown = '';
+
+ for(block in this.content)
+ {
+ markdown += this.content[block].markdown + '\n\n';
+ }
+ this.originalmd = markdown;
+ this.versions.push(markdown);
+ this.resizeAiEditor();
+ },
+ resizeAiEditor()
+ {
+ this.$nextTick(() => {
+ let kieditor = this.$refs["kieditor"];
+ if (kieditor)
+ {
+ autosize.update(kieditor);
+ }
+ });
+ },
+ resizePromptEditor()
+ {
+ this.$nextTick(() => {
+ let prompteditor = this.$refs["prompteditor"];
+ if (prompteditor)
+ {
+ autosize.update(prompteditor);
+ }
+ });
+ },
+ usePrompt(index)
+ {
+ this.prompt = this.promptlistactive[index].content;
+ this.resizePromptEditor();
+ },
+ switchVersion(index)
+ {
+ this.activeversion = index;
+ this.resizeAiEditor();
+ },
+ submitPrompt()
+ {
+ var self = this;
+ eventBus.$emit('switchLoading');
+
+ tmaxios.post('/api/v1/chatgpt',{
+ 'prompt': this.prompt,
+ 'article': this.versions[this.activeversion]
+ })
+ .then(function (response)
+ {
+ eventBus.$emit('switchLoading');
+ if (response.data.message === 'Success')
+ {
+ let answer = response.data.data.choices[0].message.content;
+ answer = answer.replace(/<\/?focus>/g, '');
+ self.versions.push(answer);
+ self.activeversion = self.versions.length-1;
+ self.prompt = '';
+ self.resizePromptEditor();
+ self.resizeAiEditor();
+ }
+ })
+ .catch(function (error)
+ {
+ eventBus.$emit('switchLoading');
+ if(error.response)
+ {
+ self.disabled = false;
+ self.message = handleErrorMessage(error);
+ self.messageClass = 'bg-rose-500';
+ self.licensemessage = error.response.data.message;
+ if(error.response.data.errors !== undefined)
+ {
+ self.errors = error.response.data.errors;
+ }
+ }
+ });
+ },
+ handleKeydown(event)
+ {
+ if (event.key === 'Enter' && !event.shiftKey)
+ {
+ event.preventDefault();
+ this.submitPrompt();
+ }
+ else if (event.key === 'Enter' && event.shiftKey)
+ {
+ // Allow line break
+ const textarea = event.target;
+ const start = textarea.selectionStart;
+ const end = textarea.selectionEnd;
+ textarea.value = textarea.value.slice(0, start) + '\n' + textarea.value.slice(end);
+ textarea.selectionStart = textarea.selectionEnd = start + 1;
+
+ let prompteditor = this.$refs["prompteditor"];
+ autosize.update(prompteditor);
+
+ event.preventDefault();
+ }
+ },
+ detectSelection(event)
+ {
+ const textarea = this.$refs.kieditor;
+ const start = textarea.selectionStart;
+ const end = textarea.selectionEnd;
+ const text = textarea.value.substring(start, end);
+
+ if (text.length > 0)
+ {
+ this.selection = { start, end, text };
+ this.showFocusButton = true;
+ this.buttonPosition = {
+ top: event.offsetY-20,
+ left: event.offsetX,
+ };
+ }
+ else
+ {
+ this.showFocusButton = false;
+ }
+ },
+ wrapInFocus()
+ {
+ const textarea = this.$refs.kieditor;
+ const { start, end, text } = this.selection;
+
+ if (!text) return;
+
+ const newText =
+ textarea.value.substring(0, start) +
+ `
${text}` +
+ textarea.value.substring(end);
+
+ this.versions[this.activeversion] = newText;
+ this.showFocusButton = false;
+ },
+ storeArticle(append = false)
+ {
+ var self = this;
+
+ var content = this.versions[this.activeversion];
+ var title = 'Title missing';
+
+ var regex = /^#(?!#)([^#\n]+)/m;
+ var match = content.match(regex);
+ if (match)
+ {
+ var title = '# ' + match[1];
+ var content = content.replace(regex, '');
+ }
+
+ tmaxios.put('/api/v1/draft',{
+ 'url': data.urlinfo.route,
+ 'item_id': this.item.keyPath,
+ 'title': title.trim(),
+ 'body': content.trim()
+ })
+ .then(function (response)
+ {
+ location.reload();
+ })
+ .catch(function (error)
+ {
+ if(error.response)
+ {
+ let message = handleErrorMessage(error);
+ if(message)
+ {
+ eventBus.$emit('publishermessage', message);
+ }
+ }
+ });
+ },
+ slugify(text)
+ {
+ return text
+ .toLowerCase() // Convert to lowercase
+ .replace(/[^a-z0-9]/g, '-') // Replace non-alphanumeric characters with '-'
+ .replace(/-+/g, '-') // Remove multiple dashes
+ .trim(); // Trim leading/trailing dashes
+ },
+ updateSettings(newSettings)
+ {
+ eventBus.$emit('updateKixoteSettings', newSettings); // Emit event
+ },
+ validateTitle(title)
+ {
+ const titleRegex = /^[a-zA-Z0-9 ]{0,20}$/;
+ if (!titleRegex.test(title))
+ {
+ this.titleError = "Title can only contain letters, numbers, and spaces (max 20 chars).";
+ }
+ else
+ {
+ this.titleError = false;
+ }
+ },
+ validateBody(body)
+ {
+ const bodyRegex = /<\/?[^>]+(>|$)/g;
+ if (bodyRegex.test(body))
+ {
+ this.bodyError = "HTML and script tags are not allowed.";
+ }
+ else
+ {
+ this.bodyError = false;
+ }
+ },
+ savePrompts()
+ {
+ if (!this.titleError && !this.bodyError)
+ {
+ eventBus.$emit('storeKixoteSettings');
+ }
+ },
+ updatePrompt(kixoteSettings, promptname)
+ {
+ if(kixoteSettings.promptlist[promptname] != undefined)
+ {
+ kixoteSettings.promptlist[promptname].errors = {};
+
+ this.validateTitle(kixoteSettings.promptlist[promptname].title);
+ kixoteSettings.promptlist[promptname].errors.title = this.titleError;
+
+ this.validateBody(kixoteSettings.promptlist[promptname].content);
+ kixoteSettings.promptlist[promptname].errors.body = this.bodyError;
+
+ this.updateSettings(kixoteSettings);
+ }
+ },
+ deletePrompt(name)
+ {
+ var promptlist = this.kixoteSettings.promptlist;
+
+ delete promptlist[name];
+
+ this.updateSettings(promptlist);
+
+ eventBus.$emit('storeKixoteSettings');
+
+ },
+ saveNewPrompt()
+ {
+ if (this.titleError || this.bodyError)
+ {
+ return false;
+ }
+
+ var promptlist = this.kixoteSettings.promptlist;
+ var promptkey = this.slugify(this.newPrompt.title);
+ promptlist[promptkey] = {
+ title: this.newPrompt.title,
+ content: this.newPrompt.content,
+ active: this.newPrompt.active,
+ system: this.newPrompt.system
+ };
+
+ this.newPrompt = {
+ title: '',
+ content: '',
+ active: true,
+ system: false
+ };
+
+ this.addNewPrompt = false;
+ this.updateSettings(promptlist);
+ eventBus.$emit('storeKixoteSettings');
+ },
+ exit()
+ {
+ eventBus.$emit('kiExit');
+ },
+ }
+})
// publish tree
// unpublish tree
@@ -172,52 +1145,52 @@ const kixoteCommands = [
},
answer: ['Save content...'],
},
-*/ ];
+*/
+ ];
-const kixote = Vue.createApp({
- template: `
-
-
-
-
Hello, I am Kixote from Typemill. How can I help?
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Enter "help" to see a list of commands
-
-
-
-
`,
- data() {
+
+
+kixote.component('tab-admin', {
+ props: [],
+ data: function () {
return {
- showKixote: false,
messenger: [],
messengerIndex: false,
command: '',
params: false,
}
},
- mounted() {
-
- this.clear();
+ template: `
+
+
Hello, I am Kixote from Typemill. How can I help?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Enter "help" to see a list of commands
+
+
+ `,
+ mounted: function()
+ {
+// this.clear();
eventBus.$on('answer', messages => {
let lastKey = this.messenger.length - 1;
@@ -239,47 +1212,32 @@ const kixote = Vue.createApp({
let lastKey = this.messenger.length - 1;
this.messenger[lastKey].storable = data;
});
+
+ this.focusOnInput();
},
methods: {
+ exit()
+ {
+ eventBus.$emit('kiExit');
+ },
clear()
{
this.messenger = [];
this.params = false;
this.command = '';
},
- startKixote()
- {
- this.clear();
- this.showKixote = true;
- this.focusOnInput();
- },
- stopKixote()
- {
- this.clear();
- this.showKixote = false;
- },
focusOnInput()
{
this.$nextTick(() => {
- if(this.showKixote)
- {
- const inputRef = this.$refs.kinput;
- inputRef.focus();
- }
+ const inputRef = this.$refs.kinput;
+ inputRef.focus();
});
- },
- scrollToBottom()
- {
- this.$nextTick(() => {
- const displayRef = this.$refs.kdisplay;
- displayRef.scrollTop = displayRef.scrollHeight;
- });
- },
+ },
finishCommand()
{
this.command = '';
this.focusOnInput();
- this.scrollToBottom();
+ eventBus.$emit('kiScrollBottom');
},
submitInlineCommand(command, index)
{
@@ -302,7 +1260,7 @@ const kixote = Vue.createApp({
if(this.command == 'exit')
{
- this.stopKixote();
+ this.exit();
return;
}
@@ -351,7 +1309,7 @@ const kixote = Vue.createApp({
if(!commandObject)
{
- message.answer.push('Command not found. Type "help" to see a list of available commands. With a KI plugin, you can also use prompts.');
+ message.answer.push('Command not found. Type "help" to see a list of available commands.');
this.messenger.push(message);
@@ -459,14 +1417,6 @@ const kixote = Vue.createApp({
{
if(!params[index].value)
{
- // TODO: validate the param
- console.info("validate params");
-
- console.info("return error message");
-
- console.info("repeat current question (set next to index).");
-
- console.info("if valid, set value and set next to index +1");
// set param if valid
this.params[index].value = this.command;
@@ -480,6 +1430,102 @@ const kixote = Vue.createApp({
}
return false;
- },
+ }
+ }
+})
+
+
+kixote.component('tab-translate', {
+ props: [],
+ data: function () {
+ return {
+ }
},
+ template: `
+ Translation Component
+ `,
+ mounted: function()
+ {
+ },
+ methods: {
+ selectComponent: function(type)
+ {
+ }
+ }
+})
+
+kixote.component('tab-automate', {
+ props: [],
+ data: function () {
+ return {
+ }
+ },
+ template: `
`,
+ mounted: function()
+ {
+ },
+ methods: {
+ selectComponent: function(type)
+ {
+ }
+ }
+})
+
+kixote.component('tab-seo', {
+ props: [],
+ data: function () {
+ return {
+ }
+ },
+ template: `
`,
+ mounted: function()
+ {
+ },
+ methods: {
+ selectComponent: function(type)
+ {
+ }
+ }
+})
+
+kixote.component('tab-rag', {
+ props: [],
+ data: function () {
+ return {
+ }
+ },
+ template: `
+ Retrieval Augmented Generation
+ `,
+ mounted: function()
+ {
+ },
+ methods: {
+ selectComponent: function(type)
+ {
+ }
+ }
+})
+
+kixote.component('tab-token', {
+ props: [],
+ data: function () {
+ return {
+ }
+ },
+ template: `
+ Token Overview Component
+ `,
+ mounted: function()
+ {
+ },
+ methods: {
+ selectComponent: function(type)
+ {
+ }
+ }
})
\ No newline at end of file
diff --git a/system/typemill/author/js/vue-shared.js b/system/typemill/author/js/vue-shared.js
index 48fed1c..ac2c501 100644
--- a/system/typemill/author/js/vue-shared.js
+++ b/system/typemill/author/js/vue-shared.js
@@ -51,15 +51,18 @@ const translatefilter = {
.replace(/^_+|_+$/g, "") // Trim underscores from the start and end of the string
.toUpperCase(); // Convert to uppercase
+
let translation_value = data.labels[translation_key];
+
if(!translation_value || translation_value.length === 0)
{
- return value
- }
- else
- {
- return data.labels[translation_key]
+ translation_value = value;
}
+
+ /* process markdown links */
+ translation_value = translation_value.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '
$1');
+
+ return translation_value;
}
}
diff --git a/system/typemill/author/js/vue-user.js b/system/typemill/author/js/vue-user.js
index fa160b5..eabcffd 100644
--- a/system/typemill/author/js/vue-user.js
+++ b/system/typemill/author/js/vue-user.js
@@ -2,6 +2,7 @@ const app = Vue.createApp({
template: `