@adminforth/agent 2.9.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +4 -0
  2. package/dist/application/runTurnUseCase.d.ts +2 -1
  3. package/dist/application/runTurnUseCase.js +3 -3
  4. package/dist/custom/ChatFooter.vue +91 -104
  5. package/dist/custom/ChatHeader.vue +63 -62
  6. package/dist/custom/ChatSurface.vue +11 -18
  7. package/dist/custom/EditActionButtons.vue +12 -8
  8. package/dist/custom/SessionsHistory.vue +90 -28
  9. package/dist/custom/SteerQueue.vue +15 -9
  10. package/dist/custom/composables/agentStore/constants.ts +1 -1
  11. package/dist/custom/composables/agentStore/useAgentChat.ts +4 -3
  12. package/dist/custom/composables/agentStore/useAgentPlaceholder.ts +1 -0
  13. package/dist/custom/composables/agentStore/useAgentSessions.ts +23 -2
  14. package/dist/custom/composables/useAgentStore.ts +8 -29
  15. package/dist/custom/conversation_area/ConversationArea.vue +48 -16
  16. package/dist/custom/conversation_area/MessageRenderer.vue +18 -13
  17. package/dist/custom/conversation_area/ProcessingTimeline.vue +83 -123
  18. package/dist/custom/conversation_area/ReasoningRenderer.vue +13 -16
  19. package/dist/custom/conversation_area/SystemMessageRenderer.vue +4 -4
  20. package/dist/custom/conversation_area/TextRenderer.vue +67 -28
  21. package/dist/custom/conversation_area/ToolApprovalRenderer.vue +89 -56
  22. package/dist/custom/conversation_area/ToolRenderer.vue +44 -82
  23. package/dist/custom/incremark_code_renderers/IncremarkShikiCodeBlock.vue +22 -61
  24. package/dist/custom/speech_recognition_frontend/AudioLines.vue +1 -2
  25. package/dist/custom/speech_recognition_frontend/MicrophoneButon.vue +36 -32
  26. package/dist/custom/types.ts +16 -8
  27. package/dist/domain/agentEvents.d.ts +3 -2
  28. package/dist/tools/approvalFormatter.d.ts +19 -5
  29. package/dist/tools/approvalFormatter.js +88 -40
  30. package/dist/transport/http/context.d.ts +2 -1
  31. package/dist/transport/http/sessionEndpoints.js +34 -0
  32. package/package.json +3 -3
  33. package/dist/custom/SteerDebugPanel.vue +0 -45
  34. package/dist/custom/conversation_area/ToolsGroup.vue +0 -59
@@ -1,58 +1,69 @@
1
1
  <template>
2
- <template v-if="ToolOrReasoningParts.length > 0 || inProgress">
3
- <div
4
- class="shine-text-container ml-2 px-4 flex items-center gap-1 select-none hover:opacity-80 tracking-wide font-medium text-sm text-listTableHeadingText dark:text-darkListTableHeadingText"
5
- :class="[ToolOrReasoningParts.length > 0 ? 'cursor-pointer' : '']"
2
+ <template v-if="timelineParts.length > 0 || inProgress">
3
+ <button
4
+ type="button"
5
+ class="z-20 self-start flex items-center gap-1 mx-4 mb-2 select-none text-[13px] font-medium transition-colors duration-150
6
+ text-lightInputIconColor dark:text-darkInputIconColor"
7
+ :class="timelineParts.length > 0 ? 'hover:text-lightInputText dark:hover:text-darkInputText' : 'cursor-default'"
6
8
  @click="isExpanded = !isExpanded"
7
9
  >
8
- <p
9
- :class="[
10
- inProgress ? `
11
- shine-text text-listTableHeadingText dark:text-darkListTableHeadingText
12
- bg-[linear-gradient(-65deg,currentColor,_rgb(120,120,120)_50%,currentColor)]
13
- dark:bg-[linear-gradient(-65deg,currentColor,_rgb(250,250,250,0.8)_70%,currentColor)]
14
- `
15
- : '']"
16
- >
17
- {{ inProgress ? $t('Thinking') : $t('Thoughts') }}
18
- </p>
19
- <ThreeDotsAnimation v-if="inProgress" />
20
- <span v-if="thinkingDuration > 0">({{ (thinkingDuration/1000).toFixed(2) }} s)</span>
21
- <IconAngleDownOutline
22
- v-if="ToolOrReasoningParts.length > 0"
23
- :class="isExpanded ? 'rotate-180' : 'rotate-0'"
24
- class="transition-transform duration-200"
10
+ <IconChevronRight
11
+ v-if="timelineParts.length > 0"
12
+ class="w-3.5 h-3.5 transition-transform duration-200"
13
+ :class="isExpanded ? 'rotate-90' : 'rotate-0'"
25
14
  />
26
- </div>
27
- <transition name="expand" class="sm:max-h-96 max-h-48 overflow-y-auto mb-4 pt-1">
28
- <CustomAutoScrollContainer
29
- ref="scrollContainerRef"
30
- :enabled="isResponseInProgress"
31
- behavior="smooth"
32
- v-if="ToolOrReasoningParts.length > 0"
15
+ <template v-if="inProgress">
16
+ <span
17
+ class="shine-text
18
+ bg-[linear-gradient(-65deg,currentColor,theme(colors.lightInputPlaceholderText)_50%,currentColor)]
19
+ dark:bg-[linear-gradient(-65deg,currentColor,theme(colors.darkInputText)_70%,currentColor)]"
20
+ >
21
+ {{ $t('Thinking') }}
22
+ </span>
23
+ <ThreeDotsAnimation />
24
+ </template>
25
+ <template v-else>
26
+ <span>{{ thinkingSeconds ? $t('Thought for {seconds}s', { seconds: thinkingSeconds }) : $t('Thoughts') }}</span>
27
+ <span v-if="toolCallsCount">
28
+ · {{ toolCallsCount === 1 ? $t('1 tool') : $t('{count} tools', { count: toolCallsCount }) }}
29
+ </span>
30
+ </template>
31
+ </button>
32
+ <transition name="expand">
33
+ <!-- the box is inset from the message edge, so its own scrollbar never sits on top of the conversation scrollbar -->
34
+ <div
35
+ v-if="timelineParts.length > 0"
33
36
  v-show="isExpanded"
34
- :threshold="5"
35
- :wrapperStyle="{
36
- marginRight: '1rem',
37
- }"
38
- :scrollBarAutoHide="false"
37
+ class="z-20 mx-4 mb-3 overflow-hidden rounded-lg border
38
+ border-lightCardBorder bg-lightCardBackground dark:border-darkCardBorder dark:bg-darkCardBackground"
39
39
  >
40
- <ol class="ml-8 my-2 relative border-l border-l-2 border-black border-default border-listTableHeadingText dark:border-darkListTableHeadingText">
41
- <template v-for="(part, index) in ToolOrReasoningParts" :key="index">
42
- <ReasoningRenderer v-if="part.type === 'reasoning'" :state="part.state" :text="part.text" />
43
- <ToolsGroup v-else-if="part.type==='data-tool-call'" :toolGroup="groupToolCallParts(message, part)" :isExpanded="isExpanded" />
44
- <li v-else-if="part.type === 'data-rendering'" class="mb-6 mx-2 mt-2 px-2 z-50 overflow-hidden">
45
- <span class="bg-lightNavbar dark:bg-darkNavbar absolute flex items-center text-listTableHeadingText dark:text-darkListTableHeadingText justify-center w-5 h-5 rounded-full -start-[0.68rem] ring-4 ring-lightNavbar dark:ring-darkNavbar">
46
- <div class="w-2 h-2 rounded-full bg-current animate-pulse"></div>
47
- </span>
48
- <h3 class="flex items-center mb-1 text-sm ml-3 gap-1 text-listTableHeadingText dark:text-darkListTableHeadingText">
49
- <span class="font-semibold">{{ part.data?.label ?? 'Rendering...' }}</span>
40
+ <CustomAutoScrollContainer
41
+ ref="scrollContainerRef"
42
+ class="max-h-48 sm:max-h-96 overflow-y-auto"
43
+ :enabled="isResponseInProgress"
44
+ behavior="smooth"
45
+ :threshold="5"
46
+ :scrollBarAutoHide="false"
47
+ >
48
+ <!-- wider right padding keeps the row chevrons clear of the box's scrollbar -->
49
+ <ul class="flex flex-col gap-1 pl-2.5 pr-5 py-2">
50
+ <template v-for="(part, index) in timelineParts" :key="index">
51
+ <ReasoningRenderer v-if="part.type === 'reasoning'" :state="part.state" :text="part.text" />
52
+ <ToolRenderer v-else-if="part.type === 'data-tool-call'" :data="formatToolCallPart(part)" />
53
+ <li
54
+ v-else-if="part.type === 'data-rendering'"
55
+ class="flex items-center gap-2 py-0.5 text-[0.8125rem] text-lightTableText dark:text-darkTableText"
56
+ >
57
+ <span class="flex items-center justify-center w-4 h-4 shrink-0">
58
+ <span class="w-2 h-2 rounded-full bg-current animate-pulse" />
59
+ </span>
60
+ {{ part.data?.label ?? 'Rendering...' }}
50
61
  <ThreeDotsAnimation />
51
- </h3>
52
- </li>
53
- </template>
54
- </ol>
55
- </CustomAutoScrollContainer>
62
+ </li>
63
+ </template>
64
+ </ul>
65
+ </CustomAutoScrollContainer>
66
+ </div>
56
67
  </transition>
57
68
  </template>
58
69
  </template>
@@ -60,14 +71,13 @@
60
71
 
61
72
 
62
73
  <script setup lang="ts">
63
- import type { IFormattedToolCallPart, IMessage, IPart, IToolGroup } from '../types';
74
+ import type { IFormattedToolCallPart, IMessage, IPart } from '../types';
64
75
  import { ref, computed, watch, onUnmounted, onMounted, defineAsyncComponent } from 'vue';
76
+ import { IconChevronRight } from '@iconify-prerendered/vue-lucide';
65
77
  import ReasoningRenderer from './ReasoningRenderer.vue';
66
- import { IconAngleDownOutline } from '@iconify-prerendered/vue-flowbite';
78
+ import ToolRenderer from './ToolRenderer.vue';
67
79
  import ThreeDotsAnimation from './ThreeDotsAnimation.vue';
68
80
  import { useAgentStore } from '../composables/useAgentStore';
69
- import { getMessageParts } from '../utils';
70
- import ToolsGroup from './ToolsGroup.vue';
71
81
 
72
82
  const CustomAutoScrollContainer = defineAsyncComponent(() => import('../CustomAutoScrollContainer.vue'));
73
83
 
@@ -83,17 +93,23 @@
83
93
  const innerScrollContainerRef = ref<HTMLElement | null>(null);
84
94
  const isExpanded = ref(true);
85
95
  let isUserScrolled = false;
86
- const ToolOrReasoningParts = computed(() => {
96
+ // One step per reasoning block and per tool call (its 'start' part; the matching 'end'
97
+ // part is merged in by formatToolCallPart), plus a rendering step while it is running.
98
+ const timelineParts = computed(() => {
87
99
  return props.message.parts.filter((part: IPart) => {
88
- return part.type === 'data-tool-call'
89
- || part.type === 'reasoning'
100
+ return part.type === 'reasoning'
101
+ || (part.type === 'data-tool-call' && part.data?.phase === 'start')
90
102
  || isActiveRenderingPart(part);
91
103
  });
92
104
  });
105
+ const toolCallsCount = computed(() => timelineParts.value.filter(part => part.type === 'data-tool-call').length);
106
+ const thinkingSeconds = computed(() => (
107
+ thinkingDuration.value > 0 ? Math.max(1, Math.round(thinkingDuration.value / 1000)) : 0
108
+ ));
93
109
  const isResponseInProgress = computed(() =>{
94
- return props.isLastMessageInChat && agentStore.isResponseInProgress;
110
+ return props.isLastMessageInChat && agentStore.isResponseInProgress;
95
111
  });
96
-
112
+
97
113
  const showFakeThinkingMessage = computed(() => {
98
114
  if (props.message.parts.length === 0 && props.isLastMessageInChat) return true;
99
115
  return false;
@@ -141,37 +157,28 @@
141
157
  }
142
158
  });
143
159
 
144
- const formatToolCallPart = (part: IPart, currentMessage: IMessage): IFormattedToolCallPart | null => {
145
- if (part.type !== 'data-tool-call' || part.data?.phase !== 'start') {
146
- return null;
147
- }
148
-
149
- const finishedPart = currentMessage.parts.find(candidate => {
160
+ function formatToolCallPart(part: IPart): IFormattedToolCallPart {
161
+ const data = part.data!;
162
+ const finishedPart = props.message.parts.find(candidate => {
150
163
  return candidate.type === 'data-tool-call'
151
- && candidate.data?.toolCallId === part.data?.toolCallId
164
+ && candidate.data?.toolCallId === data.toolCallId
152
165
  && candidate.data?.phase === 'end';
153
166
  });
154
167
 
155
168
  return {
156
169
  type: 'data-tool-call',
157
170
  toolInfo: {
158
- toolCallId: part.data.toolCallId,
159
- toolName: part.data.toolName,
171
+ toolCallId: data.toolCallId,
172
+ toolName: data.toolName,
160
173
  phase: finishedPart ? 'end' : 'start',
161
174
  durationMs: finishedPart?.data?.durationMs,
162
- input: part.data.input,
175
+ input: data.input,
163
176
  output: finishedPart?.data?.output,
164
- toolInfo: part.data.toolInfo,
177
+ toolInfo: data.toolInfo,
165
178
  error: finishedPart?.data?.error,
166
179
  }
167
180
  };
168
- };
169
-
170
- const getVisibleTimelineParts = (message: IMessage) => {
171
- return getMessageParts(message).filter(part => {
172
- return part.type === 'reasoning' || (part.type === 'data-tool-call' && part.data?.phase === 'start');
173
- });
174
- };
181
+ }
175
182
 
176
183
  function isActiveRenderingPart(part: IPart) {
177
184
  return part.type === 'data-rendering'
@@ -181,55 +188,8 @@
181
188
  });
182
189
  }
183
190
 
184
- const groupToolCallParts = (message: IMessage, currentPart: IPart): IToolGroup[] => {
185
- if (currentPart.type !== 'data-tool-call') {
186
- return [];
187
- }
188
-
189
- const visibleParts = getVisibleTimelineParts(message);
190
- const currentPartIndex = visibleParts.findIndex(part => part === currentPart);
191
-
192
- if (currentPartIndex === -1) {
193
- return [];
194
- }
195
-
196
- if (currentPartIndex > 0 && visibleParts[currentPartIndex - 1]?.type === 'data-tool-call') {
197
- return [];
198
- }
199
-
200
- const groupedParts: IToolGroup[] = [];
201
-
202
- for (let index = currentPartIndex; index < visibleParts.length; index += 1) {
203
- const part = visibleParts[index];
204
-
205
- if (part.type === 'reasoning') {
206
- break;
207
- }
208
-
209
- const formattedPart = formatToolCallPart(part, message);
210
-
211
- if (!formattedPart) {
212
- continue;
213
- }
214
-
215
- const lastGroup = groupedParts[groupedParts.length - 1];
216
-
217
- if (lastGroup?.title === formattedPart.toolInfo.toolName) {
218
- lastGroup.groupedTools.push(formattedPart);
219
- continue;
220
- }
221
-
222
- groupedParts.push({
223
- title: formattedPart.toolInfo.toolName,
224
- groupedTools: [formattedPart],
225
- });
226
- }
227
-
228
- return groupedParts;
229
- };
230
-
231
191
  function handleScroll() {
232
- if (isUserScrolled) {
192
+ if (isUserScrolled) {
233
193
  scrollContainerRef.value?.handleScroll();
234
194
  }
235
195
  }
@@ -1,36 +1,33 @@
1
1
  <template>
2
- <li class="mb-6 mx-2 mt-2 px-2 z-50 overflow-hidden">
3
- <span class="bg-lightNavbar dark:bg-darkNavbar absolute flex items-center text-listTableHeadingText dark:text-darkListTableHeadingText justify-center w-5 h-5 bg-brand-softer rounded-full -start-[0.68rem] ring-4 ring-lightNavbar dark:ring-darkNavbar ring-default">
4
- <div class="w-5 h-5 rounded-full flex items-center justify-center">
5
- <IconBrainOutline class="w-4 h-4" />
6
- </div>
7
- </span>
8
- <h3
9
- class=" flex items-center mb-1 text-sm ml-3 gap-1 cursor-pointer select-none hover:opacity-80 text-listTableHeadingText dark:text-darkListTableHeadingText"
2
+ <li class="overflow-hidden">
3
+ <button
4
+ type="button"
5
+ class="flex items-center gap-2 w-full py-0.5 text-left text-[0.8125rem] leading-5 select-none"
10
6
  @click="isExpanded = !isExpanded"
11
7
  >
12
- <span class="font-semibold">{{ reasoningTitle }}</span>
8
+ <IconBrain class="w-4 h-4 shrink-0 text-lightInputIconColor dark:text-darkInputIconColor" />
9
+ <span class="min-w-0 truncate font-medium text-lightTableText dark:text-darkTableText">{{ reasoningTitle || $t('Reasoning') }}</span>
13
10
  <ThreeDotsAnimation v-if="isStreaming"/>
14
- <IconAngleDownOutline
11
+ <IconChevronDown
15
12
  :class="isExpanded ? 'rotate-180' : 'rotate-0'"
16
- class="transition-transform duration-200"
13
+ class="ml-auto w-3.5 h-3.5 shrink-0 text-lightInputIconColor transition-transform duration-200 dark:text-darkInputIconColor"
17
14
  />
18
- </h3>
15
+ </button>
19
16
  <transition name="expand">
20
17
  <IncremarkContent
21
- v-if="isExpanded"
18
+ v-if="isExpanded"
22
19
  v-show="isExpanded"
23
- class="pl-4 text-sm"
20
+ class="ml-6 mb-1 text-sm text-lightInputIconColor dark:text-darkInputIconColor"
24
21
  :content="reasoningText"
25
22
  />
26
- </transition>
23
+ </transition>
27
24
  </li>
28
25
  </template>
29
26
 
30
27
 
31
28
 
32
29
  <script setup lang="ts">
33
- import { IconBrainOutline, IconAngleDownOutline } from '@iconify-prerendered/vue-flowbite';
30
+ import { IconBrain, IconChevronDown } from '@iconify-prerendered/vue-lucide';
34
31
  import type { IPart } from '../types';
35
32
  import { ref, computed, watch, defineAsyncComponent } from 'vue';
36
33
  import ThreeDotsAnimation from './ThreeDotsAnimation.vue';
@@ -1,10 +1,10 @@
1
1
  <template>
2
2
  <p :class="`${TEXT_CLASSES}`" v-if="message === RESERVED_SYSTEM_MESSAGE_CONTENT.START_AUDIO_CHAT">
3
- <IconPhoneSolid class="inline-block w-4 h-4 mr-1" />
3
+ <IconPhone class="inline-block w-4 h-4 mr-1" />
4
4
  {{$t('Audio chat started')}}
5
5
  </p>
6
6
  <p :class="`${TEXT_CLASSES}`" v-else-if="message === RESERVED_SYSTEM_MESSAGE_CONTENT.END_AUDIO_CHAT">
7
- <IconPhoneHangupSolid class="inline-block w-4 h-4 mr-1" />
7
+ <IconPhoneOff class="inline-block w-4 h-4 mr-1" />
8
8
  {{$t('Audio chat ended')}}
9
9
  </p>
10
10
  <p :class="`${TEXT_CLASSES}`" v-else-if="message === RESERVED_SYSTEM_MESSAGE_CONTENT.AGENT_RESPONSE_ABORTED">
@@ -17,9 +17,9 @@
17
17
 
18
18
  <script setup lang="ts">
19
19
  import { RESERVED_SYSTEM_MESSAGE_CONTENT } from '../composables/agentStore/constants';
20
- import { IconPhoneSolid, IconPhoneHangupSolid } from '@iconify-prerendered/vue-flowbite';
20
+ import { IconPhone, IconPhoneOff } from '@iconify-prerendered/vue-lucide';
21
21
 
22
- const TEXT_CLASSES = 'text-center italic text-lightListTableHeadingText dark:text-darkListTableHeadingText flex items-center justify-center';
22
+ const TEXT_CLASSES = 'flex items-center justify-center text-center text-sm italic text-lightInputIconColor dark:text-darkInputIconColor';
23
23
 
24
24
  const props = defineProps<{
25
25
  message: string
@@ -1,12 +1,14 @@
1
1
  <template>
2
2
  <div
3
- class="flex items-center justify-center rounded-xl border px-6 border-gray-200 dark:border-gray-700 min-w-0 transition-shadow duration-150 z-20"
3
+ class="flex items-center justify-center min-w-0 text-sm transition-shadow duration-150 z-20"
4
4
  @click="handleMarkdownLinkClick"
5
5
  :class="[
6
- hasVegaLite ? 'w-full my-2' : 'my-2',
7
- props.role === 'user' ? 'bg-lightListTableHeading dark:bg-darkListTableHeading self-end max-w-[80%] mr-4'
8
- : 'border-none self-start max-w-full',
9
- props.editMode ? 'ring-2 ring-lightPrimary dark:ring-darkPrimary w-full max-w-[calc(100%-2rem)] flex-col items-stretch py-3' : ''
6
+ hasVegaLite && 'w-full',
7
+ props.role === 'user'
8
+ ? 'self-end max-w-[82%] mr-4 px-3 py-2 rounded-xl rounded-br [&_.incremark-paragraph]:leading-5 bg-lightSecondaryDarken dark:bg-darkSecondaryLighten'
9
+ : 'self-start max-w-full px-4',
10
+ props.editMode ? `w-full max-w-[calc(100%-2rem)] flex-col items-stretch py-3 border bg-lightInputBackground border-lightInputFocusBorder ring-[3px] ring-lightInputFocusRing
11
+ dark:bg-darkInputBackground dark:border-darkInputFocusBorder dark:ring-darkInputFocusRing` : ''
10
12
  ]"
11
13
  >
12
14
  <IncremarkContent
@@ -18,13 +20,10 @@
18
20
  :components="incremarkComponents"
19
21
  :incremark-options="incremarkOptions"
20
22
  />
21
- <!-- <p v-else class="text-red-500 py-2">
22
- {{ $t('No content to render') }}
23
- </p> -->
24
23
  <template v-if="props.editMode">
25
24
  <textarea
26
25
  v-model="agentStore.userMessageInput"
27
- class="w-full min-h-24 resize-y bg-transparent text-sm text-gray-900 dark:text-gray-100 border-none focus:outline-none focus:ring-0 p-0"
26
+ class="w-full min-h-24 resize-y bg-transparent text-sm text-lightInputText dark:text-darkInputText border-none focus:outline-none focus:ring-0 p-0"
28
27
  @keydown.esc.prevent="agentStore.cancelEditMessage"
29
28
  />
30
29
  <EditActionButtons
@@ -154,31 +153,55 @@
154
153
  </script>
155
154
 
156
155
  <style lang="scss">
156
+ // the incremark theme ships its own fonts and palette, point them to the design tokens
157
+ .incremark {
158
+ --incremark-typography-font-family-base: theme('fontFamily.sans');
159
+ --incremark-typography-font-family-mono: theme('fontFamily.mono');
160
+ --incremark-color-text-primary: theme('colors.lightListTableText');
161
+ --incremark-color-text-secondary: theme('colors.lightInputIconColor');
162
+ --incremark-color-text-tertiary: theme('colors.lightInputPlaceholderText');
163
+ --incremark-color-border-default: theme('colors.lightCardBorder');
164
+ --incremark-color-border-strong: theme('colors.lightCardBorder');
165
+ --incremark-color-border-subtle: theme('colors.lightCardBorder');
166
+ --incremark-color-background-base: theme('colors.lightCardBackground');
167
+ --incremark-color-background-elevated: theme('colors.lightCardBackgroundHover');
168
+ --incremark-color-code-inline-background: theme('colors.lightSecondaryDarken');
169
+ --incremark-color-code-inline-text: theme('colors.lightInputText');
170
+ --incremark-color-brand-primary: theme('colors.lightPrimary');
171
+ --incremark-color-interactive-link: theme('colors.lightPrimary');
172
+ --incremark-color-interactive-link-hover: theme('colors.lightPrimary');
173
+ --incremark-color-interactive-link-visited: theme('colors.lightPrimary');
174
+ }
175
+
176
+ .dark .incremark {
177
+ --incremark-color-text-primary: theme('colors.darkListTableText');
178
+ --incremark-color-text-secondary: theme('colors.darkInputIconColor');
179
+ --incremark-color-text-tertiary: theme('colors.darkInputPlaceholderText');
180
+ --incremark-color-border-default: theme('colors.darkCardBorder');
181
+ --incremark-color-border-strong: theme('colors.darkCardBorder');
182
+ --incremark-color-border-subtle: theme('colors.darkCardBorder');
183
+ --incremark-color-background-base: theme('colors.darkCardBackground');
184
+ --incremark-color-background-elevated: theme('colors.darkCardBackgroundHover');
185
+ --incremark-color-code-inline-background: theme('colors.darkSecondaryLighten');
186
+ --incremark-color-code-inline-text: theme('colors.darkInputText');
187
+ --incremark-color-brand-primary: theme('colors.darkPrimary');
188
+ --incremark-color-interactive-link: theme('colors.darkPrimary');
189
+ --incremark-color-interactive-link-hover: theme('colors.darkPrimary');
190
+ --incremark-color-interactive-link-visited: theme('colors.darkPrimary');
191
+ }
192
+
157
193
  .incremark a.incremark-link,
158
194
  .incremark a.incremark-link:visited {
195
+ @apply text-lightPrimary dark:text-darkPrimary;
159
196
  display: inline-block;
160
197
  text-decoration: underline;
161
198
  text-underline-offset: 4px;
162
- text-decoration-style:dotted;
163
- color: rgb(0, 0, 0);
164
- transition: color 0.2s ease;
199
+ text-decoration-style: dotted;
165
200
  word-break: break-all;
166
201
  }
167
202
 
168
203
  .incremark a.incremark-link:hover {
169
- color: rgb(74, 74, 255);
170
- text-decoration: underline;
171
- text-underline-offset: 4px;
172
- text-decoration-style:dotted;
173
- }
174
-
175
- html[data-theme="dark"] .incremark a.incremark-link,
176
- html[data-theme="dark"] .incremark a.incremark-link:visited {
177
- color: rgb(220, 220, 220);
178
- }
179
-
180
- html[data-theme="dark"] .incremark a.incremark-link:hover {
181
- color: rgb(147, 147, 255);
204
+ text-decoration-style: solid;
182
205
  }
183
206
 
184
207
  a.incremark-link::after {
@@ -197,11 +220,27 @@ a.incremark-link::after {
197
220
  word-break: break-word;
198
221
  }
199
222
 
200
- .incremark-list {
223
+ // scoped under .incremark: the incremark theme is imported lazily and would win an equal selector
224
+ .incremark .incremark-list {
201
225
  list-style: disc;
226
+ margin: 0 0 8px;
227
+ padding-left: 18px;
228
+ }
229
+
230
+ .incremark .incremark-list-item {
231
+ margin: 2px 0;
232
+ }
233
+
234
+ .incremark .incremark-paragraph {
235
+ margin: 0 0 8px;
236
+ }
237
+
238
+ .incremark .incremark-list-item > .incremark-paragraph {
239
+ margin: 0;
202
240
  }
203
241
 
204
- .incremark-paragraph {
205
- margin: 8px 0;
242
+ // the gap between messages comes from the conversation, not from the last block of a message
243
+ .incremark > .incremark-block:last-child > :last-child {
244
+ margin-bottom: 0;
206
245
  }
207
246
  </style>