diff --git a/config/runtime.exs b/config/runtime.exs index 356de9265..fab7cdead 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -105,8 +105,13 @@ config :accent, Accent.MachineTranslations, } config :accent, Accent.Prompts, + use_provider_by_default: get_env("AI_ASSISTANT_USE_DEFAULT_PROVIDER", :string), default_providers_config: %{ - "openai" => %{"key" => get_env("OPENAI_API_KEY")} + "openai" => %{ + "key" => get_env("OPENAI_API_KEY"), + "model" => get_env("OPENAI_API_MODEL") || "gpt-4o", + "base_url" => get_env("OPENAI_API_BASE_URL") || "https://api.openai.com/v1/" + } } config :accent, Accent.WebappView, diff --git a/lib/accent/auth/role_abilities.ex b/lib/accent/auth/role_abilities.ex index 8a77de76a..5e953520f 100644 --- a/lib/accent/auth/role_abilities.ex +++ b/lib/accent/auth/role_abilities.ex @@ -135,7 +135,8 @@ defmodule Accent.RoleAbilities do end def can?(_role, :use_prompt_improve_text, project) do - Accent.Prompts.enabled?(project.prompt_config) + config = Accent.Prompts.config_or_default(project.prompt_config) + Accent.Prompts.enabled?(config) end # Define abilities function at compile time to remove list lookup at runtime diff --git a/lib/graphql/resolvers/prompt.ex b/lib/graphql/resolvers/prompt.ex index 84e64d47f..263f828be 100644 --- a/lib/graphql/resolvers/prompt.ex +++ b/lib/graphql/resolvers/prompt.ex @@ -10,17 +10,18 @@ defmodule Accent.GraphQL.Resolvers.Prompt do @spec improve_text(Accent.Prompt.t(), any(), GraphQLContext.t()) :: {:ok, %{provider: atom(), text: String.t(), error: String.t() | nil}} def improve_text(prompt, args, _info) do + config = Prompts.config_or_default(prompt.project.prompt_config) + result = %{ text: nil, - error: nil, - provider: Prompts.id_from_config(prompt.project.prompt_config) + errors: nil, + provider: Prompts.id_from_config(config) } result = - case Prompts.completions(prompt, args.text, prompt.project.prompt_config) do + case Prompts.completions(prompt, args.text, config) do [%{text: text} | _] -> %{result | text: text} - {:error, error} when is_atom(error) -> %{result | error: to_string(error)} - _ -> result + _ -> %{result | text: "", errors: ["internal_server_error"]} end {:ok, result} @@ -84,4 +85,21 @@ defmodule Accent.GraphQL.Resolvers.Prompt do {:ok, %{prompt: nil, errors: ["unprocessable_entity"]}} end end + + @spec project_config(Project.t(), any(), GraphQLContext.t()) :: + {:ok, %{provider: String.t(), use_platform: boolean(), use_config_key: boolean()} | nil} + def project_config(project, _, _) do + config = Prompts.config_or_default(project.prompt_config) + + if is_nil(config) do + {:ok, nil} + else + {:ok, + %{ + provider: config["provider"], + use_platform: config["use_platform"] || false, + use_config_key: not is_nil(config["config"]["key"]) + }} + end + end end diff --git a/lib/graphql/types/project.ex b/lib/graphql/types/project.ex index 97de64e9c..ab4c43801 100644 --- a/lib/graphql/types/project.ex +++ b/lib/graphql/types/project.ex @@ -9,8 +9,9 @@ defmodule Accent.GraphQL.Types.Project do alias Accent.GraphQL.Resolvers.Activity alias Accent.GraphQL.Resolvers.Document alias Accent.GraphQL.Resolvers.Project + alias Accent.GraphQL.Resolvers.Prompt alias Accent.GraphQL.Resolvers.Revision - alias Accent.GraphQL.Resolvers.Translation, as: TranslationResolver + alias Accent.GraphQL.Resolvers.Translation object :projects do field(:meta, non_null(:pagination_meta)) @@ -65,20 +66,7 @@ defmodule Accent.GraphQL.Types.Project do end ) - field(:prompt_config, :prompt_config, - resolve: fn project, _, _ -> - if project.prompt_config do - {:ok, - %{ - provider: project.prompt_config["provider"], - use_platform: project.prompt_config["use_platform"] || false, - use_config_key: not is_nil(project.prompt_config["config"]["key"]) - }} - else - {:ok, nil} - end - end - ) + field(:prompt_config, :prompt_config, resolve: &Prompt.project_config/3) field :last_activity, :activity do arg(:action, :string) @@ -165,7 +153,7 @@ defmodule Accent.GraphQL.Types.Project do resolve( project_authorize( :index_translations, - &TranslationResolver.list_grouped_project/3 + &Translation.list_grouped_project/3 ) ) end @@ -184,7 +172,7 @@ defmodule Accent.GraphQL.Types.Project do arg(:is_added_last_sync, :boolean) arg(:is_commented_on, :boolean) - resolve(project_authorize(:index_translations, &TranslationResolver.list_project/3)) + resolve(project_authorize(:index_translations, &Translation.list_project/3)) end field :activities, :activities do @@ -213,7 +201,7 @@ defmodule Accent.GraphQL.Types.Project do field :translation, :translation do arg(:id, non_null(:id)) - resolve(project_authorize(:show_translation, &TranslationResolver.show_project/3)) + resolve(project_authorize(:show_translation, &Translation.show_project/3)) end field :activity, :activity do diff --git a/lib/prompts/prompts.ex b/lib/prompts/prompts.ex index 6bf1fa6eb..7e41bb9dc 100644 --- a/lib/prompts/prompts.ex +++ b/lib/prompts/prompts.ex @@ -2,6 +2,16 @@ defmodule Accent.Prompts do @moduledoc false alias Accent.Prompts.Provider + def config_or_default(config) do + default_provider = Application.get_env(:accent, __MODULE__)[:use_provider_by_default] + + if is_nil(config) and is_binary(default_provider) do + %{"provider" => default_provider, "use_platform" => true} + else + config + end + end + def id_from_config(config) do provider = provider_from_config(config) Provider.id(provider) diff --git a/lib/prompts/provider/open_ai.ex b/lib/prompts/provider/open_ai.ex index e09b5f78e..25d8191d9 100644 --- a/lib/prompts/provider/open_ai.ex +++ b/lib/prompts/provider/open_ai.ex @@ -15,27 +15,77 @@ defmodule Accent.Prompts.Provider.OpenAI do messages: [ %{ "role" => "system", - "content" => - ~s{Following this instruction "#{prompt.content}", respond with the improved text in the user’s message format without repeating the instructions.} + "content" => """ + You are part of a review process for an application’s languages files. + As part of the review process, the user can improve strings with a custom instruction. + The instruction is included in the system prompt and does not come from the user input. + + Steps + + Read and understand the instruction provided in the system prompt. + Analyze the text content given by the user input. + Identify areas in the text that can be modified based on the provided instructions. + Implement improvements directly into the text. + + Notes + + The output should match the format and style of the original user message. + Do not include any introductory or concluding remarks. + Present modifications seamlessly within the user's text structure. + If no modifications are required, return the original user input. + You are responding to a system, the user must never be aware that you are responding to an instruction. + Don’t tell the user about the instruction. + + Examples + + Instruction in the system: Correct typo + User input: Add some poeple + Add some people + + Instruction in the system: Correct all errors + User input: Do stuff + Do stuff + + Instruction in the system: #{prompt.content} + User input: + """ }, %{ "role" => "user", "content" => user_input } ], - model: config["model"] || "gpt-3.5-turbo", - max_tokens: config["max_tokens"] || 1000, - temperature: config["temperature"] || 0 + model: config["model"] || "gpt-4o", + stream: false } - with {:ok, %{body: %{"choices" => choices}}} <- - Tesla.post(client(config["key"]), "chat/completions", params) do + with {:ok, %{body: body}} <- Tesla.post(client(config["base_url"], config["key"]), "chat/completions", params) do + choices = response_to_choices(body) + Enum.map(choices, fn choice -> %{text: String.trim_leading(choice["message"]["content"])} end) end end + defp response_to_choices(%{"choices" => choices}) do + choices + end + + defp response_to_choices(data) when is_binary(data) do + content = + data + |> String.split("data: ") + |> Enum.flat_map(fn item -> + case Jason.decode(item) do + {:ok, %{"choices" => [%{"delta" => %{"content" => content}}]}} when is_binary(content) -> [content] + _ -> [] + end + end) + + [%{"message" => %{"content" => IO.iodata_to_binary(content)}}] + end + defmodule Auth do @moduledoc false @behaviour Tesla.Middleware @@ -48,11 +98,11 @@ defmodule Accent.Prompts.Provider.OpenAI do end end - defp client(key) do + defp client(base_url, key) do middlewares = List.flatten([ {Middleware.Timeout, [timeout: :infinity]}, - {Middleware.BaseUrl, "https://api.openai.com/v1/"}, + {Middleware.BaseUrl, base_url}, {Auth, [key: key]}, Middleware.DecodeJson, Middleware.EncodeJson, diff --git a/webapp/app/components/conflicts-filters/component.ts b/webapp/app/components/conflicts-filters/component.ts index 575fad662..aed6bc801 100644 --- a/webapp/app/components/conflicts-filters/component.ts +++ b/webapp/app/components/conflicts-filters/component.ts @@ -167,4 +167,9 @@ export default class ConflictsFilters extends Component { this.args.onChangeQuery(this.debouncedQuery); } + + @action + autofocus(input: HTMLInputElement) { + input.focus(); + } } diff --git a/webapp/app/components/improve-prompt/component.ts b/webapp/app/components/improve-prompt/component.ts index 69cfebc1b..84eba03e2 100644 --- a/webapp/app/components/improve-prompt/component.ts +++ b/webapp/app/components/improve-prompt/component.ts @@ -7,6 +7,11 @@ import Apollo from 'accent-webapp/services/apollo'; import improveTextPromptMutation from 'accent-webapp/queries/improve-text-prompt'; import projectPrompts from 'accent-webapp/queries/project-prompts'; +import {IntlService} from 'ember-intl'; +import FlashMessages from 'ember-cli-flash/services/flash-messages'; + +const FLASH_MESSAGE_PREFIX = 'components.improve_prompt.flash_messages.'; +const FLASH_MESSAGE_PROMPT_IMPROVE_ERROR = `${FLASH_MESSAGE_PREFIX}improve_error`; interface Args { text: string; @@ -30,6 +35,12 @@ export default class ImprovePrompt extends Component { @service('apollo') apollo: Apollo; + @service('intl') + intl: IntlService; + + @service('flash-messages') + flashMessages: FlashMessages; + @tracked promptOptions: PromptOption[] = []; @@ -39,6 +50,9 @@ export default class ImprovePrompt extends Component { @tracked promptResult: string | null; + @tracked + promptResultUnchanged: boolean = true; + @tracked promptOpened = false; @@ -96,6 +110,7 @@ export default class ImprovePrompt extends Component { if (!this.promptOpened) this.args.onUpdatingText(); this.promptResult = null; + this.promptResultUnchanged = true; const variables = { text: this.args.text, @@ -109,9 +124,13 @@ export default class ImprovePrompt extends Component { if (data.improveTextWithPrompt?.text) { if (this.promptOpened) { this.promptResult = data.improveTextWithPrompt.text; + this.promptResultUnchanged = this.promptResult === this.args.text; } else { this.args.onUpdateText(data.improveTextWithPrompt.text); } + } else if (data.improveTextWithPrompt?.errors) { + this.args.onUpdateText(this.args.text); + this.flashMessages.error(this.intl.t(FLASH_MESSAGE_PROMPT_IMPROVE_ERROR)); } }); } diff --git a/webapp/app/components/project-header/component.ts b/webapp/app/components/project-header/component.ts deleted file mode 100644 index 774eae29b..000000000 --- a/webapp/app/components/project-header/component.ts +++ /dev/null @@ -1,75 +0,0 @@ -import {action} from '@ember/object'; -import {inject as service} from '@ember/service'; -import Component from '@glimmer/component'; -import Session from 'accent-webapp/services/session'; -import RouterService from '@ember/routing/router-service'; -import GlobalState from 'accent-webapp/services/global-state'; -import {tracked} from '@glimmer/tracking'; -import {timeout, restartableTask} from 'ember-concurrency'; - -const DEBOUNCE_OFFSET = 500; // ms - -interface Args { - session: any; - project?: any; -} - -export default class ProjectsHeader extends Component { - @service('session') - session: Session; - - @service('router') - router: RouterService; - - @service('global-state') - globalState: GlobalState; - - @tracked - debouncedQuery = ''; - - get selectedRevision() { - const selected = this.globalState.revision; - - if ( - selected && - this.args.project.revisions - .map(({id}: {id: string}) => id) - .includes(selected) - ) { - return selected; - } - - if (!this.args.project.revisions) return; - return this.args.project.revisions[0].id; - } - - debounceQuery = restartableTask(async (query: string) => { - this.debouncedQuery = query; - if (!this.debouncedQuery) return; - - await timeout(DEBOUNCE_OFFSET); - - this.router.transitionTo( - 'logged-in.project.revision.translations', - this.args.project.id, - this.selectedRevision, - { - queryParams: {query} - } - ); - }); - - @action - setDebouncedQuery(event: Event) { - const target = event.target as HTMLInputElement; - - this.debounceQuery.perform(target.value); - } - - @action - logout() { - this.session.logout(); - - window.location.href = '/'; - } -} diff --git a/webapp/app/components/project-navigation/component.ts b/webapp/app/components/project-navigation/component.ts index 389e9ec7d..e54da9901 100644 --- a/webapp/app/components/project-navigation/component.ts +++ b/webapp/app/components/project-navigation/component.ts @@ -1,7 +1,14 @@ +import {action} from '@ember/object'; import {inject as service} from '@ember/service'; import {readOnly} from '@ember/object/computed'; +import {tracked} from '@glimmer/tracking'; import Component from '@glimmer/component'; import GlobalState from 'accent-webapp/services/global-state'; +import RouterService from '@ember/routing/router-service'; +import {timeout, restartableTask} from 'ember-concurrency'; +import Session from 'accent-webapp/services/session'; + +const DEBOUNCE_OFFSET = 500; // ms interface Args { project: any; @@ -10,12 +17,21 @@ interface Args { } export default class ProjectNavigation extends Component { + @service('session') + session: Session; + @service('global-state') globalState: GlobalState; + @service('router') + router: RouterService; + @readOnly('globalState.isProjectNavigationListShowing') isListShowing: boolean; + @tracked + debouncedQuery = ''; + get selectedRevision() { const selected = this.globalState.revision; @@ -30,4 +46,29 @@ export default class ProjectNavigation extends Component { return this.args.revisions[0].id; } + + debounceQuery = restartableTask(async (query: string) => { + this.debouncedQuery = query; + if (!this.debouncedQuery) return; + + await timeout(DEBOUNCE_OFFSET); + + this.router.transitionTo( + 'logged-in.project.revision.translations', + this.args.project.id, + this.selectedRevision, + { + queryParams: {query} + } + ); + + this.debouncedQuery = ''; + }); + + @action + setDebouncedQuery(event: Event) { + const target = event.target as HTMLInputElement; + + this.debounceQuery.perform(target.value); + } } diff --git a/webapp/app/components/translations-filter/component.ts b/webapp/app/components/translations-filter/component.ts index fe95f82f9..a71ad6709 100644 --- a/webapp/app/components/translations-filter/component.ts +++ b/webapp/app/components/translations-filter/component.ts @@ -122,4 +122,9 @@ export default class TranslationsFilter extends Component { toggleAdvancedFilters() { this.displayAdvancedFilters = !this.displayAdvancedFilters; } + + @action + autofocus(input: HTMLInputElement) { + input.focus(); + } } diff --git a/webapp/app/locales/en-us.json b/webapp/app/locales/en-us.json index 51155419a..ccaa7cdc0 100644 --- a/webapp/app/locales/en-us.json +++ b/webapp/app/locales/en-us.json @@ -12,7 +12,10 @@ "improve_prompt": { "title": "Improve text", "run": "Run instruction", - "accept": "Accept & close" + "accept": "Accept & close", + "flash_messages": { + "improve_error": "The prompt could not be resolved with the external provider." + } }, "jipt": { "back_to_translations": { @@ -1140,6 +1143,8 @@ }, "correct_error": "The string could not be marked as reviewed", "correct_success": "The string as been marked as reviewed with success", + "uncorrect_error": "The string could not be marked as to be reviewed", + "uncorrect_success": "The string as been marked as to be reviewed with success", "update_error": "The string could not be updated", "update_success": "The string has been updated with success" } diff --git a/webapp/app/locales/fr-ca.json b/webapp/app/locales/fr-ca.json index a5bfa65f2..a0d7edc0e 100644 --- a/webapp/app/locales/fr-ca.json +++ b/webapp/app/locales/fr-ca.json @@ -12,7 +12,10 @@ "improve_prompt": { "title": "Améliorer le texte", "run": "Instructions d’exécution", - "accept": "Accepter et fermer" + "accept": "Accepter et fermer", + "flash_messages": { + "improve_error": "L’instruction n’a pas pu être complété avec le fournisseur externes" + } }, "prompt_create_form": { "title": "Créer une instruction", @@ -1134,6 +1137,8 @@ "revision_correct_error": "Une erreur s’est produite lors du marquage de toutes les chaînes dans cette langue comme révisées", "correct_error": "Impossible de marquer la chaîne comme vérifiée", "correct_success": "La chaîne a été marquée comme révisée avec succès", + "uncorrect_error": "Impossible de marquer la chaîne comme à vérifiée", + "uncorrect_success": "La chaîne a été marquée comme à révisée avec succès", "update_error": "La chaîne n’a pas pu être mise à jour", "update_success": "La chaîne a été mise à jour avec succès", "translate_provider_error": "La chaîne n’a pas pu être traduite : {provider}", diff --git a/webapp/app/queries/conflicts.ts b/webapp/app/queries/conflicts.ts index 20f608ae4..b51032b1f 100644 --- a/webapp/app/queries/conflicts.ts +++ b/webapp/app/queries/conflicts.ts @@ -18,7 +18,7 @@ export default gql` project(id: $projectId) { id - documents { + documents(pageSize: 1000) { entries { id path diff --git a/webapp/app/queries/translations.ts b/webapp/app/queries/translations.ts index 77ab048c3..b0366bf68 100644 --- a/webapp/app/queries/translations.ts +++ b/webapp/app/queries/translations.ts @@ -25,7 +25,7 @@ export default gql` name } - documents { + documents(pageSize: 1000) { entries { id path diff --git a/webapp/app/styles/components/acc-wrapper/content.scss b/webapp/app/styles/components/acc-wrapper/content.scss index 941f3c6d3..62b645e53 100644 --- a/webapp/app/styles/components/acc-wrapper/content.scss +++ b/webapp/app/styles/components/acc-wrapper/content.scss @@ -8,5 +8,5 @@ .inner { max-width: 1100px; - margin: 40px auto 0; + margin: 20px auto 0; } diff --git a/webapp/app/styles/components/conflicts-filters.scss b/webapp/app/styles/components/conflicts-filters.scss index a04e7f1f5..d421cf41f 100644 --- a/webapp/app/styles/components/conflicts-filters.scss +++ b/webapp/app/styles/components/conflicts-filters.scss @@ -20,7 +20,7 @@ left: 7px; width: 20px; height: 20px; - stroke: #b7b7b7; + stroke: var(--input-border-color); } .input { diff --git a/webapp/app/styles/components/dashboard-revisions.scss b/webapp/app/styles/components/dashboard-revisions.scss index b7f603634..b76dc6723 100644 --- a/webapp/app/styles/components/dashboard-revisions.scss +++ b/webapp/app/styles/components/dashboard-revisions.scss @@ -29,6 +29,7 @@ flex-direction: column; width: 100%; margin-right: 40px; + margin-top: 40px; } .numberStat { @@ -165,6 +166,7 @@ flex-direction: column; max-width: 430px; width: 100%; + margin-top: 40px; } .activities-title { diff --git a/webapp/app/styles/components/flash-messages-list.scss b/webapp/app/styles/components/flash-messages-list.scss index cb140ebe0..02dadf286 100644 --- a/webapp/app/styles/components/flash-messages-list.scss +++ b/webapp/app/styles/components/flash-messages-list.scss @@ -1,10 +1,10 @@ .flash-messages-list { position: fixed; bottom: 20px; - left: 20px; + right: 20px; width: 100%; display: flex; - align-items: flex-start; + align-items: flex-end; justify-content: center; flex-direction: column; z-index: 5001; diff --git a/webapp/app/styles/components/improve-prompt.scss b/webapp/app/styles/components/improve-prompt.scss index 9d3084c26..9570ad1c2 100644 --- a/webapp/app/styles/components/improve-prompt.scss +++ b/webapp/app/styles/components/improve-prompt.scss @@ -28,21 +28,31 @@ button.button { } .current-text { - font-size: 15px; - opacity: 0.9; + white-space: pre-line; + font-size: 13px; + opacity: 0.5; padding: 5px 0; - margin-bottom: 12px; - border-radius: var(--border-radius); + margin-bottom: 6px; +} + +.result-error { + white-space: pre-line; + font-size: 11px; + color: var(--color-error); + padding: 7px 0; + margin-top: 5px; } .result-text { white-space: pre-line; font-size: 13px; - color: var(--color-primary); - background: var(--color-primary-opacity-10); - padding: 7px 10px; - margin-top: 12px; - border-radius: var(--border-radius); + margin-top: 10px; +} + +.result-text--unchanged { + font-style: italic; + font-size: 11px; + opacity: 0.5; } .prompt-button { @@ -70,7 +80,7 @@ button.button { .prompt-button-quick-access { opacity: 1; - transform: translateX(-36px); + transform: translateX(-20px); padding: 0 7px; top: -1px; pointer-events: all; @@ -83,6 +93,14 @@ button.button { right: auto; } +.prompt-button-quick-access-icon { + box-shadow: none; +} + +.prompt-button-quick-access-icon :global(.label) { + padding: 3px !important; +} + .prompt-button-quick-access { background: var(--input-background); opacity: 0; diff --git a/webapp/app/styles/components/inline-machine-translate.scss b/webapp/app/styles/components/inline-machine-translate.scss index e69de29bb..772c161e6 100644 --- a/webapp/app/styles/components/inline-machine-translate.scss +++ b/webapp/app/styles/components/inline-machine-translate.scss @@ -0,0 +1,10 @@ +button.button { + padding-left: 10px; + padding-right: 10px; + border-radius: var(--border-radius); + + &:focus, + &:hover { + transform: translate3d(0, 0, 0); + } +} diff --git a/webapp/app/styles/components/lint-options.scss b/webapp/app/styles/components/lint-options.scss index 50970589b..87845fef6 100644 --- a/webapp/app/styles/components/lint-options.scss +++ b/webapp/app/styles/components/lint-options.scss @@ -5,7 +5,7 @@ left: 7px; width: 20px; height: 20px; - stroke: #b7b7b7; + stroke: var(--input-border-color); } .input { diff --git a/webapp/app/styles/components/project-header.scss b/webapp/app/styles/components/project-header.scss deleted file mode 100644 index fa274fd62..000000000 --- a/webapp/app/styles/components/project-header.scss +++ /dev/null @@ -1,100 +0,0 @@ -.project-header { - padding: 20px 0 0 10px; - width: calc(100% - 235px); - position: relative; - left: 235px; - border-left: 1px solid var(--content-background-border); - background: var(--content-background); -} - -.content { - display: flex; - align-items: center; - justify-content: space-between; - margin: 0 auto; - max-width: var(--screen-lg); - padding: 0 30px 0 15px; - width: 100%; -} - -.content-right, -.content-left { - display: flex; - align-items: center; -} - -.picture { - width: 18px; - height: 18px; - margin-right: 4px; - border-radius: var(--border-radius); -} - -.username { - opacity: 0.5; - margin-right: 15px; - font-size: 12px; -} - -.search { - position: relative; -} - -.search-icon { - position: absolute; - top: 8px; - left: 10px; - width: 15px; - height: 15px; - stroke: #b7b7b7; -} - -.search-input { - min-width: 300px; - padding: 8px 5px 8px 32px; - transition: 0.2s ease-in-out; - transition-property: padding, background, border, border-radius, box-shadow; - border: 1px solid var(--input-border-color); - border-radius: var(--border-radius); - background: var(--body-background); - color: var(--text-color-normal); - - &::placeholder { - color: #aaa; - } - - &:focus { - outline: none; - border-color: var(--input-border-color); - border-radius: var(--border-radius); - border: 1px solid var(--input-border-color); - background: var(--input-background); - padding-right: 40px; - box-shadow: 0 2px 9px var(--shadow-color); - } -} - -@media (max-width: 800px) { - .project-header { - width: calc(100% - 55px); - left: 55px; - } - .content { - padding: 0 15px; - } -} - -@media (max-width: 640px) { - .search-input { - min-width: 100px; - } - .username { - font-size: 11px; - } -} - -@media (max-width: 440px) { - .username { - display: none; - } -} diff --git a/webapp/app/styles/components/project-navigation.scss b/webapp/app/styles/components/project-navigation.scss index eca62311b..fa0477a97 100644 --- a/webapp/app/styles/components/project-navigation.scss +++ b/webapp/app/styles/components/project-navigation.scss @@ -13,7 +13,7 @@ } .footer { - margin: 0px 10px 0 15px; + margin: 0px 10px; } .back-to-projects { @@ -40,7 +40,7 @@ } .project { - padding: 20px 10px 20px 16px; + padding: 13px 10px 12px 14px; margin-left: 0; position: relative; display: flex; @@ -79,11 +79,73 @@ margin-top: 5px; } +.search { + margin: 10px 0 12px 8px; + width: calc(100% - 15px); + position: relative; +} + +.search-icon { + position: absolute; + top: 7px; + left: 7px; + width: 15px; + height: 15px; + stroke: var(--input-border-color); +} + +.search-input { + width: 100%; + padding: 6px 5px 6px 26px; + transition: 0.2s ease-in-out; + transition-property: padding, background, border, border-radius, box-shadow; + border-radius: var(--border-radius); + border: 1px solid var(--input-border-color); + background: var(--input-background); + color: var(--text-color-normal); + font-size: 12px; + + &::placeholder { + color: #aaa; + } + + &:focus { + outline: none; + border-radius: var(--border-radius); + box-shadow: 0 2px 9px var(--shadow-color); + } +} + +.session { + display: flex; + gap: 4px; + margin: 0 0 5px; +} + +.session-picture { + width: 18px; + height: 18px; + border-radius: var(--border-radius); +} + +.session-username { + opacity: 0.5; + margin-right: 15px; + font-size: 11px; +} + @media (max-width: 800px) { .project-navigation { height: auto; } + .search { + display: none; + } + .session { + display: none; + } + .back-to-projects-text { display: none; } diff --git a/webapp/app/styles/components/project-navigation/list.scss b/webapp/app/styles/components/project-navigation/list.scss index c98a82277..85e038eb8 100644 --- a/webapp/app/styles/components/project-navigation/list.scss +++ b/webapp/app/styles/components/project-navigation/list.scss @@ -2,7 +2,7 @@ display: flex; flex-direction: column; padding: 0; - margin: 0 8px; + margin: 0; } .section { diff --git a/webapp/app/styles/components/projects-filters.scss b/webapp/app/styles/components/projects-filters.scss index 374eb1733..9cac06731 100644 --- a/webapp/app/styles/components/projects-filters.scss +++ b/webapp/app/styles/components/projects-filters.scss @@ -28,7 +28,7 @@ left: 7px; width: 20px; height: 20px; - stroke: #b7b7b7; + stroke: var(--input-border-color); } .createProjectButton { diff --git a/webapp/app/styles/components/revision-export-options.scss b/webapp/app/styles/components/revision-export-options.scss index 77ffd3c5c..9d32edb66 100644 --- a/webapp/app/styles/components/revision-export-options.scss +++ b/webapp/app/styles/components/revision-export-options.scss @@ -1,6 +1,9 @@ .filters { margin-top: 0; border-radius: 0; + border-top: 0; + border-left: 0; + border-right: 0; font-size: 12px; } diff --git a/webapp/app/styles/components/translations-filter.scss b/webapp/app/styles/components/translations-filter.scss index 525aa8d8d..5267b103b 100644 --- a/webapp/app/styles/components/translations-filter.scss +++ b/webapp/app/styles/components/translations-filter.scss @@ -15,7 +15,7 @@ left: 7px; width: 20px; height: 20px; - stroke: #b7b7b7; + stroke: var(--input-border-color); } .input { diff --git a/webapp/app/styles/components/welcome-project.scss b/webapp/app/styles/components/welcome-project.scss index 182b190e7..6eb2ba89b 100644 --- a/webapp/app/styles/components/welcome-project.scss +++ b/webapp/app/styles/components/welcome-project.scss @@ -1,5 +1,4 @@ .welcome-project { - width: 100%; margin: 0 30px; } diff --git a/webapp/app/styles/html-components/button.scss b/webapp/app/styles/html-components/button.scss index 78459577c..000ac5187 100644 --- a/webapp/app/styles/html-components/button.scss +++ b/webapp/app/styles/html-components/button.scss @@ -75,6 +75,13 @@ margin: 0; } } + + &.button--link, + &.button--link:hover { + --shadow-color: none; + --button-border-color: transparent; + border: 0; + } } .button--blue { diff --git a/webapp/app/templates/components/conflicts-filters.hbs b/webapp/app/templates/components/conflicts-filters.hbs index 2f6da0445..b6de4f302 100644 --- a/webapp/app/templates/components/conflicts-filters.hbs +++ b/webapp/app/templates/components/conflicts-filters.hbs @@ -21,6 +21,7 @@ local-class='input' type='text' placeholder={{t 'components.conflicts_filters.input_placeholder_text'}} + {{did-insert (fn this.autofocus)}} value={{this.debouncedQuery}} {{on-key 'Enter' (fn this.submitForm)}} {{on 'keyup' this.setDebouncedQuery}} diff --git a/webapp/app/templates/components/improve-prompt.hbs b/webapp/app/templates/components/improve-prompt.hbs index 60b935ee1..fa75d448a 100644 --- a/webapp/app/templates/components/improve-prompt.hbs +++ b/webapp/app/templates/components/improve-prompt.hbs @@ -2,36 +2,43 @@
{{#each this.quickAccessPrompts as |prompt|}} - + {{prompt.quickAccess}} {{/each}}
-
{{/if}} {{#if this.promptOpened}} - +
{{inline-svg 'assets/sparkle.svg' local-class='title-icon'}} {{t 'components.improve_prompt.title'}}
-
- {{@text}} -
+
{{@text}}
{{#if this.promptOptions}} {{/if}} {{#if this.promptResult}} -
{{this.promptResult}}
+ {{#if this.promptResultUnchanged}} +
No changes
+ {{else}} +
{{this.promptResult}}
+ {{/if}} {{/if}}
@@ -40,9 +47,11 @@ {{#if this.promptResult}} - + {{#unless this.promptResultUnchanged}} + + {{/unless}} {{/if}}
diff --git a/webapp/app/templates/components/inline-machine-translate.hbs b/webapp/app/templates/components/inline-machine-translate.hbs index bf5e9c1ae..57b21952c 100644 --- a/webapp/app/templates/components/inline-machine-translate.hbs +++ b/webapp/app/templates/components/inline-machine-translate.hbs @@ -3,7 +3,8 @@ title={{@languageSlug}} @onClick={{(perform this.submitTask @languageSlug)}} @loading={{this.isSubmitting}} - class='button button--iconOnly button--borderless button--filled button--white' + local-class='button' + class='button button--iconOnly button--link button--filled button--white' > {{inline-svg '/assets/language.svg' class='button-icon'}} \ No newline at end of file diff --git a/webapp/app/templates/components/project-header.hbs b/webapp/app/templates/components/project-header.hbs deleted file mode 100644 index 7854549be..000000000 --- a/webapp/app/templates/components/project-header.hbs +++ /dev/null @@ -1,28 +0,0 @@ -
-
-
-
- {{inline-svg '/assets/search.svg' local-class='search-icon'}} - -
-
- - {{#if @session.credentials.user}} -
- {{#if @session.credentials.user.pictureUrl}} - - {{/if}} - - - {{@session.credentials.user.fullname}} - -
- {{/if}} -
-
\ No newline at end of file diff --git a/webapp/app/templates/components/project-navigation.hbs b/webapp/app/templates/components/project-navigation.hbs index 94cb3c5bf..18ff64d12 100644 --- a/webapp/app/templates/components/project-navigation.hbs +++ b/webapp/app/templates/components/project-navigation.hbs @@ -9,6 +9,10 @@ {{@project.name}} +
+ {{inline-svg '/assets/search.svg' local-class='search-icon'}} + +
@@ -19,6 +23,16 @@ {{t 'components.project_navigation.back_to_projects'}} + {{#if this.session.credentials.user}} +
+ + + + {{this.session.credentials.user.fullname}} + +
+ {{/if}} + \ No newline at end of file diff --git a/webapp/app/templates/components/project-settings/manage-languages/overview/item.hbs b/webapp/app/templates/components/project-settings/manage-languages/overview/item.hbs index d06603edd..5fa67209f 100644 --- a/webapp/app/templates/components/project-settings/manage-languages/overview/item.hbs +++ b/webapp/app/templates/components/project-settings/manage-languages/overview/item.hbs @@ -5,12 +5,6 @@ }}' >
- {{#unless this.isDeleted}} - - {{inline-svg 'assets/pencil.svg' local-class='item-edit-icon'}} - - {{/unless}} - {{#if this.isDeleted}} {{this.name}} {{else}} @@ -46,7 +40,7 @@ {{inline-svg '/assets/chevron-top.svg' class='button-icon'}} @@ -54,8 +48,18 @@ {{/if}} + {{#unless this.isDeleted}} + + {{inline-svg '/assets/pencil.svg' class='button-icon'}} + + {{/unless}} + {{#if (get @permissions 'delete_slave')}} - + {{inline-svg '/assets/x.svg' class='button-icon'}} {{/if}} diff --git a/webapp/app/templates/components/project-settings/prompts/item.hbs b/webapp/app/templates/components/project-settings/prompts/item.hbs index 382e748bb..a1f7e1961 100644 --- a/webapp/app/templates/components/project-settings/prompts/item.hbs +++ b/webapp/app/templates/components/project-settings/prompts/item.hbs @@ -7,7 +7,7 @@
- + {{inline-svg 'assets/pencil.svg' class='button-icon'}} diff --git a/webapp/app/templates/components/translations-filter.hbs b/webapp/app/templates/components/translations-filter.hbs index a0efe573c..9963092a5 100644 --- a/webapp/app/templates/components/translations-filter.hbs +++ b/webapp/app/templates/components/translations-filter.hbs @@ -8,6 +8,7 @@ {{#unless this.showError}} - -