@genesislcap/ai-assistant 14.482.1 → 14.483.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.
@@ -1,5 +1,4 @@
1
1
  export * from './main.template';
2
- export * from '../styles';
3
2
  export * from './main';
4
3
  export * from './main.types';
5
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/main/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/main/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"main.styles.d.ts","sourceRoot":"","sources":["../../../src/main/main.styles.ts"],"names":[],"mappings":"AAgpBA,eAAO,MAAM,MAAM,iDAGlB,CAAC"}
1
+ {"version":3,"file":"main.styles.d.ts","sourceRoot":"","sources":["../../../src/main/main.styles.ts"],"names":[],"mappings":"AA4tBA,eAAO,MAAM,MAAM,iDAGlB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"main.template.d.ts","sourceRoot":"","sources":["../../../src/main/main.template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AASH,OAAO,EAAuC,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAuNpD,gBAAgB;AAChB,eAAO,MAAM,6BAA6B,GACxC,oBAAoB,MAAM,KACzB,YAAY,CAAC,qBAAqB,CA+ZpC,CAAC"}
1
+ {"version":3,"file":"main.template.d.ts","sourceRoot":"","sources":["../../../src/main/main.template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AASH,OAAO,EAAuC,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAsOpD,gBAAgB;AAChB,eAAO,MAAM,6BAA6B,GACxC,oBAAoB,MAAM,KACzB,YAAY,CAAC,qBAAqB,CAwapC,CAAC"}
@@ -2,6 +2,13 @@ import { GenesisElement } from '@genesislcap/web-core';
2
2
  import type { SuggestionsState } from '../main/main.types';
3
3
  export declare class ChatSuggestions extends GenesisElement {
4
4
  state: SuggestionsState;
5
+ /**
6
+ * Reflected to a `has-content` attribute so the host can collapse when there is
7
+ * nothing to render. True only while loading or when loaded with at least one
8
+ * suggestion; idle, errored, and empty-loaded states render nothing.
9
+ */
10
+ hasContent: boolean;
11
+ stateChanged(): void;
5
12
  handleSuggestionClick(suggestion: string): void;
6
13
  }
7
14
  //# sourceMappingURL=chat-suggestions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-suggestions.d.ts","sourceRoot":"","sources":["../../../src/suggestions/chat-suggestions.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,cAAc,EAMf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AA+I3D,qBAKa,eAAgB,SAAQ,cAAc;IAEjD,KAAK,EAAE,gBAAgB,CAAsB;IAE7C,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;CAGhD"}
1
+ {"version":3,"file":"chat-suggestions.d.ts","sourceRoot":"","sources":["../../../src/suggestions/chat-suggestions.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,cAAc,EAMf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAuJ3D,qBAKa,eAAgB,SAAQ,cAAc;IAEjD,KAAK,EAAE,gBAAgB,CAAsB;IAE7C;;;;OAIG;IACkD,UAAU,UAAS;IAExE,YAAY,IAAI,IAAI;IAKpB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;CAGhD"}
@@ -1,4 +1,3 @@
1
1
  export * from './main.template';
2
- export * from '../styles';
3
2
  export * from './main';
4
3
  export * from './main.types';
@@ -3,6 +3,13 @@ import { settingsModalStyles } from '../components/settings-modal/settings-modal
3
3
  import { AI_COLOUR_AMBER, AI_COLOUR_CYAN, AI_COLOUR_PINK, AI_COLOUR_VIOLET, } from '../styles/ai-colours';
4
4
  const baseStyles = css `
5
5
  :host {
6
+ /* Shared max width for the conversation column and the composer, so they stay
7
+ aligned. Override on the host to change both at once. NOTE: the @container
8
+ threshold that floats the composer can't read this (container queries can't
9
+ use custom properties), so if you override this below the 1500px default the
10
+ float still triggers at 1500px. */
11
+ --ai-conversation-max-width: 1500px;
12
+
6
13
  display: flex;
7
14
  flex-direction: column;
8
15
  height: 100%;
@@ -81,9 +88,20 @@ const baseStyles = css `
81
88
  min-height: 0;
82
89
  overflow-y: auto;
83
90
  padding: 10px;
91
+ }
92
+
93
+ /* Caps and centres the conversation column inside the (full-width) scroll
94
+ container, so the scrollbar stays at the panel edge while the messages sit
95
+ in a readable-width column. Exposed as part="messages-content" so a host
96
+ can widen it (::part(messages-content) { max-width: none }) or neutralise
97
+ the wrapper entirely (::part(messages-content) { display: contents }). */
98
+ .messages-content {
84
99
  display: flex;
85
100
  flex-direction: column;
86
- gap: 10px;
101
+ gap: calc(var(--design-unit) * 2.5px);
102
+ width: 100%;
103
+ max-width: var(--ai-conversation-max-width);
104
+ margin-inline: auto;
87
105
  }
88
106
 
89
107
  @keyframes slide-in-right {
@@ -230,11 +248,17 @@ const baseStyles = css `
230
248
  }
231
249
 
232
250
  .message {
233
- padding: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 3px);
251
+ padding: calc(var(--design-unit) * 2.5px) calc(var(--design-unit) * 3.5px);
234
252
  max-width: 80%;
235
253
  min-width: 70px;
236
254
  font-family: var(--body-font);
237
255
  font-size: 1.1em;
256
+
257
+ /* Long unbroken strings (URLs, file paths, tokens, base64) wrap instead of
258
+ overflowing the bubble. overflow-wrap: anywhere also lets the bubble shrink
259
+ below such a string's width in the flex row, preventing horizontal overflow. */
260
+ overflow-wrap: anywhere;
261
+ word-break: break-word;
238
262
  }
239
263
 
240
264
  .message.user {
@@ -399,6 +423,58 @@ const baseStyles = css `
399
423
  background-color: var(--neutral-layer-2);
400
424
  }
401
425
 
426
+ /* Mirror the messages column: cap the composer (input row + its buttons, resize
427
+ handle, attachment chips, agent picker panel, suggestions) at the same max width
428
+ and centre it, so it does not span full width while the messages sit in a
429
+ narrower column. These are flex items in the column layout, so margin-inline:auto
430
+ would disable their default stretch — width:100% keeps them filling up to the
431
+ cap, and border-box keeps padding/border from overflowing when full-width.
432
+ Overridable via ::part(input-row) etc. */
433
+ .input-row,
434
+ .composer-resize-handle,
435
+ .attachment-chips,
436
+ .agent-picker-panel,
437
+ chat-suggestions {
438
+ box-sizing: border-box;
439
+ width: 100%;
440
+ max-width: var(--ai-conversation-max-width);
441
+ margin-inline: auto;
442
+ }
443
+
444
+ /* When the assistant is wider than that cap the composer is genuinely constrained,
445
+ so lift the input row — and the panels that sit above it — into matching
446
+ floating, rounded, outlined cards with a gap to the bottom. Below the cap they
447
+ stay full-width and flush (the default border-top). The query resolves against
448
+ the assistant's own inline-size container (.chat-wrapper). The threshold is a
449
+ literal because a container query can't read a custom property — keep it equal
450
+ to the --ai-conversation-max-width default. */
451
+ @container (width > 1500px) {
452
+ .input-row,
453
+ .agent-picker-panel,
454
+ chat-suggestions {
455
+ border: 1px solid var(--neutral-stroke-rest);
456
+ border-radius: 12px;
457
+ }
458
+
459
+ /* chat-suggestions paints its background on an inner element, so clip it to the
460
+ rounded corners — otherwise that square inner background bleeds past the radius
461
+ and the corners read as square with the border hidden behind them. */
462
+ chat-suggestions {
463
+ overflow: hidden;
464
+ }
465
+
466
+ .input-row {
467
+ margin-bottom: 12px;
468
+ }
469
+
470
+ /* Suggestions and the picker stack above the input row — a smaller gap keeps
471
+ the cards distinct without a large void. */
472
+ .agent-picker-panel,
473
+ chat-suggestions {
474
+ margin-bottom: 8px;
475
+ }
476
+ }
477
+
402
478
  /* Right-hand control column, mirroring .input-left-controls — holds the
403
479
  Stop button (shown while a turn runs) stacked above the Send button. The
404
480
  Stop button is an icon (stop → spinner), so the column width is driven by
@@ -111,6 +111,20 @@ const senderLabelFor = (m) => m.category === 'narration' ? 'Working' : senderLab
111
111
  * `m.interaction` first; non-interaction messages never consult this.
112
112
  */
113
113
  const interactionPresentation = (m) => { var _a, _b; return (_b = (_a = m.interaction) === null || _a === void 0 ? void 0 : _a.presentation) !== null && _b !== void 0 ? _b : 'label'; };
114
+ /**
115
+ * Space-separated `part` names for a message bubble, so a host can style bubbles
116
+ * from its own stylesheet via `::part()`. Every bubble gets `message` plus its
117
+ * type (`message-user`, `message-ai`, `message-ai-thinking`, `message-ai-function`,
118
+ * `message-interaction`). Interaction bubbles additionally carry a
119
+ * presentation-specific part (`message-interaction-label` / `-bare` / `-bubble`)
120
+ * so a host can target, say, only the bubble-framed widgets.
121
+ */
122
+ const messageParts = (m) => {
123
+ const type = messageType(m);
124
+ return type === 'interaction'
125
+ ? `message message-interaction message-interaction-${interactionPresentation(m)}`
126
+ : `message message-${type}`;
127
+ };
114
128
  // ─── Sub-agent trace fragments ────────────────────────────────────────────────
115
129
  const subAgentAssistantTemplate = html `
116
130
  <div class="sub-agent-message sub-agent-assistant">${(m) => m.content}</div>
@@ -238,6 +252,7 @@ ${(tc) => { var _a; return (((_a = tc.foldPath) === null || _a === void 0 ? void
238
252
  </div>
239
253
  <div
240
254
  class="message ${(m) => messageType(m)} ${(m) => classNames(['narration', m.category === 'narration'])}"
255
+ part="${(m) => messageParts(m)}"
241
256
  >
242
257
  ${when(
243
258
  // A 'bare' interaction owns its full presentation — suppress the
@@ -385,28 +400,33 @@ ${(tc) => { var _a; return (((_a = tc.foldPath) === null || _a === void 0 ? void
385
400
  </div>
386
401
 
387
402
  <div class="messages" part="messages" ${ref('messagesEl')}>
388
- ${repeat((x) => x.visibleMessages, messageRowTemplate)}
389
- ${repeat((x) => x.activeInteractionRow, messageRowTemplate)}
390
- ${when((x) => x.liveSubAgentTrace.length > 0 && x.showToolCalls, liveSubAgentTraceTemplate)}
391
- ${when((x) => {
403
+ <div class="messages-content" part="messages-content">
404
+ ${repeat((x) => x.visibleMessages, messageRowTemplate)}
405
+ ${repeat((x) => x.activeInteractionRow, messageRowTemplate)}
406
+ ${when((x) => x.liveSubAgentTrace.length > 0 && x.showToolCalls, liveSubAgentTraceTemplate)}
407
+ ${when((x) => {
392
408
  var _a;
393
409
  return x.showLoadingIndicator &&
394
410
  (((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.animations) == null ||
395
411
  LOADING_STYLE_ANIMATIONS.some((s) => x.enabledAnimations.includes(s)));
396
412
  }, html `
397
- <div class="message-row ai" part="thinking">
398
- <div class="avatar">
399
- ${(x) => x.imageSrc
413
+ <div class="message-row ai" part="thinking">
414
+ <div class="avatar">
415
+ ${(x) => x.imageSrc
400
416
  ? html `
401
- <img src="${() => x.imageSrc}" alt="Assistant" class="avatar-img" />
402
- `
417
+ <img src="${() => x.imageSrc}" alt="Assistant" class="avatar-img" />
418
+ `
403
419
  : html `
404
- <span class="avatar-icon" :innerHTML="${() => x.assistantIconSafe}"></span>
405
- `}
420
+ <span
421
+ class="avatar-icon"
422
+ :innerHTML="${() => x.assistantIconSafe}"
423
+ ></span>
424
+ `}
425
+ </div>
426
+ ${(x) => selectThinkingTemplate(x)}
406
427
  </div>
407
- ${(x) => selectThinkingTemplate(x)}
408
- </div>
409
- `)}
428
+ `)}
429
+ </div>
410
430
  </div>
411
431
 
412
432
  ${when((x) => !x.composerHiddenByConfig && (x.attachments.length > 0 || x.attachmentErrors.length > 0), html `
@@ -1,5 +1,5 @@
1
1
  import { __decorate } from "tslib";
2
- import { customElement, GenesisElement, observable, html, css, repeat, when, } from '@genesislcap/web-core';
2
+ import { attr, customElement, GenesisElement, observable, html, css, repeat, when, } from '@genesislcap/web-core';
3
3
  const PILL_ANIMATION_STAGGER_MS = 70;
4
4
  const template = html `
5
5
  ${when((x) => x.state.status === 'loading', html `
@@ -25,14 +25,22 @@ const template = html `
25
25
  `;
26
26
  const styles = css `
27
27
  :host {
28
+ /* Collapsed unless there is something to render (see hasContent), so the empty
29
+ host never occupies space or shows through a consumer's border as a stray
30
+ line. */
31
+ display: none;
32
+ }
33
+
34
+ :host([has-content]) {
28
35
  display: block;
29
36
  }
30
37
 
31
38
  .suggestions-container {
32
39
  display: flex;
33
40
  flex-wrap: wrap;
41
+ align-items: center;
34
42
  gap: 6px;
35
- padding: 10px calc(var(--design-unit) * 3px) 6px;
43
+ padding: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 3px);
36
44
  background-color: var(--neutral-layer-2);
37
45
  animation: suggestions-appear 0.2s ease-out both;
38
46
  }
@@ -128,6 +136,16 @@ let ChatSuggestions = class ChatSuggestions extends GenesisElement {
128
136
  constructor() {
129
137
  super(...arguments);
130
138
  this.state = { status: 'idle' };
139
+ /**
140
+ * Reflected to a `has-content` attribute so the host can collapse when there is
141
+ * nothing to render. True only while loading or when loaded with at least one
142
+ * suggestion; idle, errored, and empty-loaded states render nothing.
143
+ */
144
+ this.hasContent = false;
145
+ }
146
+ stateChanged() {
147
+ const s = this.state;
148
+ this.hasContent = s.status === 'loading' || (s.status === 'loaded' && s.suggestions.length > 0);
131
149
  }
132
150
  handleSuggestionClick(suggestion) {
133
151
  this.$emit('suggestion-clicked', suggestion);
@@ -136,6 +154,9 @@ let ChatSuggestions = class ChatSuggestions extends GenesisElement {
136
154
  __decorate([
137
155
  observable
138
156
  ], ChatSuggestions.prototype, "state", void 0);
157
+ __decorate([
158
+ attr({ attribute: 'has-content', mode: 'boolean' })
159
+ ], ChatSuggestions.prototype, "hasContent", void 0);
139
160
  ChatSuggestions = __decorate([
140
161
  customElement({
141
162
  name: 'chat-suggestions',
@@ -1 +1 @@
1
- {"root":["../src/index.ts","../src/channel/ai-activity-bus.ts","../src/channel/ai-activity-channel.ts","../src/components/flowing-waves-indicator.ts","../src/components/halo-overlay.ts","../src/components/plasma-orb-indicator.ts","../src/components/waves-indicator.ts","../src/components/activity-halo/activity-halo.ts","../src/components/agent-picker/agent-picker.constants.ts","../src/components/agent-picker/agent-picker.styles.ts","../src/components/agent-picker/agent-picker.template.ts","../src/components/agent-picker/agent-picker.ts","../src/components/agent-picker/index.ts","../src/components/ai-driver/ai-driver.ts","../src/components/ai-driver/index.ts","../src/components/chat-bubble/chat-bubble.styles.ts","../src/components/chat-bubble/chat-bubble.template.ts","../src/components/chat-bubble/chat-bubble.ts","../src/components/chat-bubble/index.ts","../src/components/chat-driver/align-event-globals.ts","../src/components/chat-driver/chat-driver.test.ts","../src/components/chat-driver/chat-driver.ts","../src/components/chat-driver/index.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.test.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts","../src/components/chat-interaction-wrapper/index.ts","../src/components/chat-markdown/chat-markdown.ts","../src/components/chat-markdown/index.ts","../src/components/orchestrating-driver/index.ts","../src/components/orchestrating-driver/orchestrating-driver.ts","../src/components/popout-manager/index.ts","../src/components/popout-manager/popout-manager.ts","../src/components/settings-modal/index.ts","../src/components/settings-modal/settings-modal.styles.ts","../src/components/settings-modal/settings-modal.template.ts","../src/config/config.ts","../src/config/define-stateful-agent.test.ts","../src/config/define-stateful-agent.ts","../src/config/fallback-agents.ts","../src/config/index.ts","../src/config/validate-providers.test.ts","../src/config/validate-providers.ts","../src/main/index.ts","../src/main/main.styles.ts","../src/main/main.template.ts","../src/main/main.ts","../src/main/main.types.ts","../src/provider/ai-provider-switcher.ts","../src/provider/assistant-app-settings.ts","../src/state/ai-assistant-slice.ts","../src/state/debug-event-log.test.ts","../src/state/debug-event-log.ts","../src/state/driver-registry.test.ts","../src/state/driver-registry.ts","../src/state/session-store.ts","../src/styles/ai-colours.ts","../src/styles/index.ts","../src/styles/styles.ts","../src/suggestions/chat-suggestions.ts","../src/tags/index.ts","../src/types/ai-chat-widget.ts","../src/utils/animated-panel-toggle.ts","../src/utils/animation-exclusivity.test.ts","../src/utils/animation-exclusivity.ts","../src/utils/collect-session-models.test.ts","../src/utils/collect-session-models.ts","../src/utils/condense-history.test.ts","../src/utils/condense-history.ts","../src/utils/cost-session-history.test.ts","../src/utils/cost-session-history.ts","../src/utils/derive-cost-session-title.test.ts","../src/utils/derive-cost-session-title.ts","../src/utils/flatten-sub-agent-messages.test.ts","../src/utils/flatten-sub-agent-messages.ts","../src/utils/history-transform.ts","../src/utils/index.ts","../src/utils/logger.ts","../src/utils/message-partition.test.ts","../src/utils/message-partition.ts","../src/utils/resolve-cost-history-config.test.ts","../src/utils/resolve-cost-history-config.ts","../src/utils/strip-agent-handlers.test.ts","../src/utils/strip-agent-handlers.ts","../src/utils/sum-costs.test.ts","../src/utils/sum-costs.ts","../src/utils/sum-tokens.test.ts","../src/utils/sum-tokens.ts","../src/utils/tool-fold.ts"],"version":"5.9.2"}
1
+ {"root":["../src/index.ts","../src/channel/ai-activity-bus.ts","../src/channel/ai-activity-channel.ts","../src/components/flowing-waves-indicator.ts","../src/components/halo-overlay.ts","../src/components/plasma-orb-indicator.ts","../src/components/waves-indicator.ts","../src/components/activity-halo/activity-halo.ts","../src/components/agent-picker/agent-picker.constants.ts","../src/components/agent-picker/agent-picker.styles.ts","../src/components/agent-picker/agent-picker.template.ts","../src/components/agent-picker/agent-picker.ts","../src/components/agent-picker/index.ts","../src/components/ai-driver/ai-driver.ts","../src/components/ai-driver/index.ts","../src/components/chat-bubble/chat-bubble.styles.ts","../src/components/chat-bubble/chat-bubble.template.ts","../src/components/chat-bubble/chat-bubble.ts","../src/components/chat-bubble/index.ts","../src/components/chat-driver/align-event-globals.ts","../src/components/chat-driver/chat-driver.test.ts","../src/components/chat-driver/chat-driver.ts","../src/components/chat-driver/index.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.test.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts","../src/components/chat-interaction-wrapper/index.ts","../src/components/chat-markdown/chat-markdown.ts","../src/components/chat-markdown/index.ts","../src/components/orchestrating-driver/index.ts","../src/components/orchestrating-driver/orchestrating-driver.ts","../src/components/popout-manager/index.ts","../src/components/popout-manager/popout-manager.ts","../src/components/settings-modal/index.ts","../src/components/settings-modal/settings-modal.styles.ts","../src/components/settings-modal/settings-modal.template.ts","../src/config/config.ts","../src/config/define-stateful-agent.test.ts","../src/config/define-stateful-agent.ts","../src/config/fallback-agents.ts","../src/config/index.ts","../src/config/validate-providers.test.ts","../src/config/validate-providers.ts","../src/main/index.ts","../src/main/main.styles.ts","../src/main/main.template.ts","../src/main/main.ts","../src/main/main.types.ts","../src/provider/ai-provider-switcher.ts","../src/provider/assistant-app-settings.ts","../src/state/ai-assistant-slice.ts","../src/state/debug-event-log.test.ts","../src/state/debug-event-log.ts","../src/state/driver-registry.test.ts","../src/state/driver-registry.ts","../src/state/session-store.ts","../src/styles/ai-colours.ts","../src/suggestions/chat-suggestions.ts","../src/tags/index.ts","../src/types/ai-chat-widget.ts","../src/utils/animated-panel-toggle.ts","../src/utils/animation-exclusivity.test.ts","../src/utils/animation-exclusivity.ts","../src/utils/collect-session-models.test.ts","../src/utils/collect-session-models.ts","../src/utils/condense-history.test.ts","../src/utils/condense-history.ts","../src/utils/cost-session-history.test.ts","../src/utils/cost-session-history.ts","../src/utils/derive-cost-session-title.test.ts","../src/utils/derive-cost-session-title.ts","../src/utils/flatten-sub-agent-messages.test.ts","../src/utils/flatten-sub-agent-messages.ts","../src/utils/history-transform.ts","../src/utils/index.ts","../src/utils/logger.ts","../src/utils/message-partition.test.ts","../src/utils/message-partition.ts","../src/utils/resolve-cost-history-config.test.ts","../src/utils/resolve-cost-history-config.ts","../src/utils/strip-agent-handlers.test.ts","../src/utils/strip-agent-handlers.ts","../src/utils/sum-costs.test.ts","../src/utils/sum-costs.ts","../src/utils/sum-tokens.test.ts","../src/utils/sum-tokens.ts","../src/utils/tool-fold.ts"],"version":"5.9.2"}
@@ -0,0 +1,132 @@
1
+ # Styling & Theming
2
+
3
+ ## Overview
4
+
5
+ The assistant renders in a shadow root, so its internal classes are not reachable
6
+ from an app stylesheet. Customization is exposed through three deliberate,
7
+ stable surfaces:
8
+
9
+ 1. **Design-system tokens** — the widget reads standard tokens (`--neutral-layer-*`,
10
+ `--accent-fill-rest`, `--design-unit`, `--control-corner-radius`, `--body-font`,
11
+ the `--type-ramp-*` ramp, …). These inherit through the shadow boundary, so
12
+ theming the host design system themes the assistant with no widget-specific work.
13
+ 2. **`--ai-*` custom properties** — a small set of assistant-specific variables,
14
+ each with a built-in fallback, for the parts of the UI that have no natural
15
+ design-system token.
16
+ 3. **CSS Shadow Parts (`::part()`)** — named hooks on internal elements for
17
+ arbitrary restyling from an app stylesheet.
18
+
19
+ Prefer tokens where one exists (they inherit and can be set inline); reach for
20
+ `::part()` when you need to change something a token doesn't cover.
21
+
22
+ ---
23
+
24
+ ## `--ai-*` custom properties
25
+
26
+ | Property | Default | Applies to |
27
+ | --- | --- | --- |
28
+ | `--ai-conversation-max-width` | `1500px` | Max width of the centred conversation column **and** the composer (kept in sync) |
29
+ | `--ai-message-shadow` | `0 2px 8px rgb(0 0 0 / 15%)` | Assistant message bubble shadow |
30
+ | `--ai-thinking-color` | `#7c3aed` | Thinking/reasoning bubble and its avatar ring |
31
+ | `--ai-function-color` | `#86efac` | Tool-call bubble text and border |
32
+
33
+ Set them on the host element (or any ancestor — they inherit):
34
+
35
+ ```css
36
+ foundation-ai-assistant {
37
+ --ai-thinking-color: #2563eb;
38
+ --ai-message-shadow: none;
39
+ }
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Shadow parts
45
+
46
+ Parts on the conversation area (the outer frame exposes many more — e.g.
47
+ `chat-header`, `input-row`, `input`, `send-button`):
48
+
49
+ | Part | Element |
50
+ | --- | --- |
51
+ | `chat-wrapper` | Outer frame (header + messages + composer) |
52
+ | `messages` | Scroll container (full width; owns the scrollbar) |
53
+ | `messages-content` | Width-capped, centred column that holds every message |
54
+ | `message` | Every message bubble |
55
+ | `message-<type>` | A bubble of one kind: `message-user`, `message-ai`, `message-ai-thinking`, `message-ai-function`, `message-interaction` |
56
+ | `message-interaction-<presentation>` | An interaction bubble in one presentation: `message-interaction-label`, `message-interaction-bare`, `message-interaction-bubble` |
57
+ | `thinking` | The loading-indicator row |
58
+
59
+ Target them from an app stylesheet with `::part()`:
60
+
61
+ ```css
62
+ /* Constrain every bubble to a width range */
63
+ foundation-ai-assistant::part(message) {
64
+ min-width: 200px;
65
+ max-width: 640px;
66
+ }
67
+
68
+ /* Restyle just one kind of bubble */
69
+ foundation-ai-assistant::part(message-user) {
70
+ border-radius: 18px;
71
+ }
72
+
73
+ /* Only interaction widgets, and only when framed as a bubble */
74
+ foundation-ai-assistant::part(message-interaction-bubble) {
75
+ min-width: 320px;
76
+ }
77
+
78
+ /* Change the padding inside bubbles */
79
+ foundation-ai-assistant::part(message) {
80
+ padding: 12px 16px;
81
+ }
82
+ ```
83
+
84
+ ### The conversation column (`messages-content`)
85
+
86
+ Messages sit in a column capped at `1000px` and centred inside the full-width
87
+ scroll container, so the scrollbar stays at the panel edge while the text keeps
88
+ a readable width. Adjust or remove the cap via the part:
89
+
90
+ ```css
91
+ /* Widen (or narrow) the column */
92
+ foundation-ai-assistant::part(messages-content) { max-width: 1400px; }
93
+
94
+ /* Remove the cap — messages fill the full width */
95
+ foundation-ai-assistant::part(messages-content) { max-width: none; }
96
+
97
+ /* Neutralise the wrapper entirely (as if the div weren't there) */
98
+ foundation-ai-assistant::part(messages-content) { display: contents; }
99
+ ```
100
+
101
+ ---
102
+
103
+ ## Responsive sizing
104
+
105
+ A `min-width` only ever affects *short* messages — long ones are already wider —
106
+ so it can push a bubble past a narrow container. The assistant can be rendered at
107
+ any width (docked panel, popout, inline), so size overrides against the *element's*
108
+ width, not the viewport.
109
+
110
+ `@media` queries the viewport, which does not track the assistant's width, so it is
111
+ the wrong tool. Use a **container query** instead. `::part()` rules can be wrapped
112
+ in `@container` from the host stylesheet; the reliable pattern is to make your own
113
+ wrapper a container:
114
+
115
+ ```css
116
+ .ai-slot {
117
+ container-type: inline-size;
118
+ }
119
+
120
+ /* A comfortable minimum on wide layouts… */
121
+ foundation-ai-assistant::part(message) {
122
+ min-width: 220px;
123
+ }
124
+
125
+ /* …dropped when the assistant itself is narrow, so bubbles never overflow. */
126
+ @container (max-width: 420px) {
127
+ foundation-ai-assistant::part(message) {
128
+ min-width: 0;
129
+ }
130
+ }
131
+ ```
132
+
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/ai-assistant",
3
3
  "description": "Genesis AI Assistant micro-frontend",
4
- "version": "14.482.1",
4
+ "version": "14.483.0",
5
5
  "license": "SEE LICENSE IN license.txt",
6
6
  "main": "dist/esm/index.js",
7
7
  "types": "dist/ai-assistant.d.ts",
@@ -64,25 +64,25 @@
64
64
  }
65
65
  },
66
66
  "devDependencies": {
67
- "@genesislcap/foundation-testing": "14.482.1",
68
- "@genesislcap/genx": "14.482.1",
69
- "@genesislcap/rollup-builder": "14.482.1",
70
- "@genesislcap/ts-builder": "14.482.1",
71
- "@genesislcap/uvu-playwright-builder": "14.482.1",
72
- "@genesislcap/vite-builder": "14.482.1",
73
- "@genesislcap/webpack-builder": "14.482.1",
67
+ "@genesislcap/foundation-testing": "14.483.0",
68
+ "@genesislcap/genx": "14.483.0",
69
+ "@genesislcap/rollup-builder": "14.483.0",
70
+ "@genesislcap/ts-builder": "14.483.0",
71
+ "@genesislcap/uvu-playwright-builder": "14.483.0",
72
+ "@genesislcap/vite-builder": "14.483.0",
73
+ "@genesislcap/webpack-builder": "14.483.0",
74
74
  "@types/dompurify": "^3.0.5",
75
75
  "@types/marked": "^5.0.2"
76
76
  },
77
77
  "dependencies": {
78
- "@genesislcap/foundation-ai": "14.482.1",
79
- "@genesislcap/foundation-logger": "14.482.1",
80
- "@genesislcap/foundation-notifications": "14.482.1",
81
- "@genesislcap/foundation-redux": "14.482.1",
82
- "@genesislcap/foundation-ui": "14.482.1",
83
- "@genesislcap/foundation-utils": "14.482.1",
84
- "@genesislcap/rapid-design-system": "14.482.1",
85
- "@genesislcap/web-core": "14.482.1",
78
+ "@genesislcap/foundation-ai": "14.483.0",
79
+ "@genesislcap/foundation-logger": "14.483.0",
80
+ "@genesislcap/foundation-notifications": "14.483.0",
81
+ "@genesislcap/foundation-redux": "14.483.0",
82
+ "@genesislcap/foundation-ui": "14.483.0",
83
+ "@genesislcap/foundation-utils": "14.483.0",
84
+ "@genesislcap/rapid-design-system": "14.483.0",
85
+ "@genesislcap/web-core": "14.483.0",
86
86
  "dompurify": "^3.3.1",
87
87
  "marked": "^17.0.3"
88
88
  },
@@ -94,5 +94,5 @@
94
94
  "publishConfig": {
95
95
  "access": "public"
96
96
  },
97
- "gitHead": "43a902838587d527b6786bbb41a5c3784c638e70"
97
+ "gitHead": "28a6b21ae981f0a10a9a49b503aa0a3c99146da9"
98
98
  }
package/src/main/index.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  export * from './main.template';
2
- export * from '../styles';
3
2
  export * from './main';
4
3
  export * from './main.types';
@@ -9,6 +9,13 @@ import {
9
9
 
10
10
  const baseStyles = css`
11
11
  :host {
12
+ /* Shared max width for the conversation column and the composer, so they stay
13
+ aligned. Override on the host to change both at once. NOTE: the @container
14
+ threshold that floats the composer can't read this (container queries can't
15
+ use custom properties), so if you override this below the 1500px default the
16
+ float still triggers at 1500px. */
17
+ --ai-conversation-max-width: 1500px;
18
+
12
19
  display: flex;
13
20
  flex-direction: column;
14
21
  height: 100%;
@@ -87,9 +94,20 @@ const baseStyles = css`
87
94
  min-height: 0;
88
95
  overflow-y: auto;
89
96
  padding: 10px;
97
+ }
98
+
99
+ /* Caps and centres the conversation column inside the (full-width) scroll
100
+ container, so the scrollbar stays at the panel edge while the messages sit
101
+ in a readable-width column. Exposed as part="messages-content" so a host
102
+ can widen it (::part(messages-content) { max-width: none }) or neutralise
103
+ the wrapper entirely (::part(messages-content) { display: contents }). */
104
+ .messages-content {
90
105
  display: flex;
91
106
  flex-direction: column;
92
- gap: 10px;
107
+ gap: calc(var(--design-unit) * 2.5px);
108
+ width: 100%;
109
+ max-width: var(--ai-conversation-max-width);
110
+ margin-inline: auto;
93
111
  }
94
112
 
95
113
  @keyframes slide-in-right {
@@ -236,11 +254,17 @@ const baseStyles = css`
236
254
  }
237
255
 
238
256
  .message {
239
- padding: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 3px);
257
+ padding: calc(var(--design-unit) * 2.5px) calc(var(--design-unit) * 3.5px);
240
258
  max-width: 80%;
241
259
  min-width: 70px;
242
260
  font-family: var(--body-font);
243
261
  font-size: 1.1em;
262
+
263
+ /* Long unbroken strings (URLs, file paths, tokens, base64) wrap instead of
264
+ overflowing the bubble. overflow-wrap: anywhere also lets the bubble shrink
265
+ below such a string's width in the flex row, preventing horizontal overflow. */
266
+ overflow-wrap: anywhere;
267
+ word-break: break-word;
244
268
  }
245
269
 
246
270
  .message.user {
@@ -405,6 +429,58 @@ const baseStyles = css`
405
429
  background-color: var(--neutral-layer-2);
406
430
  }
407
431
 
432
+ /* Mirror the messages column: cap the composer (input row + its buttons, resize
433
+ handle, attachment chips, agent picker panel, suggestions) at the same max width
434
+ and centre it, so it does not span full width while the messages sit in a
435
+ narrower column. These are flex items in the column layout, so margin-inline:auto
436
+ would disable their default stretch — width:100% keeps them filling up to the
437
+ cap, and border-box keeps padding/border from overflowing when full-width.
438
+ Overridable via ::part(input-row) etc. */
439
+ .input-row,
440
+ .composer-resize-handle,
441
+ .attachment-chips,
442
+ .agent-picker-panel,
443
+ chat-suggestions {
444
+ box-sizing: border-box;
445
+ width: 100%;
446
+ max-width: var(--ai-conversation-max-width);
447
+ margin-inline: auto;
448
+ }
449
+
450
+ /* When the assistant is wider than that cap the composer is genuinely constrained,
451
+ so lift the input row — and the panels that sit above it — into matching
452
+ floating, rounded, outlined cards with a gap to the bottom. Below the cap they
453
+ stay full-width and flush (the default border-top). The query resolves against
454
+ the assistant's own inline-size container (.chat-wrapper). The threshold is a
455
+ literal because a container query can't read a custom property — keep it equal
456
+ to the --ai-conversation-max-width default. */
457
+ @container (width > 1500px) {
458
+ .input-row,
459
+ .agent-picker-panel,
460
+ chat-suggestions {
461
+ border: 1px solid var(--neutral-stroke-rest);
462
+ border-radius: 12px;
463
+ }
464
+
465
+ /* chat-suggestions paints its background on an inner element, so clip it to the
466
+ rounded corners — otherwise that square inner background bleeds past the radius
467
+ and the corners read as square with the border hidden behind them. */
468
+ chat-suggestions {
469
+ overflow: hidden;
470
+ }
471
+
472
+ .input-row {
473
+ margin-bottom: 12px;
474
+ }
475
+
476
+ /* Suggestions and the picker stack above the input row — a smaller gap keeps
477
+ the cards distinct without a large void. */
478
+ .agent-picker-panel,
479
+ chat-suggestions {
480
+ margin-bottom: 8px;
481
+ }
482
+ }
483
+
408
484
  /* Right-hand control column, mirroring .input-left-controls — holds the
409
485
  Stop button (shown while a turn runs) stacked above the Send button. The
410
486
  Stop button is an icon (stop → spinner), so the column width is driven by
@@ -130,6 +130,21 @@ const senderLabelFor = (m: ChatMessage): string =>
130
130
  const interactionPresentation = (m: ChatMessage): InteractionPresentation =>
131
131
  m.interaction?.presentation ?? 'label';
132
132
 
133
+ /**
134
+ * Space-separated `part` names for a message bubble, so a host can style bubbles
135
+ * from its own stylesheet via `::part()`. Every bubble gets `message` plus its
136
+ * type (`message-user`, `message-ai`, `message-ai-thinking`, `message-ai-function`,
137
+ * `message-interaction`). Interaction bubbles additionally carry a
138
+ * presentation-specific part (`message-interaction-label` / `-bare` / `-bubble`)
139
+ * so a host can target, say, only the bubble-framed widgets.
140
+ */
141
+ const messageParts = (m: ChatMessage): string => {
142
+ const type = messageType(m);
143
+ return type === 'interaction'
144
+ ? `message message-interaction message-interaction-${interactionPresentation(m)}`
145
+ : `message message-${type}`;
146
+ };
147
+
133
148
  // ─── Sub-agent trace fragments ────────────────────────────────────────────────
134
149
 
135
150
  const subAgentAssistantTemplate = html<ChatMessage>`
@@ -310,6 +325,7 @@ ${(tc) => (tc.foldPath?.length ? `${tc.foldPath.join(' › ')} › ` : '')}<stro
310
325
  <div
311
326
  class="message ${(m) => messageType(m)} ${(m) =>
312
327
  classNames(['narration', m.category === 'narration'])}"
328
+ part="${(m) => messageParts(m)}"
313
329
  >
314
330
  ${when(
315
331
  // A 'bare' interaction owns its full presentation — suppress the
@@ -496,30 +512,38 @@ ${(tc) => (tc.foldPath?.length ? `${tc.foldPath.join(' › ')} › ` : '')}<stro
496
512
  </div>
497
513
 
498
514
  <div class="messages" part="messages" ${ref('messagesEl')}>
499
- ${repeat((x) => x.visibleMessages, messageRowTemplate)}
500
- ${repeat((x) => x.activeInteractionRow, messageRowTemplate)}
501
- ${when((x) => x.liveSubAgentTrace.length > 0 && x.showToolCalls, liveSubAgentTraceTemplate)}
502
- ${when(
503
- (x) =>
504
- x.showLoadingIndicator &&
505
- (x.chatConfig.ui?.animations == null ||
506
- LOADING_STYLE_ANIMATIONS.some((s) => x.enabledAnimations.includes(s))),
507
- html<FoundationAiAssistant>`
508
- <div class="message-row ai" part="thinking">
509
- <div class="avatar">
510
- ${(x) =>
511
- x.imageSrc
512
- ? html<FoundationAiAssistant>`
513
- <img src="${() => x.imageSrc}" alt="Assistant" class="avatar-img" />
514
- `
515
- : html<FoundationAiAssistant>`
516
- <span class="avatar-icon" :innerHTML="${() => x.assistantIconSafe}"></span>
517
- `}
515
+ <div class="messages-content" part="messages-content">
516
+ ${repeat((x) => x.visibleMessages, messageRowTemplate)}
517
+ ${repeat((x) => x.activeInteractionRow, messageRowTemplate)}
518
+ ${when(
519
+ (x) => x.liveSubAgentTrace.length > 0 && x.showToolCalls,
520
+ liveSubAgentTraceTemplate,
521
+ )}
522
+ ${when(
523
+ (x) =>
524
+ x.showLoadingIndicator &&
525
+ (x.chatConfig.ui?.animations == null ||
526
+ LOADING_STYLE_ANIMATIONS.some((s) => x.enabledAnimations.includes(s))),
527
+ html<FoundationAiAssistant>`
528
+ <div class="message-row ai" part="thinking">
529
+ <div class="avatar">
530
+ ${(x) =>
531
+ x.imageSrc
532
+ ? html<FoundationAiAssistant>`
533
+ <img src="${() => x.imageSrc}" alt="Assistant" class="avatar-img" />
534
+ `
535
+ : html<FoundationAiAssistant>`
536
+ <span
537
+ class="avatar-icon"
538
+ :innerHTML="${() => x.assistantIconSafe}"
539
+ ></span>
540
+ `}
541
+ </div>
542
+ ${(x) => selectThinkingTemplate(x)}
518
543
  </div>
519
- ${(x) => selectThinkingTemplate(x)}
520
- </div>
521
- `,
522
- )}
544
+ `,
545
+ )}
546
+ </div>
523
547
  </div>
524
548
 
525
549
  ${when(
@@ -1,4 +1,5 @@
1
1
  import {
2
+ attr,
2
3
  customElement,
3
4
  GenesisElement,
4
5
  observable,
@@ -50,14 +51,22 @@ const template = html<ChatSuggestions>`
50
51
 
51
52
  const styles = css`
52
53
  :host {
54
+ /* Collapsed unless there is something to render (see hasContent), so the empty
55
+ host never occupies space or shows through a consumer's border as a stray
56
+ line. */
57
+ display: none;
58
+ }
59
+
60
+ :host([has-content]) {
53
61
  display: block;
54
62
  }
55
63
 
56
64
  .suggestions-container {
57
65
  display: flex;
58
66
  flex-wrap: wrap;
67
+ align-items: center;
59
68
  gap: 6px;
60
- padding: 10px calc(var(--design-unit) * 3px) 6px;
69
+ padding: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 3px);
61
70
  background-color: var(--neutral-layer-2);
62
71
  animation: suggestions-appear 0.2s ease-out both;
63
72
  }
@@ -159,6 +168,18 @@ export class ChatSuggestions extends GenesisElement {
159
168
  @observable
160
169
  state: SuggestionsState = { status: 'idle' };
161
170
 
171
+ /**
172
+ * Reflected to a `has-content` attribute so the host can collapse when there is
173
+ * nothing to render. True only while loading or when loaded with at least one
174
+ * suggestion; idle, errored, and empty-loaded states render nothing.
175
+ */
176
+ @attr({ attribute: 'has-content', mode: 'boolean' }) hasContent = false;
177
+
178
+ stateChanged(): void {
179
+ const s = this.state;
180
+ this.hasContent = s.status === 'loading' || (s.status === 'loaded' && s.suggestions.length > 0);
181
+ }
182
+
162
183
  handleSuggestionClick(suggestion: string): void {
163
184
  this.$emit('suggestion-clicked', suggestion);
164
185
  }
@@ -1,2 +0,0 @@
1
- export * from './styles';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/styles/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC"}
@@ -1,5 +0,0 @@
1
- /**
2
- * @beta
3
- */
4
- export declare const FoundationAiAssistantStyles: import("@microsoft/fast-element").ElementStyles;
5
- //# sourceMappingURL=styles.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/styles/styles.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,eAAO,MAAM,2BAA2B,iDAwOvC,CAAC"}
@@ -1 +0,0 @@
1
- export * from './styles';
@@ -1,237 +0,0 @@
1
- import { css } from '@genesislcap/web-core';
2
- /**
3
- * @beta
4
- */
5
- export const FoundationAiAssistantStyles = css `
6
- :host {
7
- display: flex;
8
- flex-direction: column;
9
- height: 100%;
10
- box-sizing: border-box;
11
- }
12
-
13
- .chat-wrapper {
14
- display: flex;
15
- flex-direction: column;
16
- height: 100%;
17
- overflow: hidden;
18
- background: var(--neutral-fill-layer-rest, var(--neutral-layer-1));
19
- border-radius: calc(var(--design-unit, 4) * 1px);
20
- border: 1px solid var(--neutral-stroke-rest);
21
- }
22
-
23
- .chat-header {
24
- display: flex;
25
- align-items: center;
26
- gap: calc(var(--design-unit, 4) * 3px);
27
- padding: calc(var(--design-unit, 4) * 1.5px) calc(var(--design-unit, 4) * 3px);
28
- border-bottom: 1px solid var(--neutral-stroke-rest);
29
- background: var(--neutral-fill-stealth-rest);
30
- font-size: var(--type-ramp-minus-1-font-size);
31
- color: var(--neutral-foreground-rest);
32
- }
33
-
34
- .chat-title {
35
- flex: 1;
36
- font-weight: 600;
37
- font-size: var(--type-ramp-base-font-size);
38
- overflow: hidden;
39
- text-overflow: ellipsis;
40
- white-space: nowrap;
41
- }
42
-
43
- .toggle-label {
44
- display: flex;
45
- align-items: center;
46
- gap: calc(var(--design-unit, 4) * 1px);
47
- cursor: pointer;
48
- user-select: none;
49
- }
50
-
51
- .toggle-label input[type='checkbox'] {
52
- cursor: pointer;
53
- accent-color: var(--accent-fill-rest);
54
- }
55
-
56
- .download-button {
57
- margin-left: auto;
58
- }
59
-
60
- .messages {
61
- flex: 1;
62
- overflow-y: auto;
63
- padding: calc(var(--design-unit, 4) * 3px);
64
- display: flex;
65
- flex-direction: column;
66
- gap: calc(var(--design-unit, 4) * 2px);
67
- }
68
-
69
- .message {
70
- padding: calc(var(--design-unit, 4) * 2px) calc(var(--design-unit, 4) * 3px);
71
- border-radius: calc(var(--design-unit, 4) * 2px);
72
- max-width: 80%;
73
- word-break: break-word;
74
- font-size: var(--type-ramp-base-font-size);
75
- line-height: var(--type-ramp-base-line-height);
76
- }
77
-
78
- .message--user {
79
- align-self: flex-end;
80
- background: var(--accent-fill-rest);
81
- color: var(--foreground-on-accent-rest);
82
- border-bottom-right-radius: calc(var(--design-unit, 4) * 0.5px);
83
- }
84
-
85
- .message--assistant {
86
- align-self: flex-start;
87
- background: var(--neutral-fill-rest);
88
- color: var(--neutral-foreground-rest);
89
- border-bottom-left-radius: calc(var(--design-unit, 4) * 0.5px);
90
- }
91
-
92
- .thinking-block {
93
- margin-bottom: calc(var(--design-unit, 4) * 1px);
94
- border: 1px solid var(--neutral-stroke-rest);
95
- border-radius: calc(var(--design-unit, 4) * 1px);
96
- overflow: hidden;
97
- }
98
-
99
- .thinking-summary {
100
- cursor: pointer;
101
- padding: calc(var(--design-unit, 4) * 1px) calc(var(--design-unit, 4) * 2px);
102
- font-size: var(--type-ramp-minus-1-font-size);
103
- color: var(--neutral-foreground-hint);
104
- user-select: none;
105
- list-style: none;
106
- }
107
-
108
- .thinking-summary::marker,
109
- .thinking-summary::-webkit-details-marker {
110
- display: none;
111
- }
112
-
113
- .thinking-summary::before {
114
- content: '▶ ';
115
- font-size: 0.6em;
116
- vertical-align: middle;
117
- }
118
-
119
- details[open] .thinking-summary::before {
120
- content: '▼ ';
121
- }
122
-
123
- .thinking-content {
124
- padding: calc(var(--design-unit, 4) * 2px);
125
- font-size: var(--type-ramp-minus-1-font-size);
126
- color: var(--neutral-foreground-hint);
127
- border-top: 1px solid var(--neutral-stroke-rest);
128
- }
129
-
130
- .tool-calls {
131
- font-size: var(--type-ramp-minus-1-font-size);
132
- color: var(--neutral-foreground-hint);
133
- font-family: monospace;
134
- }
135
-
136
- .message--tool {
137
- align-self: flex-start;
138
- background: var(--neutral-fill-stealth-rest);
139
- color: var(--neutral-foreground-hint);
140
- font-size: var(--type-ramp-minus-1-font-size);
141
- font-family: monospace;
142
- max-width: 100%;
143
- border-radius: calc(var(--design-unit, 4) * 1px);
144
- }
145
-
146
- .empty-state {
147
- flex: 1;
148
- display: flex;
149
- align-items: center;
150
- justify-content: center;
151
- color: var(--neutral-foreground-hint);
152
- font-size: var(--type-ramp-base-font-size);
153
- }
154
-
155
- .thinking {
156
- align-self: flex-start;
157
- padding: calc(var(--design-unit, 4) * 1px);
158
- }
159
-
160
- .file-input {
161
- display: none;
162
- }
163
-
164
- .attachment-chips {
165
- display: flex;
166
- flex-wrap: wrap;
167
- gap: calc(var(--design-unit, 4) * 1px);
168
- padding: calc(var(--design-unit, 4) * 1px) calc(var(--design-unit, 4) * 2px);
169
- border-top: 1px solid var(--neutral-stroke-rest);
170
- }
171
-
172
- .attachment-chip {
173
- display: inline-flex;
174
- align-items: center;
175
- gap: calc(var(--design-unit, 4) * 1px);
176
- padding: calc(var(--design-unit, 4) * 0.5px) calc(var(--design-unit, 4) * 1.5px);
177
- background: var(--neutral-fill-rest);
178
- border: 1px solid var(--neutral-stroke-rest);
179
- border-radius: calc(var(--design-unit, 4) * 4px);
180
- font-size: var(--type-ramp-minus-1-font-size);
181
- color: var(--neutral-foreground-rest);
182
- max-width: 200px;
183
- overflow: hidden;
184
- text-overflow: ellipsis;
185
- white-space: nowrap;
186
- }
187
-
188
- .attachment-chip-error {
189
- background: var(--error-fill-rest, #fde8e8);
190
- border-color: var(--error-stroke-rest, #f87171);
191
- color: var(--error-foreground-rest, #b91c1c);
192
- }
193
-
194
- .chip-remove {
195
- flex-shrink: 0;
196
- background: none;
197
- border: none;
198
- cursor: pointer;
199
- padding: 0;
200
- line-height: 1;
201
- color: inherit;
202
- opacity: 0.6;
203
- font-size: 0.9em;
204
- }
205
-
206
- .chip-remove:hover {
207
- opacity: 1;
208
- }
209
-
210
- .input-row {
211
- display: flex;
212
- gap: calc(var(--design-unit, 4) * 2px);
213
- padding: calc(var(--design-unit, 4) * 2px);
214
- border-top: 1px solid var(--neutral-stroke-rest);
215
- align-items: flex-end;
216
- }
217
-
218
- .attach-button {
219
- flex-shrink: 0;
220
- align-self: flex-end;
221
- }
222
-
223
- .chat-input {
224
- flex: 1;
225
- }
226
-
227
- .chat-input::part(control) {
228
- resize: none;
229
- min-height: calc(var(--base-height-multiplier, 10) * 1px + var(--design-unit, 4) * 4px);
230
- max-height: calc(var(--base-height-multiplier, 10) * 5px);
231
- }
232
-
233
- .send-button {
234
- flex-shrink: 0;
235
- align-self: flex-end;
236
- }
237
- `;
@@ -1 +0,0 @@
1
- export * from './styles';
@@ -1,238 +0,0 @@
1
- import { css } from '@genesislcap/web-core';
2
-
3
- /**
4
- * @beta
5
- */
6
- export const FoundationAiAssistantStyles = css`
7
- :host {
8
- display: flex;
9
- flex-direction: column;
10
- height: 100%;
11
- box-sizing: border-box;
12
- }
13
-
14
- .chat-wrapper {
15
- display: flex;
16
- flex-direction: column;
17
- height: 100%;
18
- overflow: hidden;
19
- background: var(--neutral-fill-layer-rest, var(--neutral-layer-1));
20
- border-radius: calc(var(--design-unit, 4) * 1px);
21
- border: 1px solid var(--neutral-stroke-rest);
22
- }
23
-
24
- .chat-header {
25
- display: flex;
26
- align-items: center;
27
- gap: calc(var(--design-unit, 4) * 3px);
28
- padding: calc(var(--design-unit, 4) * 1.5px) calc(var(--design-unit, 4) * 3px);
29
- border-bottom: 1px solid var(--neutral-stroke-rest);
30
- background: var(--neutral-fill-stealth-rest);
31
- font-size: var(--type-ramp-minus-1-font-size);
32
- color: var(--neutral-foreground-rest);
33
- }
34
-
35
- .chat-title {
36
- flex: 1;
37
- font-weight: 600;
38
- font-size: var(--type-ramp-base-font-size);
39
- overflow: hidden;
40
- text-overflow: ellipsis;
41
- white-space: nowrap;
42
- }
43
-
44
- .toggle-label {
45
- display: flex;
46
- align-items: center;
47
- gap: calc(var(--design-unit, 4) * 1px);
48
- cursor: pointer;
49
- user-select: none;
50
- }
51
-
52
- .toggle-label input[type='checkbox'] {
53
- cursor: pointer;
54
- accent-color: var(--accent-fill-rest);
55
- }
56
-
57
- .download-button {
58
- margin-left: auto;
59
- }
60
-
61
- .messages {
62
- flex: 1;
63
- overflow-y: auto;
64
- padding: calc(var(--design-unit, 4) * 3px);
65
- display: flex;
66
- flex-direction: column;
67
- gap: calc(var(--design-unit, 4) * 2px);
68
- }
69
-
70
- .message {
71
- padding: calc(var(--design-unit, 4) * 2px) calc(var(--design-unit, 4) * 3px);
72
- border-radius: calc(var(--design-unit, 4) * 2px);
73
- max-width: 80%;
74
- word-break: break-word;
75
- font-size: var(--type-ramp-base-font-size);
76
- line-height: var(--type-ramp-base-line-height);
77
- }
78
-
79
- .message--user {
80
- align-self: flex-end;
81
- background: var(--accent-fill-rest);
82
- color: var(--foreground-on-accent-rest);
83
- border-bottom-right-radius: calc(var(--design-unit, 4) * 0.5px);
84
- }
85
-
86
- .message--assistant {
87
- align-self: flex-start;
88
- background: var(--neutral-fill-rest);
89
- color: var(--neutral-foreground-rest);
90
- border-bottom-left-radius: calc(var(--design-unit, 4) * 0.5px);
91
- }
92
-
93
- .thinking-block {
94
- margin-bottom: calc(var(--design-unit, 4) * 1px);
95
- border: 1px solid var(--neutral-stroke-rest);
96
- border-radius: calc(var(--design-unit, 4) * 1px);
97
- overflow: hidden;
98
- }
99
-
100
- .thinking-summary {
101
- cursor: pointer;
102
- padding: calc(var(--design-unit, 4) * 1px) calc(var(--design-unit, 4) * 2px);
103
- font-size: var(--type-ramp-minus-1-font-size);
104
- color: var(--neutral-foreground-hint);
105
- user-select: none;
106
- list-style: none;
107
- }
108
-
109
- .thinking-summary::marker,
110
- .thinking-summary::-webkit-details-marker {
111
- display: none;
112
- }
113
-
114
- .thinking-summary::before {
115
- content: '▶ ';
116
- font-size: 0.6em;
117
- vertical-align: middle;
118
- }
119
-
120
- details[open] .thinking-summary::before {
121
- content: '▼ ';
122
- }
123
-
124
- .thinking-content {
125
- padding: calc(var(--design-unit, 4) * 2px);
126
- font-size: var(--type-ramp-minus-1-font-size);
127
- color: var(--neutral-foreground-hint);
128
- border-top: 1px solid var(--neutral-stroke-rest);
129
- }
130
-
131
- .tool-calls {
132
- font-size: var(--type-ramp-minus-1-font-size);
133
- color: var(--neutral-foreground-hint);
134
- font-family: monospace;
135
- }
136
-
137
- .message--tool {
138
- align-self: flex-start;
139
- background: var(--neutral-fill-stealth-rest);
140
- color: var(--neutral-foreground-hint);
141
- font-size: var(--type-ramp-minus-1-font-size);
142
- font-family: monospace;
143
- max-width: 100%;
144
- border-radius: calc(var(--design-unit, 4) * 1px);
145
- }
146
-
147
- .empty-state {
148
- flex: 1;
149
- display: flex;
150
- align-items: center;
151
- justify-content: center;
152
- color: var(--neutral-foreground-hint);
153
- font-size: var(--type-ramp-base-font-size);
154
- }
155
-
156
- .thinking {
157
- align-self: flex-start;
158
- padding: calc(var(--design-unit, 4) * 1px);
159
- }
160
-
161
- .file-input {
162
- display: none;
163
- }
164
-
165
- .attachment-chips {
166
- display: flex;
167
- flex-wrap: wrap;
168
- gap: calc(var(--design-unit, 4) * 1px);
169
- padding: calc(var(--design-unit, 4) * 1px) calc(var(--design-unit, 4) * 2px);
170
- border-top: 1px solid var(--neutral-stroke-rest);
171
- }
172
-
173
- .attachment-chip {
174
- display: inline-flex;
175
- align-items: center;
176
- gap: calc(var(--design-unit, 4) * 1px);
177
- padding: calc(var(--design-unit, 4) * 0.5px) calc(var(--design-unit, 4) * 1.5px);
178
- background: var(--neutral-fill-rest);
179
- border: 1px solid var(--neutral-stroke-rest);
180
- border-radius: calc(var(--design-unit, 4) * 4px);
181
- font-size: var(--type-ramp-minus-1-font-size);
182
- color: var(--neutral-foreground-rest);
183
- max-width: 200px;
184
- overflow: hidden;
185
- text-overflow: ellipsis;
186
- white-space: nowrap;
187
- }
188
-
189
- .attachment-chip-error {
190
- background: var(--error-fill-rest, #fde8e8);
191
- border-color: var(--error-stroke-rest, #f87171);
192
- color: var(--error-foreground-rest, #b91c1c);
193
- }
194
-
195
- .chip-remove {
196
- flex-shrink: 0;
197
- background: none;
198
- border: none;
199
- cursor: pointer;
200
- padding: 0;
201
- line-height: 1;
202
- color: inherit;
203
- opacity: 0.6;
204
- font-size: 0.9em;
205
- }
206
-
207
- .chip-remove:hover {
208
- opacity: 1;
209
- }
210
-
211
- .input-row {
212
- display: flex;
213
- gap: calc(var(--design-unit, 4) * 2px);
214
- padding: calc(var(--design-unit, 4) * 2px);
215
- border-top: 1px solid var(--neutral-stroke-rest);
216
- align-items: flex-end;
217
- }
218
-
219
- .attach-button {
220
- flex-shrink: 0;
221
- align-self: flex-end;
222
- }
223
-
224
- .chat-input {
225
- flex: 1;
226
- }
227
-
228
- .chat-input::part(control) {
229
- resize: none;
230
- min-height: calc(var(--base-height-multiplier, 10) * 1px + var(--design-unit, 4) * 4px);
231
- max-height: calc(var(--base-height-multiplier, 10) * 5px);
232
- }
233
-
234
- .send-button {
235
- flex-shrink: 0;
236
- align-self: flex-end;
237
- }
238
- `;