Copy the Chatwoot (v4.14.0) frontend runnable subset into frontend/ for customization: - app/javascript/ (Vue SPA: dashboard, widget, sdk, portal, superadmin) - app/views/ (ERB templates for vite-plugin-ruby entrypoint resolution) - app/helpers/, app/assets/ (Rails view helpers, static assets) - enterprise/ (Enterprise edition frontend overlay) - config/vite.json, vite.config.ts, bin/vite (Vite-Rails toolchain) - package.json, pnpm-lock.yaml, tailwind/postcss/eslint configs - Gemfile, Gemfile.lock (vite_rails gem for bin/vite binstub) Excluded Rails backend: controllers, models, services, jobs, mailers, policies, db, lib, spec, public, node_modules. Update references to the new frontend location: - .gitignore: exclude frontend build artifacts (node_modules, tmp, packs), keep frontend/bin/ and frontend/vendor/ via negation - backend/scripts/parity_frontend_smoke.sh: CHATWOOT_DIR default reference/chatwoot -> ../frontend - backend/scripts/parity_frontend_browser_smoke.mjs: same default update - AGENTS.md: add frontend section with Rails/Vite coupling notes - README: architecture tree includes frontend/
95 lines
2.2 KiB
Vue
95 lines
2.2 KiB
Vue
<script setup>
|
|
import { ref } from 'vue';
|
|
import CopilotEditor from 'dashboard/components/widgets/WootWriter/CopilotEditor.vue';
|
|
import CaptainLoader from 'dashboard/components/widgets/conversation/copilot/CaptainLoader.vue';
|
|
|
|
defineProps({
|
|
showCopilotEditor: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
isGeneratingContent: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
generatedContent: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits([
|
|
'focus',
|
|
'blur',
|
|
'clearSelection',
|
|
'contentReady',
|
|
'send',
|
|
]);
|
|
|
|
const copilotEditorContent = ref('');
|
|
|
|
const onFocus = () => {
|
|
emit('focus');
|
|
};
|
|
|
|
const onBlur = () => {
|
|
emit('blur');
|
|
};
|
|
|
|
const clearEditorSelection = () => {
|
|
emit('clearSelection');
|
|
};
|
|
|
|
const onSend = () => {
|
|
emit('send', copilotEditorContent.value);
|
|
copilotEditorContent.value = '';
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<Transition
|
|
mode="out-in"
|
|
enter-active-class="transition-all duration-300 ease-out"
|
|
enter-from-class="opacity-0 translate-y-2 scale-[0.98]"
|
|
enter-to-class="opacity-100 translate-y-0 scale-100"
|
|
leave-active-class="transition-all duration-200 ease-in"
|
|
leave-from-class="opacity-100 translate-y-0 scale-100"
|
|
leave-to-class="opacity-0 translate-y-2 scale-[0.98]"
|
|
@after-enter="emit('contentReady')"
|
|
>
|
|
<CopilotEditor
|
|
v-if="showCopilotEditor && !isGeneratingContent"
|
|
key="copilot-editor"
|
|
v-model="copilotEditorContent"
|
|
class="copilot-editor"
|
|
:generated-content="generatedContent"
|
|
:min-height="4"
|
|
:enabled-menu-options="[]"
|
|
@focus="onFocus"
|
|
@blur="onBlur"
|
|
@clear-selection="clearEditorSelection"
|
|
@send="onSend"
|
|
/>
|
|
<div
|
|
v-else-if="isGeneratingContent"
|
|
key="loading-state"
|
|
class="bg-n-iris-5 rounded min-h-[4.75rem] w-full mb-4 p-4 flex items-start"
|
|
>
|
|
<div class="flex items-center gap-2">
|
|
<CaptainLoader class="text-n-iris-10 size-4" />
|
|
<span class="text-sm text-n-iris-10">
|
|
{{ $t('CONVERSATION.REPLYBOX.COPILOT_THINKING') }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</Transition>
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
.copilot-editor {
|
|
.ProseMirror-menubar {
|
|
display: none;
|
|
}
|
|
}
|
|
</style>
|