Merge branch 'develop' into feat/app-store-reviews
This commit is contained in:
@@ -142,29 +142,27 @@ watch(
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.editor-wrapper {
|
||||
::v-deep {
|
||||
.ProseMirror-menubar-wrapper {
|
||||
.ProseMirror.ProseMirror-woot-style {
|
||||
p {
|
||||
@apply first:mt-0 !important;
|
||||
}
|
||||
|
||||
.empty-node {
|
||||
@apply m-0 !important;
|
||||
|
||||
&::before {
|
||||
@apply text-n-slate-11 dark:text-n-slate-11;
|
||||
}
|
||||
}
|
||||
:deep(.ProseMirror-menubar-wrapper) {
|
||||
.ProseMirror.ProseMirror-woot-style {
|
||||
p {
|
||||
@apply first:mt-0 !important;
|
||||
}
|
||||
|
||||
.ProseMirror-menubar {
|
||||
width: fit-content !important;
|
||||
position: relative !important;
|
||||
top: unset !important;
|
||||
@apply ltr:left-[-0.188rem] rtl:right-[-0.188rem] !important;
|
||||
.empty-node {
|
||||
@apply m-0 !important;
|
||||
|
||||
&::before {
|
||||
@apply text-n-slate-11 dark:text-n-slate-11;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ProseMirror-menubar {
|
||||
width: fit-content !important;
|
||||
position: relative !important;
|
||||
top: unset !important;
|
||||
@apply ltr:left-[-0.188rem] rtl:right-[-0.188rem] !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+30
-32
@@ -145,45 +145,43 @@ const handleCreateArticle = event => {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep {
|
||||
.ProseMirror .empty-node::before {
|
||||
@apply text-n-slate-10 text-base;
|
||||
}
|
||||
:deep(.ProseMirror .empty-node::before) {
|
||||
@apply text-n-slate-10 text-base;
|
||||
}
|
||||
|
||||
.ProseMirror-menubar-wrapper {
|
||||
.ProseMirror-woot-style {
|
||||
@apply min-h-[15rem] max-h-full;
|
||||
}
|
||||
:deep(.ProseMirror-menubar-wrapper) {
|
||||
.ProseMirror-woot-style {
|
||||
@apply min-h-[15rem] max-h-full;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ProseMirror-menubar) {
|
||||
display: none; // Hide by default
|
||||
}
|
||||
|
||||
:deep(.editor-root .has-selection) {
|
||||
.ProseMirror-menubar:not(:has(*)) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.ProseMirror-menubar {
|
||||
display: none; // Hide by default
|
||||
}
|
||||
@apply rounded-lg !px-3 !py-1.5 z-50 bg-n-background items-center gap-4 ml-0 mb-0 shadow-md outline outline-1 outline-n-weak;
|
||||
display: flex;
|
||||
top: var(--selection-top, auto) !important;
|
||||
left: var(--selection-left, 0) !important;
|
||||
width: fit-content !important;
|
||||
position: absolute !important;
|
||||
|
||||
.editor-root .has-selection {
|
||||
.ProseMirror-menubar:not(:has(*)) {
|
||||
display: none !important;
|
||||
.ProseMirror-menuitem {
|
||||
@apply ltr:mr-0 rtl:ml-0 size-4 flex items-center;
|
||||
|
||||
.ProseMirror-icon {
|
||||
@apply p-0.5 flex-shrink-0 ltr:mr-2 rtl:ml-2;
|
||||
}
|
||||
}
|
||||
|
||||
.ProseMirror-menubar {
|
||||
@apply rounded-lg !px-3 !py-1.5 z-50 bg-n-background items-center gap-4 ml-0 mb-0 shadow-md outline outline-1 outline-n-weak;
|
||||
display: flex;
|
||||
top: var(--selection-top, auto) !important;
|
||||
left: var(--selection-left, 0) !important;
|
||||
width: fit-content !important;
|
||||
position: absolute !important;
|
||||
|
||||
.ProseMirror-menuitem {
|
||||
@apply ltr:mr-0 rtl:ml-0 size-4 flex items-center;
|
||||
|
||||
.ProseMirror-icon {
|
||||
@apply p-0.5 flex-shrink-0 ltr:mr-2 rtl:ml-2;
|
||||
}
|
||||
}
|
||||
|
||||
.ProseMirror-menu-active {
|
||||
@apply bg-n-slate-3;
|
||||
}
|
||||
.ProseMirror-menu-active {
|
||||
@apply bg-n-slate-3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup>
|
||||
import { defineProps } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Icon from 'dashboard/components-next/icon/Icon.vue';
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { defineModel, watch } from 'vue';
|
||||
import { watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import Input from 'dashboard/components-next/input/Input.vue';
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, defineModel, ref, watch } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import Input from 'dashboard/components-next/input/Input.vue';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, defineProps, defineEmits } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { Chrome } from '@lk77/vue3-color';
|
||||
import { OnClickOutside } from '@vueuse/components';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { defineProps, ref, defineEmits, computed, onMounted } from 'vue';
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Icon from 'dashboard/components-next/icon/Icon.vue';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, defineModel, h, watch, ref } from 'vue';
|
||||
import { computed, h, watch, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import Button from 'next/button/Button.vue';
|
||||
import Input from 'dashboard/components-next/input/Input.vue';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { defineModel, computed } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import Icon from 'next/icon/Icon.vue';
|
||||
import Button from 'next/button/Button.vue';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { defineModel, computed, ref } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { picoSearch } from '@scmmishra/pico-search';
|
||||
import Icon from 'next/icon/Icon.vue';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { h, defineProps } from 'vue';
|
||||
import { h } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
country: { type: String, required: true },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { defineProps, computed, reactive } from 'vue';
|
||||
import { computed, reactive } from 'vue';
|
||||
import Message from './Message.vue';
|
||||
import { MESSAGE_TYPES } from './constants.js';
|
||||
import { useCamelCase } from 'dashboard/composables/useTransformKeys';
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<script setup>
|
||||
import { defineProps, defineEmits } from 'vue';
|
||||
|
||||
defineProps({
|
||||
showingOriginal: Boolean,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, defineOptions, useAttrs } from 'vue';
|
||||
import { computed, useAttrs } from 'vue';
|
||||
|
||||
import ImageChip from 'next/message/chips/Image.vue';
|
||||
import VideoChip from 'next/message/chips/Video.vue';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup>
|
||||
import EmojiOrIcon from 'shared/components/EmojiOrIcon.vue';
|
||||
import { defineEmits } from 'vue';
|
||||
|
||||
defineProps({
|
||||
title: {
|
||||
|
||||
@@ -339,17 +339,15 @@ export default {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep {
|
||||
.selector-wrap {
|
||||
@apply m-0 top-1;
|
||||
:deep(.selector-wrap) {
|
||||
@apply m-0 top-1;
|
||||
|
||||
.selector-name {
|
||||
@apply ml-0;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
.selector-name {
|
||||
@apply ml-0;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.name) {
|
||||
@apply ml-0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, defineEmits } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { useIntersectionObserver } from '@vueuse/core';
|
||||
|
||||
const { options } = defineProps({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
// [TODO] Use Teleport to move the modal to the end of the body
|
||||
import { ref, computed, defineEmits, onMounted } from 'vue';
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup>
|
||||
import { defineEmits, defineModel } from 'vue';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
defineProps({
|
||||
|
||||
@@ -63,10 +63,8 @@ export default {
|
||||
.colorpicker--chrome.vc-chrome {
|
||||
@apply shadow-lg -mt-2.5 absolute z-[9999] border border-solid border-n-weak rounded;
|
||||
|
||||
::v-deep {
|
||||
input {
|
||||
@apply bg-white dark:bg-white;
|
||||
}
|
||||
:deep(input) {
|
||||
@apply bg-white dark:bg-white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import getUuid from 'widget/helpers/uuid';
|
||||
import { ref, onMounted, onUnmounted, defineEmits, defineExpose } from 'vue';
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import WaveSurfer from 'wavesurfer.js';
|
||||
import RecordPlugin from 'wavesurfer.js/dist/plugins/record.js';
|
||||
import { format, intervalToDuration } from 'date-fns';
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useI18n } from 'vue-i18n';
|
||||
import { useElementSize, useWindowSize } from '@vueuse/core';
|
||||
import { useMapGetter } from 'dashboard/composables/store';
|
||||
import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants';
|
||||
import { useCaptain } from 'dashboard/composables/useCaptain';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import DropdownBody from 'next/dropdown-menu/base/DropdownBody.vue';
|
||||
|
||||
@@ -19,9 +18,11 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
editorContent: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
// Signature-aware emptiness is computed by the parent (which has access to
|
||||
// the signature + channel context) and passed in as a boolean.
|
||||
hasContent: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
conversationId: {
|
||||
type: Number,
|
||||
@@ -33,17 +34,8 @@ const emit = defineEmits(['executeCopilotAction']);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { draftMessage } = useCaptain();
|
||||
|
||||
const replyMode = useMapGetter('draftMessages/getReplyEditorMode');
|
||||
|
||||
// When editorContent prop is passed, use it exclusively (even if empty)
|
||||
// This ensures each editor instance shows menu items based on its own content
|
||||
// Falls back to global draftMessage only when editorContent is not provided
|
||||
const effectiveContent = computed(() =>
|
||||
props.editorContent !== undefined ? props.editorContent : draftMessage.value
|
||||
);
|
||||
|
||||
// Selection-based menu items (when text is selected)
|
||||
const menuItems = computed(() => {
|
||||
const items = [];
|
||||
@@ -63,7 +55,7 @@ const menuItems = computed(() => {
|
||||
} else if (
|
||||
props.conversationId &&
|
||||
replyMode.value === REPLY_EDITOR_MODES.REPLY &&
|
||||
effectiveContent.value
|
||||
props.hasContent
|
||||
) {
|
||||
items.push({
|
||||
label: t('INTEGRATION_SETTINGS.OPEN_AI.REPLY_OPTIONS.IMPROVE_REPLY'),
|
||||
@@ -72,7 +64,7 @@ const menuItems = computed(() => {
|
||||
});
|
||||
}
|
||||
|
||||
if (effectiveContent.value) {
|
||||
if (props.hasContent) {
|
||||
items.push(
|
||||
{
|
||||
label: t(
|
||||
|
||||
@@ -354,16 +354,17 @@ function isBodyEmpty(content) {
|
||||
// if content is undefined, we assume that the body is empty
|
||||
if (!content) return true;
|
||||
|
||||
// if the signature is present, we need to remove it before checking
|
||||
// note that we don't update the editorView, so this is safe
|
||||
// Use effective channel type to match how signature was appended
|
||||
const bodyWithoutSignature = props.signature
|
||||
? removeSignatureHelper(
|
||||
content,
|
||||
props.signature,
|
||||
effectiveChannelType.value
|
||||
)
|
||||
: content;
|
||||
// Only strip the signature when it's actually being auto-appended for this
|
||||
// draft. Otherwise an agent whose typed text happens to match their saved
|
||||
// signature would be mistakenly treated as empty.
|
||||
const bodyWithoutSignature =
|
||||
sendWithSignature.value && props.signature
|
||||
? removeSignatureHelper(
|
||||
content,
|
||||
props.signature,
|
||||
effectiveChannelType.value
|
||||
)
|
||||
: content;
|
||||
|
||||
// trimming should remove all the whitespaces, so we can check the length
|
||||
return bodyWithoutSignature.trim().length === 0;
|
||||
@@ -474,17 +475,6 @@ function removeSignature() {
|
||||
reloadState(content);
|
||||
}
|
||||
|
||||
function toggleSignatureInEditor(signatureEnabled) {
|
||||
// The toggleSignatureInEditor gets the new value from the
|
||||
// watcher, this means that if the value is true, the signature
|
||||
// is supposed to be added, else we remove it.
|
||||
if (signatureEnabled) {
|
||||
addSignature();
|
||||
} else {
|
||||
removeSignature();
|
||||
}
|
||||
}
|
||||
|
||||
function setToolbarPosition() {
|
||||
const editorRect = editorRoot.value.getBoundingClientRect();
|
||||
const rect = selectedImageNode.value.getBoundingClientRect();
|
||||
@@ -559,6 +549,20 @@ function emitOnChange() {
|
||||
emit('update:modelValue', contentFromEditor());
|
||||
}
|
||||
|
||||
function toggleSignatureInEditor(signatureEnabled) {
|
||||
// The toggleSignatureInEditor gets the new value from the
|
||||
// watcher, this means that if the value is true, the signature
|
||||
// is supposed to be added, else we remove it.
|
||||
if (signatureEnabled) {
|
||||
addSignature();
|
||||
} else {
|
||||
removeSignature();
|
||||
}
|
||||
// reloadState replaces editor state directly and bypasses dispatchTransaction,
|
||||
// so v-model never hears about the signature change — sync it back explicitly.
|
||||
emitOnChange();
|
||||
}
|
||||
|
||||
function updateImgToolbarOnDelete() {
|
||||
// check if the selected node is present or not on keyup
|
||||
// this is needed because the user can select an image and then delete it
|
||||
@@ -899,7 +903,7 @@ useEmitter(BUS_EVENTS.INSERT_INTO_RICH_EDITOR, insertContentIntoEditor);
|
||||
v-on-click-outside="handleClickOutside"
|
||||
:has-selection="isTextSelected"
|
||||
:is-editor-menu-popover="isEditorMenuPopover"
|
||||
:editor-content="modelValue"
|
||||
:has-content="!isBodyEmpty(modelValue)"
|
||||
:conversation-id="conversationId"
|
||||
:show-selection-menu="showSelectionMenu"
|
||||
:show-general-menu="false"
|
||||
|
||||
@@ -419,7 +419,7 @@ export default {
|
||||
@apply flex;
|
||||
}
|
||||
|
||||
::v-deep .file-uploads {
|
||||
:deep(.file-uploads) {
|
||||
label {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,10 @@ export default {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
hasContent: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['setReplyMode', 'toggleEditorSize', 'executeCopilotAction'],
|
||||
setup(props, { emit }) {
|
||||
@@ -76,6 +80,7 @@ export default {
|
||||
|
||||
const { captainTasksEnabled } = useCaptain();
|
||||
const showCopilotMenu = ref(false);
|
||||
const copilotToggleRef = ref(null);
|
||||
|
||||
const handleCopilotAction = (actionKey, data) => {
|
||||
emit('executeCopilotAction', actionKey, data || props.editorContent);
|
||||
@@ -117,6 +122,7 @@ export default {
|
||||
captainTasksEnabled,
|
||||
handleCopilotAction,
|
||||
showCopilotMenu,
|
||||
copilotToggleRef,
|
||||
toggleCopilotMenu,
|
||||
handleClickOutside,
|
||||
};
|
||||
@@ -164,6 +170,7 @@ export default {
|
||||
<div v-if="captainTasksEnabled" class="flex items-center gap-2">
|
||||
<div class="relative">
|
||||
<NextButton
|
||||
ref="copilotToggleRef"
|
||||
ghost
|
||||
:disabled="disabled || isEditorDisabled"
|
||||
:class="{
|
||||
@@ -176,9 +183,12 @@ export default {
|
||||
/>
|
||||
<CopilotMenuBar
|
||||
v-if="showCopilotMenu"
|
||||
v-on-click-outside="handleClickOutside"
|
||||
v-on-click-outside="[
|
||||
handleClickOutside,
|
||||
{ ignore: [copilotToggleRef] },
|
||||
]"
|
||||
:has-selection="false"
|
||||
:editor-content="editorContent"
|
||||
:has-content="hasContent"
|
||||
:conversation-id="conversationId"
|
||||
class="ltr:right-0 rtl:left-0 bottom-full mb-2"
|
||||
@execute-copilot-action="handleCopilotAction"
|
||||
|
||||
@@ -179,6 +179,21 @@ export default {
|
||||
}
|
||||
return true;
|
||||
},
|
||||
hasMeaningfulEditorContent() {
|
||||
const body = this.message || '';
|
||||
// Only strip the signature when it's actually being auto-appended.
|
||||
// If the toggle is off, the agent's text might happen to match their
|
||||
// saved signature and we'd incorrectly treat it as empty.
|
||||
const shouldStripSignature =
|
||||
!this.isPrivate && this.sendWithSignature && !!this.messageSignature;
|
||||
if (!shouldStripSignature) return !!body.trim();
|
||||
const stripped = removeSignature(
|
||||
body,
|
||||
this.messageSignature,
|
||||
getEffectiveChannelType(this.channelType, this.inbox?.medium || '')
|
||||
);
|
||||
return !!stripped.trim();
|
||||
},
|
||||
isReplyRestricted() {
|
||||
return (
|
||||
!this.currentChat?.can_reply &&
|
||||
@@ -1231,6 +1246,7 @@ export default {
|
||||
:is-message-length-reaching-threshold="isMessageLengthReachingThreshold"
|
||||
:characters-remaining="charactersRemaining"
|
||||
:editor-content="message"
|
||||
:has-content="hasMeaningfulEditorContent"
|
||||
@set-reply-mode="setReplyMode"
|
||||
@toggle-editor-size="toggleEditorSize"
|
||||
@toggle-copilot="copilot.toggleEditor"
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, computed, defineOptions } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import FilterButton from 'dashboard/components/ui/Dropdown/DropdownButton.vue';
|
||||
import FilterListDropdown from 'dashboard/components/ui/Dropdown/DropdownList.vue';
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ const variableKey = (item = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
.canned-item__button::v-deep .button__content {
|
||||
.canned-item__button :deep(.button__content) {
|
||||
@apply overflow-hidden text-ellipsis whitespace-nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import {
|
||||
messageSchema,
|
||||
MessageMarkdownTransformer,
|
||||
MessageMarkdownSerializer,
|
||||
MessageMarkdownTransformer,
|
||||
messageSchema,
|
||||
Selection,
|
||||
} from '@chatwoot/prosemirror-schema';
|
||||
import { replaceVariablesInMessage } from '@chatwoot/utils';
|
||||
import * as Sentry from '@sentry/vue';
|
||||
import camelcaseKeys from 'camelcase-keys';
|
||||
import { FORMATTING, MARKDOWN_PATTERNS } from 'dashboard/constants/editor';
|
||||
import { INBOX_TYPES, TWILIO_CHANNEL_MEDIUM } from 'dashboard/helper/inbox';
|
||||
import camelcaseKeys from 'camelcase-keys';
|
||||
|
||||
/**
|
||||
* Extract text from markdown, and remove all images, code blocks, links, headers, bold, italic, lists etc.
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
import { EditorState, EditorView } from '@chatwoot/prosemirror-schema';
|
||||
import { FORMATTING } from 'dashboard/constants/editor';
|
||||
import { Schema } from 'prosemirror-model';
|
||||
import {
|
||||
findSignatureInBody,
|
||||
appendSignature,
|
||||
removeSignature,
|
||||
replaceSignature,
|
||||
calculateMenuPosition,
|
||||
cleanSignature,
|
||||
collapseSelection,
|
||||
extractTextFromMarkdown,
|
||||
stripUnsupportedMarkdown,
|
||||
insertAtCursor,
|
||||
findNodeToInsertImage,
|
||||
setURLWithQueryAndSize,
|
||||
findSignatureInBody,
|
||||
getContentNode,
|
||||
getFormattingForEditor,
|
||||
getSelectionCoords,
|
||||
getMenuAnchor,
|
||||
calculateMenuPosition,
|
||||
stripUnsupportedFormatting,
|
||||
getSelectionCoords,
|
||||
insertAtCursor,
|
||||
removeSignature,
|
||||
replaceSignature,
|
||||
setURLWithQueryAndSize,
|
||||
stripInlineBase64Images,
|
||||
collapseSelection,
|
||||
stripUnsupportedFormatting,
|
||||
stripUnsupportedMarkdown,
|
||||
} from '../editorHelper';
|
||||
import { FORMATTING } from 'dashboard/constants/editor';
|
||||
import { EditorState } from '@chatwoot/prosemirror-schema';
|
||||
import { EditorView } from '@chatwoot/prosemirror-schema';
|
||||
import { Schema } from 'prosemirror-model';
|
||||
|
||||
// Define a basic ProseMirror schema
|
||||
const schema = new Schema({
|
||||
|
||||
@@ -272,13 +272,11 @@ export default {
|
||||
}
|
||||
|
||||
.context-menu--delete-modal {
|
||||
::v-deep {
|
||||
.modal-container {
|
||||
@apply max-w-[30rem];
|
||||
:deep(.modal-container) {
|
||||
@apply max-w-[30rem];
|
||||
|
||||
h2 {
|
||||
@apply font-medium text-base;
|
||||
}
|
||||
h2 {
|
||||
@apply font-medium text-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,12 +104,12 @@ const authorText = computed(() => {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.message-content::v-deep p {
|
||||
.message-content :deep(p) {
|
||||
@apply inline;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.message-content::v-deep br {
|
||||
.message-content :deep(br) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, computed, defineModel, watch } from 'vue';
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useToggle } from '@vueuse/core';
|
||||
import { vOnClickOutside } from '@vueuse/components';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, ref, defineModel } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useToggle } from '@vueuse/core';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { vOnClickOutside } from '@vueuse/components';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, defineModel } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import SearchDateRangeSelector from './SearchDateRangeSelector.vue';
|
||||
import SearchContactAgentSelector from './SearchContactAgentSelector.vue';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, watch, useTemplateRef, defineModel } from 'vue';
|
||||
import { ref, watch, useTemplateRef } from 'vue';
|
||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||
import { INSTALLATION_TYPES } from 'dashboard/constants/installationTypes';
|
||||
import { ROLES } from 'dashboard/constants/permissions';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, computed, defineModel } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useToggle } from '@vueuse/core';
|
||||
import { vOnClickOutside } from '@vueuse/components';
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { defineProps, computed } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import { useMapGetter } from 'dashboard/composables/store.js';
|
||||
|
||||
import SearchResultSection from './SearchResultSection.vue';
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<script setup>
|
||||
import { defineProps } from 'vue';
|
||||
|
||||
defineProps({
|
||||
config: {
|
||||
type: Object,
|
||||
|
||||
@@ -317,9 +317,7 @@ onMounted(() => {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep {
|
||||
.contact--profile {
|
||||
@apply pb-3 border-b border-solid border-n-weak;
|
||||
}
|
||||
:deep(.contact--profile) {
|
||||
@apply pb-3 border-b border-solid border-n-weak;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -143,17 +143,15 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
::v-deep {
|
||||
.ProseMirror-menubar {
|
||||
@apply hidden;
|
||||
}
|
||||
:deep(.ProseMirror-menubar) {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.ProseMirror-woot-style {
|
||||
@apply min-h-[12.5rem];
|
||||
:deep(.ProseMirror-woot-style) {
|
||||
@apply min-h-[12.5rem];
|
||||
|
||||
p {
|
||||
@apply text-base;
|
||||
}
|
||||
p {
|
||||
@apply text-base;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -147,17 +147,15 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
::v-deep {
|
||||
.ProseMirror-menubar {
|
||||
@apply hidden;
|
||||
}
|
||||
:deep(.ProseMirror-menubar) {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.ProseMirror-woot-style {
|
||||
@apply min-h-[12.5rem];
|
||||
:deep(.ProseMirror-woot-style) {
|
||||
@apply min-h-[12.5rem];
|
||||
|
||||
p {
|
||||
@apply text-base;
|
||||
}
|
||||
p {
|
||||
@apply text-base;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,7 +4,7 @@ import AddCanned from './AddCanned.vue';
|
||||
import EditCanned from './EditCanned.vue';
|
||||
import SettingsLayout from '../SettingsLayout.vue';
|
||||
import BaseSettingsHeader from '../components/BaseSettingsHeader.vue';
|
||||
import { computed, onMounted, ref, defineOptions } from 'vue';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useStoreGetters, useStore } from 'dashboard/composables/store';
|
||||
import { picoSearch } from '@scmmishra/pico-search';
|
||||
|
||||
+19
-17
@@ -74,23 +74,25 @@ const tableHeaders = computed(() => {
|
||||
|
||||
<template>
|
||||
<div class="w-full min-h-[12rem] relative">
|
||||
<div class="w-full space-y-3 text-sm">
|
||||
<thead class="opacity-30 dark:opacity-30">
|
||||
<th
|
||||
v-for="thHeader in tableHeaders"
|
||||
:key="thHeader"
|
||||
class="py-4 ltr:pr-4 rtl:pl-4 text-start text-heading-3 text-n-slate-12"
|
||||
>
|
||||
<span class="mb-0">
|
||||
{{ thHeader }}
|
||||
</span>
|
||||
</th>
|
||||
</thead>
|
||||
<CustomRoleListItem
|
||||
class="opacity-25 dark:opacity-20"
|
||||
:roles="dummyCustomRolesData"
|
||||
:loading="{}"
|
||||
/>
|
||||
<div class="w-full space-y-3 text-sm overflow-x-auto">
|
||||
<table class="min-w-full">
|
||||
<thead class="opacity-30 dark:opacity-30">
|
||||
<tr>
|
||||
<th
|
||||
v-for="thHeader in tableHeaders"
|
||||
:key="thHeader"
|
||||
class="py-4 ltr:pr-4 rtl:pl-4 text-start text-heading-3 text-n-slate-12"
|
||||
>
|
||||
<span class="mb-0">
|
||||
{{ thHeader }}
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="opacity-25 dark:opacity-20">
|
||||
<CustomRoleListItem :roles="dummyCustomRolesData" :loading="{}" />
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div
|
||||
class="absolute inset-0 flex flex-col items-center justify-center w-full h-full bg-gradient-to-t from-white dark:from-slate-900 to-transparent"
|
||||
|
||||
@@ -156,10 +156,8 @@ onMounted(() => {
|
||||
.message-editor {
|
||||
@apply px-3;
|
||||
|
||||
::v-deep {
|
||||
.ProseMirror-menubar {
|
||||
@apply rounded-tl-[4px];
|
||||
}
|
||||
:deep(.ProseMirror-menubar) {
|
||||
@apply rounded-tl-[4px];
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
-1
@@ -1,6 +1,5 @@
|
||||
<script setup>
|
||||
import OAuthChannel from './OAuthChannel.vue';
|
||||
import { defineOptions } from 'vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'GoogleOAuthChannel',
|
||||
|
||||
-1
@@ -1,6 +1,5 @@
|
||||
<script setup>
|
||||
import OAuthChannel from './OAuthChannel.vue';
|
||||
import { defineOptions } from 'vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'MicrosoftOAuthChannel',
|
||||
|
||||
+1
-1
@@ -227,7 +227,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep.message-editor {
|
||||
:deep(.message-editor) {
|
||||
@apply border-0;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -446,7 +446,7 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.whatsapp-settings--content {
|
||||
::v-deep input {
|
||||
:deep(input) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { reactive, onMounted, ref, defineProps, watch, computed } from 'vue';
|
||||
import { reactive, onMounted, ref, watch, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useStore, useMapGetter } from 'dashboard/composables/store';
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
<script setup>
|
||||
import { defineProps, defineEmits } from 'vue';
|
||||
import { useIntegrationHook } from 'dashboard/composables/useIntegrationHook';
|
||||
import { useBranding } from 'shared/composables/useBranding';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
@@ -132,10 +132,8 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
// Label API supports only lowercase letters
|
||||
.label-name--input {
|
||||
::v-deep {
|
||||
input {
|
||||
@apply lowercase;
|
||||
}
|
||||
:deep(input) {
|
||||
@apply lowercase;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -135,10 +135,8 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
// Label API supports only lowercase letters
|
||||
.label-name--input {
|
||||
::v-deep {
|
||||
input {
|
||||
@apply lowercase;
|
||||
}
|
||||
:deep(input) {
|
||||
@apply lowercase;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, inject, defineModel } from 'vue';
|
||||
import { computed, inject } from 'vue';
|
||||
import { useMacros } from 'dashboard/composables/useMacros';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import ActionInput from 'dashboard/components/widgets/AutomationActionInput.vue';
|
||||
|
||||
@@ -163,11 +163,11 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
::v-deep input[type='text'] {
|
||||
:deep(input[type='text']) {
|
||||
@apply mb-0;
|
||||
}
|
||||
|
||||
::v-deep .error {
|
||||
:deep(.error) {
|
||||
.message {
|
||||
@apply mb-0;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, ref, watch, defineModel } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useToggle } from '@vueuse/core';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import addMonths from 'date-fns/addMonths';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { onMounted, computed, defineExpose, defineProps } from 'vue';
|
||||
import { onMounted, computed } from 'vue';
|
||||
import { useStore } from 'dashboard/composables/store';
|
||||
import { useMapGetter } from 'dashboard/composables/store.js';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, defineProps, defineEmits } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
selectedRating: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { computed, defineEmits } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import { OnClickOutside } from '@vueuse/components';
|
||||
import { useToggle } from '@vueuse/core';
|
||||
|
||||
|
||||
@@ -26,11 +26,9 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dropdown-menu__item {
|
||||
::v-deep {
|
||||
a,
|
||||
.button {
|
||||
@apply inline-flex whitespace-nowrap w-full text-left rtl:text-right;
|
||||
}
|
||||
:deep(a),
|
||||
:deep(.button) {
|
||||
@apply inline-flex whitespace-nowrap w-full text-left rtl:text-right;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { defineProps, defineModel, computed } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import { useToggle } from '@vueuse/core';
|
||||
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import Avatar from 'dashboard/components-next/avatar/Avatar.vue';
|
||||
import { defineProps, computed } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
users: {
|
||||
|
||||
@@ -37,12 +37,12 @@ export default {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.chat-bubble.user::v-deep {
|
||||
p code {
|
||||
.chat-bubble.user {
|
||||
:deep(p code) {
|
||||
@apply bg-n-alpha-2 dark:bg-n-alpha-1 text-white;
|
||||
}
|
||||
|
||||
pre {
|
||||
:deep(pre) {
|
||||
@apply text-white bg-n-alpha-2 dark:bg-n-alpha-1;
|
||||
|
||||
code {
|
||||
@@ -50,7 +50,7 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
:deep(blockquote) {
|
||||
@apply bg-transparent border-n-slate-7 ltr:border-l-2 rtl:border-r-2 border-solid;
|
||||
|
||||
p {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { defineProps, defineEmits, computed } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import ArticleListItem from './ArticleListItem.vue';
|
||||
import { useMapGetter } from 'dashboard/composables/store';
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup>
|
||||
import { defineProps, defineEmits } from 'vue';
|
||||
const props = defineProps({
|
||||
link: {
|
||||
type: String,
|
||||
|
||||
Reference in New Issue
Block a user