@eeacms/volto-eea-chatbot 2.0.5 → 3.0.1
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.
- package/.github/workflows/betterleaks.yml +133 -0
- package/.gitleaks.toml +89 -0
- package/AGENTS.md +230 -0
- package/CHANGELOG.md +40 -0
- package/README.md +59 -0
- package/RELEASE.md +1 -1
- package/locales/de/LC_MESSAGES/volto.po +44 -0
- package/locales/en/LC_MESSAGES/volto.po +44 -0
- package/locales/it/LC_MESSAGES/volto.po +44 -0
- package/locales/ro/LC_MESSAGES/volto.po +44 -0
- package/locales/volto.pot +46 -2
- package/package.json +2 -1
- package/src/AISearchInput/AISearchInputEdit.jsx +37 -0
- package/src/AISearchInput/AISearchInputView.jsx +130 -0
- package/src/AISearchInput/components/DeepResearchToggle.jsx +35 -0
- package/src/AISearchInput/components/DisclaimerText.jsx +10 -0
- package/src/AISearchInput/components/IntroHeader.jsx +24 -0
- package/src/AISearchInput/components/PromptChips.jsx +22 -0
- package/src/AISearchInput/components/SearchInput.jsx +53 -0
- package/src/AISearchInput/index.js +32 -0
- package/src/AISearchInput/messages.js +38 -0
- package/src/AISearchInput/schema.jsx +215 -0
- package/src/AISearchInput/styles.less +320 -0
- package/src/ChatBlock/ChatBlockView.jsx +4 -0
- package/src/ChatBlock/chat/ChatWindow.tsx +23 -0
- package/src/ChatBlock/packets/MultiToolRenderer.tsx +20 -11
- package/src/ChatBlock/services/messageProcessor.ts +32 -14
- package/src/ChatBlock/style.less +41 -51
- package/src/halloumi/filtering.js +2 -2
- package/src/halloumi/generative.test.js +1 -1
- package/src/halloumi/middleware.js +2 -2
- package/src/halloumi/middleware.test.js +5 -5
- package/src/icons/sparkle.svg +3 -0
- package/src/index.js +2 -0
- package/src/middleware.js +2 -2
- package/src/middleware.test.js +11 -11
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
name: Betterleaks
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
scan:
|
|
12
|
+
name: Scan for secrets
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
env:
|
|
15
|
+
SMTP_URL: ${{ secrets.SMTP_URL }}
|
|
16
|
+
SMTP_PORT: ${{ secrets.SMTP_PORT || '25' }}
|
|
17
|
+
SMTP_EMAIL: ${{ secrets.SMTP_EMAIL }}
|
|
18
|
+
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout repository
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
with:
|
|
23
|
+
fetch-depth: 1
|
|
24
|
+
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
25
|
+
|
|
26
|
+
- name: Run Betterleaks
|
|
27
|
+
id: betterleaks
|
|
28
|
+
continue-on-error: true
|
|
29
|
+
uses: dortort/betterleaks-action@v0.1.0
|
|
30
|
+
with:
|
|
31
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
32
|
+
scan-mode: dir
|
|
33
|
+
scan-path: .
|
|
34
|
+
config: .gitleaks.toml
|
|
35
|
+
report-format: json
|
|
36
|
+
report-path: betterleaks-report.json
|
|
37
|
+
redact: "true"
|
|
38
|
+
no-color: "true"
|
|
39
|
+
no-banner: "true"
|
|
40
|
+
fail-on-leak: "true"
|
|
41
|
+
|
|
42
|
+
- name: Upload Betterleaks report
|
|
43
|
+
if: always()
|
|
44
|
+
uses: actions/upload-artifact@v4
|
|
45
|
+
with:
|
|
46
|
+
name: betterleaks-report
|
|
47
|
+
path: betterleaks-report.json
|
|
48
|
+
if-no-files-found: ignore
|
|
49
|
+
|
|
50
|
+
- name: Build Betterleaks email summary
|
|
51
|
+
id: leak_summary
|
|
52
|
+
if: steps.betterleaks.outcome == 'failure'
|
|
53
|
+
shell: bash
|
|
54
|
+
run: |
|
|
55
|
+
if [[ -s betterleaks-report.json ]]; then
|
|
56
|
+
jq -r '
|
|
57
|
+
def one_line:
|
|
58
|
+
tostring
|
|
59
|
+
| gsub("[\r\n]+"; " ")
|
|
60
|
+
| if length > 240 then .[0:240] + "..." else . end;
|
|
61
|
+
|
|
62
|
+
.[:20][]
|
|
63
|
+
| "- " + (.RuleID // "unknown-rule")
|
|
64
|
+
+ " at " + (.File // "unknown-file")
|
|
65
|
+
+ ":" + ((.StartLine // 0) | tostring)
|
|
66
|
+
+ "\n match: " + ((.Match // .Secret // "REDACTED") | one_line)
|
|
67
|
+
' betterleaks-report.json > betterleaks-email-summary.txt
|
|
68
|
+
|
|
69
|
+
count="$(jq 'length' betterleaks-report.json)"
|
|
70
|
+
if (( count > 20 )); then
|
|
71
|
+
{
|
|
72
|
+
echo ""
|
|
73
|
+
echo "... and $((count - 20)) more finding(s). Download the artifact for full details."
|
|
74
|
+
} >> betterleaks-email-summary.txt
|
|
75
|
+
fi
|
|
76
|
+
else
|
|
77
|
+
echo "No JSON report was generated. Download the workflow logs for details." > betterleaks-email-summary.txt
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
{
|
|
81
|
+
echo "text<<BETTERLEAKS_SUMMARY"
|
|
82
|
+
cat betterleaks-email-summary.txt
|
|
83
|
+
echo "BETTERLEAKS_SUMMARY"
|
|
84
|
+
} >> "$GITHUB_OUTPUT"
|
|
85
|
+
|
|
86
|
+
- name: Resolve committer email
|
|
87
|
+
id: committer
|
|
88
|
+
if: steps.betterleaks.outcome == 'failure'
|
|
89
|
+
shell: bash
|
|
90
|
+
run: |
|
|
91
|
+
committer_email="$(git log -1 --format='%ce')"
|
|
92
|
+
author_email="$(git log -1 --format='%ae')"
|
|
93
|
+
email="$committer_email"
|
|
94
|
+
if [[ -z "$email" || "$email" == *"noreply.github.com"* ]]; then
|
|
95
|
+
email="$author_email"
|
|
96
|
+
fi
|
|
97
|
+
if [[ "$email" =~ ^[^[:space:]@]+@[^[:space:]@]+\.[^[:space:]@]+$ && "$email" != *"noreply.github.com"* ]]; then
|
|
98
|
+
echo "email=$email" >> "$GITHUB_OUTPUT"
|
|
99
|
+
else
|
|
100
|
+
echo "No deliverable committer email found; skipping Betterleaks email notification."
|
|
101
|
+
echo "email=" >> "$GITHUB_OUTPUT"
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
- name: Email committer on Betterleaks failure
|
|
105
|
+
if: steps.betterleaks.outcome == 'failure' && steps.committer.outputs.email != '' && env.SMTP_URL != '' && env.SMTP_EMAIL != ''
|
|
106
|
+
uses: dawidd6/action-send-mail@v18
|
|
107
|
+
with:
|
|
108
|
+
server_address: ${{ env.SMTP_URL }}
|
|
109
|
+
server_port: ${{ env.SMTP_PORT }}
|
|
110
|
+
secure: ${{ env.SMTP_PORT == '465' }}
|
|
111
|
+
username: ${{ env.SMTP_EMAIL }}
|
|
112
|
+
password: ${{ env.SMTP_PASSWORD }}
|
|
113
|
+
from: ${{ env.SMTP_EMAIL }}
|
|
114
|
+
to: ${{ steps.committer.outputs.email }}
|
|
115
|
+
subject: "[Betterleaks] Secret scan failed in ${{ github.repository }}"
|
|
116
|
+
body: |
|
|
117
|
+
Betterleaks detected one or more potential secrets.
|
|
118
|
+
|
|
119
|
+
Repository: ${{ github.repository }}
|
|
120
|
+
Branch: ${{ github.ref_name }}
|
|
121
|
+
Commit: ${{ github.sha }}
|
|
122
|
+
Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
123
|
+
|
|
124
|
+
Findings:
|
|
125
|
+
${{ steps.leak_summary.outputs.text }}
|
|
126
|
+
|
|
127
|
+
Download the betterleaks-report artifact from the workflow run for details.
|
|
128
|
+
|
|
129
|
+
- name: Fail if Betterleaks found leaks
|
|
130
|
+
if: steps.betterleaks.outcome == 'failure'
|
|
131
|
+
run: |
|
|
132
|
+
echo "Betterleaks detected one or more secrets. Download the betterleaks-report artifact from this workflow run for details."
|
|
133
|
+
exit 1
|
package/.gitleaks.toml
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
title = "Betterleaks config"
|
|
2
|
+
|
|
3
|
+
[extend]
|
|
4
|
+
useDefault = true
|
|
5
|
+
|
|
6
|
+
[[rules]]
|
|
7
|
+
id = "secret-literal-assignment"
|
|
8
|
+
description = "Secret-like literal assignment in source, YAML, env and config files"
|
|
9
|
+
regex = '''(?i)([A-Za-z0-9_.-]*(?:password|passwd|pwd|api[_-]?key|apikey|access[_-]?key|secret[_-]?access[_-]?key|private[_-]?key|client[_-]?secret|consumer[_-]?key|consumer[_-]?secret|(?:auth|access|refresh|session|api|bearer|id|jwt|csrf|xsrf|oauth)[_-]?token|translate[_-]?auth|translation[_-]?auth|auth[_-]?password|auth[_-]?key|auth[_-]?credentials|database[_-]?url|connection[_-]?string)[A-Za-z0-9_.-]*)[ \t]*[:=][ \t]*["'']?([^"''[:space:]#{}$.][^"''\n#{}]{2,})["'']?'''
|
|
10
|
+
secretGroup = 2
|
|
11
|
+
keywords = [
|
|
12
|
+
"password",
|
|
13
|
+
"passwd",
|
|
14
|
+
"pwd",
|
|
15
|
+
"api_key",
|
|
16
|
+
"apikey",
|
|
17
|
+
"access_key",
|
|
18
|
+
"private_key",
|
|
19
|
+
"client_secret",
|
|
20
|
+
"consumer_key",
|
|
21
|
+
"consumer_secret",
|
|
22
|
+
"auth_token",
|
|
23
|
+
"access_token",
|
|
24
|
+
"refresh_token",
|
|
25
|
+
"session_token",
|
|
26
|
+
"api_token",
|
|
27
|
+
"bearer_token",
|
|
28
|
+
"id_token",
|
|
29
|
+
"jwt_token",
|
|
30
|
+
"csrf_token",
|
|
31
|
+
"xsrf_token",
|
|
32
|
+
"oauth_token",
|
|
33
|
+
"translate_auth",
|
|
34
|
+
"translation_auth",
|
|
35
|
+
"auth_password",
|
|
36
|
+
"auth_key",
|
|
37
|
+
"auth_credentials",
|
|
38
|
+
"database_url",
|
|
39
|
+
"connection_string"
|
|
40
|
+
]
|
|
41
|
+
tags = ["literal-secret"]
|
|
42
|
+
|
|
43
|
+
[[rules]]
|
|
44
|
+
id = "env-short-secret-assignment"
|
|
45
|
+
description = "Uppercase env-style PASS/TOKEN/SECRET assignment"
|
|
46
|
+
regex = '''\b((?:PASS|TOKEN|SECRET|[A-Z0-9_]*(?:_PASS|_TOKEN|_SECRET|PASS_|TOKEN_|SECRET_)[A-Z0-9_]*))[ \t]*[:=][ \t]*["'']?([^"''[:space:]#{}$.][^"''\n#{}]{2,})["'']?'''
|
|
47
|
+
secretGroup = 2
|
|
48
|
+
keywords = [
|
|
49
|
+
"PASS",
|
|
50
|
+
"TOKEN",
|
|
51
|
+
"SECRET"
|
|
52
|
+
]
|
|
53
|
+
tags = ["env", "literal-secret"]
|
|
54
|
+
|
|
55
|
+
[[rules]]
|
|
56
|
+
id = "standalone-sk-token"
|
|
57
|
+
description = "Standalone sk-* token not attached to a secret-like variable name"
|
|
58
|
+
regex = '''(?i)\b(sk-[A-Za-z0-9][A-Za-z0-9_-]{20,})\b'''
|
|
59
|
+
secretGroup = 1
|
|
60
|
+
entropy = 2.5
|
|
61
|
+
keywords = ["sk-"]
|
|
62
|
+
tags = ["standalone-token", "generic"]
|
|
63
|
+
|
|
64
|
+
[[rules]]
|
|
65
|
+
id = "dotenv-only-jest-setup"
|
|
66
|
+
description = ".env may only contain the committed Jest setup lines"
|
|
67
|
+
path = '''(?i)(^|/)\.env$'''
|
|
68
|
+
regex = '''(?m)^(.+)$'''
|
|
69
|
+
secretGroup = 1
|
|
70
|
+
tags = ["file", "dotenv"]
|
|
71
|
+
[[rules.allowlists]]
|
|
72
|
+
regexTarget = "match"
|
|
73
|
+
regexes = [
|
|
74
|
+
'''^JEST_USE_SETUP=OFF # Jest configuration variables: ON, OFF\r?$''',
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
[[rules]]
|
|
78
|
+
id = "forbidden-secret-file"
|
|
79
|
+
description = "Forbidden secret-bearing file committed to repository"
|
|
80
|
+
path = '''(?i)(^|/)(\.env\..*|\.npmrc|\.pypirc|id_rsa|id_ed25519|.*\.(pem|key|p12|pfx|jks|kubeconfig))$'''
|
|
81
|
+
regex = '''(?s).{1,}'''
|
|
82
|
+
tags = ["file", "secret-file"]
|
|
83
|
+
|
|
84
|
+
[[allowlists]]
|
|
85
|
+
description = "Allow Jenkins SonarQube token environment variable reference"
|
|
86
|
+
regexTarget = "match"
|
|
87
|
+
regexes = [
|
|
88
|
+
'''SONAR_AUTH_TOKEN''',
|
|
89
|
+
]
|
package/AGENTS.md
CHANGED
|
@@ -87,3 +87,233 @@ and consistency.
|
|
|
87
87
|
|
|
88
88
|
For more detailed contribution guidelines, refer to `DEVELOP.md`.
|
|
89
89
|
For release procedures, refer to `RELEASE.md`.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Frontend Development Guide for AI Assistants
|
|
94
|
+
|
|
95
|
+
### EEA Design System Tokens
|
|
96
|
+
|
|
97
|
+
The EEA Design System provides shared LESS tokens for colors, spacing, typography, borders, and shapes. These are available via `@eeacms/volto-design-tokens`.
|
|
98
|
+
|
|
99
|
+
**Importing tokens:**
|
|
100
|
+
|
|
101
|
+
```less
|
|
102
|
+
@import '@eeacms/volto-design-tokens/src/colors'; // @blue-6, @grey-0, @green-4, etc.
|
|
103
|
+
@import '@eeacms/volto-design-tokens/src/shapes'; // @radius-1, @radius-3, @radius-round
|
|
104
|
+
@import '@eeacms/volto-design-tokens/src/borders'; // @border-size-1, @border-size-2
|
|
105
|
+
@import '@eeacms/volto-design-tokens/src/fonts'; // @font-size-0, @font-weight-6, @font-lineheight-2
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Pitfall:** Do NOT import `@eeacms/volto-design-tokens/src/sizes` directly — it references `@1px` which is a Semantic UI variable not available in standalone LESS. Instead, inline the spacing tokens you need:
|
|
109
|
+
|
|
110
|
+
```less
|
|
111
|
+
// Spacing tokens (from sizes.less, inlined to avoid @1px dependency)
|
|
112
|
+
@spacer: 4px;
|
|
113
|
+
@space-05: @spacer * 0.5; // 2px
|
|
114
|
+
@space-1: @spacer * 1; // 4px
|
|
115
|
+
@space-2: @spacer * 2; // 8px
|
|
116
|
+
@space-3: @spacer * 3; // 12px
|
|
117
|
+
@space-4: @spacer * 4; // 16px
|
|
118
|
+
@space-5: @spacer * 5; // 20px
|
|
119
|
+
@space-6: @spacer * 6; // 24px
|
|
120
|
+
@space-8: @spacer * 8; // 32px
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**When adding new LESS files that use design tokens, ensure `@eeacms/volto-design-tokens` is listed in `package.json` dependencies:**
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
"dependencies": {
|
|
127
|
+
"@eeacms/volto-design-tokens": "*"
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Rendering Slate (Rich Text) Content
|
|
132
|
+
|
|
133
|
+
Volto stores rich text as Slate JSON. To render it in view components, use `serializeNodes`:
|
|
134
|
+
|
|
135
|
+
```jsx
|
|
136
|
+
import { serializeNodes } from '@plone/volto-slate/editor/render';
|
|
137
|
+
|
|
138
|
+
// In your component:
|
|
139
|
+
{someSlateContent && serializeNodes(someSlateContent)}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Do NOT use `SlateViewer` from `@plone/volto/components`** — it does not exist. The `serializeNodes` function is the correct approach (used internally by the ChatBlock's `AIMessage.tsx`).
|
|
143
|
+
|
|
144
|
+
### Volto Block Registration Pattern
|
|
145
|
+
|
|
146
|
+
When creating a new Volto block:
|
|
147
|
+
|
|
148
|
+
1. **Schema function** accepts `{ intl, data }` and returns a schema object:
|
|
149
|
+
```jsx
|
|
150
|
+
export function MyBlockSchema({ intl, data }) {
|
|
151
|
+
return {
|
|
152
|
+
title: intl.formatMessage({ id: 'My Block', defaultMessage: 'My Block' }),
|
|
153
|
+
fieldsets: [{ id: 'default', title: 'Default', fields: ['myField'] }],
|
|
154
|
+
properties: { /* ... */ },
|
|
155
|
+
required: [],
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
2. **Edit component** must inject `intl` via `useIntl` and pass it to the schema:
|
|
161
|
+
```jsx
|
|
162
|
+
import { useIntl } from 'react-intl';
|
|
163
|
+
|
|
164
|
+
const MyBlockEdit = (props) => {
|
|
165
|
+
const intl = useIntl();
|
|
166
|
+
const schema = React.useMemo(
|
|
167
|
+
() => MyBlockSchema({ intl, data: props.data }),
|
|
168
|
+
[props.data, intl],
|
|
169
|
+
);
|
|
170
|
+
// ... render with BlockDataForm
|
|
171
|
+
};
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
3. **Block registration** in `index.js`:
|
|
175
|
+
```js
|
|
176
|
+
export default function installMyBlock(config) {
|
|
177
|
+
config.blocks.blocksConfig.myBlock = {
|
|
178
|
+
id: 'myBlock',
|
|
179
|
+
title: 'My Block',
|
|
180
|
+
icon: someSVG,
|
|
181
|
+
group: 'common',
|
|
182
|
+
view: MyBlockView,
|
|
183
|
+
edit: MyBlockEdit,
|
|
184
|
+
schema: MyBlockSchema,
|
|
185
|
+
// ...
|
|
186
|
+
};
|
|
187
|
+
return config;
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
4. **Wire into `src/index.js`**: `installMyBlock(config);`
|
|
192
|
+
|
|
193
|
+
### Internationalization (i18n)
|
|
194
|
+
|
|
195
|
+
**All user-facing strings MUST use `react-intl`:**
|
|
196
|
+
|
|
197
|
+
```jsx
|
|
198
|
+
import { FormattedMessage, defineMessages } from 'react-intl';
|
|
199
|
+
|
|
200
|
+
// In JSX:
|
|
201
|
+
<FormattedMessage id="Hello world" defaultMessage="Hello world" />
|
|
202
|
+
|
|
203
|
+
// For message definitions (e.g., in messages.js):
|
|
204
|
+
export default defineMessages({
|
|
205
|
+
greeting: { id: 'Hello world', defaultMessage: 'Hello world' },
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
After adding new strings, run `make i18n` to extract them into PO files.
|
|
210
|
+
|
|
211
|
+
### Semantic UI Toggle Checkboxes
|
|
212
|
+
|
|
213
|
+
**Avoid using Semantic UI `<Checkbox toggle>` for custom-styled toggles.** The toggle's internal DOM structure (`.box`, `label:before`) is difficult to style reliably across different background colors, and the default styles often clash with custom themes.
|
|
214
|
+
|
|
215
|
+
**Preferred approach:** Use a pure CSS toggle with a hidden `<input type="checkbox">`:
|
|
216
|
+
|
|
217
|
+
```jsx
|
|
218
|
+
<label className="my-toggle">
|
|
219
|
+
<input type="checkbox" checked={enabled} onChange={(e) => onChange(e.target.checked)} />
|
|
220
|
+
<span className="my-toggle-slider" />
|
|
221
|
+
Label text
|
|
222
|
+
</label>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
```less
|
|
226
|
+
.my-toggle {
|
|
227
|
+
display: inline-flex;
|
|
228
|
+
align-items: center;
|
|
229
|
+
gap: 8px;
|
|
230
|
+
cursor: pointer;
|
|
231
|
+
|
|
232
|
+
input { display: none; }
|
|
233
|
+
|
|
234
|
+
.my-toggle-slider {
|
|
235
|
+
position: relative;
|
|
236
|
+
width: 28px;
|
|
237
|
+
height: 16px;
|
|
238
|
+
border: 1px solid currentColor;
|
|
239
|
+
border-radius: 1rem;
|
|
240
|
+
background-color: rgba(currentColor, 0.15);
|
|
241
|
+
|
|
242
|
+
&::before {
|
|
243
|
+
position: absolute;
|
|
244
|
+
top: 1px;
|
|
245
|
+
left: 2px;
|
|
246
|
+
width: 12px;
|
|
247
|
+
height: 12px;
|
|
248
|
+
border-radius: 50%;
|
|
249
|
+
background-color: currentColor;
|
|
250
|
+
content: '';
|
|
251
|
+
transition: left 0.2s;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
input:checked ~ .my-toggle-slider {
|
|
256
|
+
background-color: @green-4;
|
|
257
|
+
border-color: @green-4;
|
|
258
|
+
|
|
259
|
+
&::before { left: 13px; }
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### LESS Styling Conventions
|
|
265
|
+
|
|
266
|
+
- **Property ordering:** Stylelint enforces alphabetical property order. Run `make stylelint-fix` to auto-fix.
|
|
267
|
+
- **`:global()` usage:** Use `:global()` in LESS nesting to target Semantic UI or Volto classes without the parent selector prefix:
|
|
268
|
+
```less
|
|
269
|
+
.my-component {
|
|
270
|
+
:global(.ui.button) { color: red; }
|
|
271
|
+
}
|
|
272
|
+
// Produces: .my-component .ui.button { color: red; }
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Testing Patterns
|
|
276
|
+
|
|
277
|
+
**Mocking common dependencies in Jest tests:**
|
|
278
|
+
|
|
279
|
+
```js
|
|
280
|
+
// react-intl
|
|
281
|
+
jest.mock('react-intl', () => ({
|
|
282
|
+
useIntl: () => ({ formatMessage: ({ defaultMessage }) => defaultMessage }),
|
|
283
|
+
FormattedMessage: ({ defaultMessage }) => <span>{defaultMessage}</span>,
|
|
284
|
+
}));
|
|
285
|
+
|
|
286
|
+
// react-router-dom (Volto uses v5)
|
|
287
|
+
const mockHistory = { push: jest.fn(), replace: jest.fn() };
|
|
288
|
+
jest.mock('react-router-dom', () => ({ useHistory: () => mockHistory }));
|
|
289
|
+
|
|
290
|
+
// Volto components
|
|
291
|
+
jest.mock('@plone/volto/components/manage/Sidebar/SidebarPortal', () => ({
|
|
292
|
+
__esModule: true,
|
|
293
|
+
default: ({ selected, children }) =>
|
|
294
|
+
selected ? <div>{children}</div> : null,
|
|
295
|
+
}));
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Internal Import Paths
|
|
299
|
+
|
|
300
|
+
Use `@eeacms/volto-eea-chatbot/...` for internal imports (matches the Jest `moduleNameMapper`):
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
import SVGIcon from '@eeacms/volto-eea-chatbot/ChatBlock/components/Icon';
|
|
304
|
+
import SendIcon from '@eeacms/volto-eea-chatbot/icons/send.svg';
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Matomo Tracking
|
|
308
|
+
|
|
309
|
+
Use `trackEvent` from `@eeacms/volto-matomo/utils` for analytics:
|
|
310
|
+
|
|
311
|
+
```js
|
|
312
|
+
import { trackEvent } from '@eeacms/volto-matomo/utils';
|
|
313
|
+
|
|
314
|
+
trackEvent({
|
|
315
|
+
category: 'Chatbot',
|
|
316
|
+
action: 'Some action',
|
|
317
|
+
name: 'Some name',
|
|
318
|
+
});
|
|
319
|
+
```
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,46 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
### [3.0.1](https://github.com/eea/volto-eea-chatbot/compare/3.0.0...3.0.1) - 5 August 2026
|
|
8
|
+
|
|
9
|
+
#### :bug: Bug Fixes
|
|
10
|
+
|
|
11
|
+
- fix: Add betterleaks github action - refs #304517 [dobri1408 - [`837c461`](https://github.com/eea/volto-eea-chatbot/commit/837c4619bd3dab3ffe06aeaabd0513c50e3066b4)]
|
|
12
|
+
|
|
13
|
+
## [3.0.0](https://github.com/eea/volto-eea-chatbot/compare/2.0.5...3.0.0) - 7 July 2026
|
|
14
|
+
|
|
15
|
+
#### :bug: Bug Fixes
|
|
16
|
+
|
|
17
|
+
- fix: refine custom toggle switch sizing and proportions [Tiberiu Ichim - [`d4c7499`](https://github.com/eea/volto-eea-chatbot/commit/d4c749964f9a1be063fae930c01b4a34e608909d)]
|
|
18
|
+
- fix: use 50% border-radius for toggle knob instead of @radius-round [Tiberiu Ichim - [`9013d1c`](https://github.com/eea/volto-eea-chatbot/commit/9013d1c889b26715be7b6c8a3b76b9bbc20a96de)]
|
|
19
|
+
- fix: inline spacing tokens to avoid Semantic UI @1px dependency [Tiberiu Ichim - [`7cb8ee6`](https://github.com/eea/volto-eea-chatbot/commit/7cb8ee69710005632f76d6aabc171f0ac70561d8)]
|
|
20
|
+
- fix: add @eeacms/volto-design-tokens as dependency [Tiberiu Ichim - [`5573047`](https://github.com/eea/volto-eea-chatbot/commit/5573047ec574e6a50ec5fb39e673f993b439f237)]
|
|
21
|
+
- fix: replace Semantic UI Checkbox with custom CSS toggle switch [Tiberiu Ichim - [`8b6780b`](https://github.com/eea/volto-eea-chatbot/commit/8b6780b05b5f334142d53b7ce5070565d93e1fc5)]
|
|
22
|
+
- fix: remove custom checkbox styling, use Semantic UI defaults [Tiberiu Ichim - [`c070e90`](https://github.com/eea/volto-eea-chatbot/commit/c070e90142ea3ba7eb281193d32fe71066f6ef40)]
|
|
23
|
+
- fix: make deep research toggle checkbox visible [Tiberiu Ichim - [`793716f`](https://github.com/eea/volto-eea-chatbot/commit/793716ff52708f32b99644c9572975649c4b6a1e)]
|
|
24
|
+
- fix: place prompt chips and deep research toggle on same row [Tiberiu Ichim - [`97c2e82`](https://github.com/eea/volto-eea-chatbot/commit/97c2e82b54b7eb5612ef595bd4e7c83c231e2e8f)]
|
|
25
|
+
- fix: align deep research toggle to the right with flexbox [Tiberiu Ichim - [`64755cd`](https://github.com/eea/volto-eea-chatbot/commit/64755cd9d578081a12259ac991c7d8da67dda5e9)]
|
|
26
|
+
- fix: use EEA green/teal for accent variant instead of blue [Tiberiu Ichim - [`ff0e46c`](https://github.com/eea/volto-eea-chatbot/commit/ff0e46cb1a7c1c2b52e7524a6f95c135dcdd33b7)]
|
|
27
|
+
- fix: pass intl to AISearchInputSchema in edit component [Tiberiu Ichim - [`fd1144d`](https://github.com/eea/volto-eea-chatbot/commit/fd1144d0f351fd9630732b9c465c4ed4219cf1c7)]
|
|
28
|
+
- fix: import EEA Design System color tokens in styles.less [Tiberiu Ichim - [`d84cda9`](https://github.com/eea/volto-eea-chatbot/commit/d84cda9355e911607996a72539279cb53f2524b0)]
|
|
29
|
+
- fix: replace non-existent SlateViewer with serializeNodes from volto-slate [Tiberiu Ichim - [`246ac09`](https://github.com/eea/volto-eea-chatbot/commit/246ac099ebd0cd90879de9f52e4b124c3abbc8d5)]
|
|
30
|
+
|
|
31
|
+
#### :nail_care: Enhancements
|
|
32
|
+
|
|
33
|
+
- refactor: replace hardcoded values with EEA Design System tokens [Tiberiu Ichim - [`723c939`](https://github.com/eea/volto-eea-chatbot/commit/723c939546d8dbbc83b9df13860303d73478893a)]
|
|
34
|
+
|
|
35
|
+
#### :house: Internal changes
|
|
36
|
+
|
|
37
|
+
- chore: bump version to 3.0.0 (EEA design system dependency) [skip ci] [Tiberiu Ichim - [`bc5a8c1`](https://github.com/eea/volto-eea-chatbot/commit/bc5a8c1f72d4e706b096cb96d0d24c2c3f5ac0f8)]
|
|
38
|
+
- chore: [JENKINSFILE] sanitize sonarqube command Refs #305084 [valentinab25 - [`83bf08b`](https://github.com/eea/volto-eea-chatbot/commit/83bf08b7188548a50801b8e464a4d113f0cbbac4)]
|
|
39
|
+
|
|
40
|
+
#### :house: Documentation changes
|
|
41
|
+
|
|
42
|
+
- docs: add frontend development guide for AI assistants [Tiberiu Ichim - [`36a77dc`](https://github.com/eea/volto-eea-chatbot/commit/36a77dcc7c6b551d4f049066ced698ff905fd4ef)]
|
|
43
|
+
|
|
44
|
+
#### :hammer_and_wrench: Others
|
|
45
|
+
|
|
46
|
+
- test: pin Chromium version 149 to work with Cypress [valentinab25 - [`5db1fcc`](https://github.com/eea/volto-eea-chatbot/commit/5db1fcc53c3147cc35c5d8c561c5dd0776e683c7)]
|
|
7
47
|
### [2.0.5](https://github.com/eea/volto-eea-chatbot/compare/2.0.4...2.0.5) - 21 June 2026
|
|
8
48
|
|
|
9
49
|
#### :bug: Bug Fixes
|
package/README.md
CHANGED
|
@@ -162,6 +162,65 @@ See [RELEASE.md](https://github.com/eea/volto-eea-chatbot/blob/master/RELEASE.md
|
|
|
162
162
|
|
|
163
163
|
See [DEVELOP.md](https://github.com/eea/volto-eea-chatbot/blob/master/DEVELOP.md).
|
|
164
164
|
|
|
165
|
+
## Secret Scanning
|
|
166
|
+
|
|
167
|
+
This repository uses the Betterleaks GitHub Action to scan the current
|
|
168
|
+
repository content on every push and pull request. The scan uses the rules in
|
|
169
|
+
`.gitleaks.toml` and uploads a `betterleaks-report` artifact when a finding is
|
|
170
|
+
detected.
|
|
171
|
+
|
|
172
|
+
If the optional SMTP secrets are configured, failed scans also send an email to
|
|
173
|
+
the last commit committer. The workflow expects these repository or
|
|
174
|
+
organization secrets:
|
|
175
|
+
|
|
176
|
+
- `SMTP_URL`
|
|
177
|
+
- `SMTP_PORT` (optional, defaults to `25`)
|
|
178
|
+
- `SMTP_EMAIL`
|
|
179
|
+
- `SMTP_PASSWORD` (optional if the SMTP server does not require authentication)
|
|
180
|
+
|
|
181
|
+
Port `465` is sent with direct TLS; other ports use the default SMTP handshake.
|
|
182
|
+
The email includes a short finding summary from the redacted Betterleaks report,
|
|
183
|
+
including the redacted matched line from each finding.
|
|
184
|
+
|
|
185
|
+
There are three common outcomes:
|
|
186
|
+
|
|
187
|
+
1. **Everything is OK.** The `Betterleaks / Scan for secrets` check is green and
|
|
188
|
+
no action is needed. Regular references to runtime values are OK, for example:
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
const tokenFromCookie = req.universalCookies.get('auth_token');
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
2. **A real secret was found.** The check is red and the workflow log asks you to
|
|
195
|
+
download the `betterleaks-report` artifact. Open the artifact from the GitHub
|
|
196
|
+
Actions run and check the reported file, line and rule. Remove the committed
|
|
197
|
+
value, move it to the proper secret store, and rotate it if it was exposed.
|
|
198
|
+
A report entry looks like this:
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"RuleID": "secret-literal-assignment",
|
|
203
|
+
"File": "src/config.js",
|
|
204
|
+
"StartLine": 12,
|
|
205
|
+
"Secret": "[REDACTED]"
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
3. **The finding is a false positive.** Keep the value only if it is clearly not
|
|
210
|
+
sensitive, such as a test fixture, placeholder, or public example. Add
|
|
211
|
+
`betterleaks:allow` on the same line and include a short explanation in the
|
|
212
|
+
pull request.
|
|
213
|
+
|
|
214
|
+
```js
|
|
215
|
+
const testPassword = 'admin'; //betterleaks:allow
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
```yaml
|
|
219
|
+
password: "admin" #betterleaks:allow
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Do not add `betterleaks:allow` to real credentials.
|
|
223
|
+
|
|
165
224
|
## Copyright and license
|
|
166
225
|
|
|
167
226
|
The Initial Owner of the Original Code is European Environment Agency (EEA).
|
package/RELEASE.md
CHANGED
|
@@ -33,7 +33,7 @@ Release-it is a tool that automates 4 important steps in the release process:
|
|
|
33
33
|
To configure the authentification, you need to export GITHUB_TOKEN for [GitHub](https://github.com/settings/tokens)
|
|
34
34
|
|
|
35
35
|
```
|
|
36
|
-
export GITHUB_TOKEN=
|
|
36
|
+
export GITHUB_TOKEN="${GITHUB_TOKEN}"
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
To configure npm, you can use the `npm login` command or use a configuration file with a TOKEN :
|
|
@@ -11,4 +11,48 @@ msgstr ""
|
|
|
11
11
|
"Content-Transfer-Encoding: \n"
|
|
12
12
|
"Plural-Forms: \n"
|
|
13
13
|
|
|
14
|
+
#. Default: "AI Search Input"
|
|
15
|
+
#: AISearchInput/messages
|
|
16
|
+
msgid "AI Search Input"
|
|
17
|
+
msgstr ""
|
|
18
|
+
|
|
19
|
+
#. Default: "Ask your question..."
|
|
20
|
+
#: AISearchInput/components/SearchInput
|
|
21
|
+
#: AISearchInput/messages
|
|
22
|
+
msgid "Ask your question..."
|
|
23
|
+
msgstr ""
|
|
24
|
+
|
|
25
|
+
#. Default: "Assistant page is not configured. Please contact the site administrator."
|
|
26
|
+
#: AISearchInput/AISearchInputView
|
|
27
|
+
#: AISearchInput/messages
|
|
28
|
+
msgid "Assistant page is not configured. Please contact the site administrator."
|
|
29
|
+
msgstr ""
|
|
30
|
+
|
|
31
|
+
#. Default: "Deep research"
|
|
32
|
+
#: AISearchInput/components/DeepResearchToggle
|
|
33
|
+
#: AISearchInput/messages
|
|
34
|
+
msgid "Deep research"
|
|
35
|
+
msgstr ""
|
|
14
36
|
|
|
37
|
+
#. Default: "Deep research on"
|
|
38
|
+
#: AISearchInput/components/DeepResearchToggle
|
|
39
|
+
#: AISearchInput/messages
|
|
40
|
+
msgid "Deep research on"
|
|
41
|
+
msgstr ""
|
|
42
|
+
|
|
43
|
+
#. Default: "Invalid assistant page URL."
|
|
44
|
+
#: AISearchInput/AISearchInputView
|
|
45
|
+
#: AISearchInput/messages
|
|
46
|
+
msgid "Invalid assistant page URL."
|
|
47
|
+
msgstr ""
|
|
48
|
+
|
|
49
|
+
#. Default: "Submit question"
|
|
50
|
+
#: AISearchInput/components/SearchInput
|
|
51
|
+
#: AISearchInput/messages
|
|
52
|
+
msgid "Submit question"
|
|
53
|
+
msgstr ""
|
|
54
|
+
|
|
55
|
+
#. Default: "This assistant uses AI. Responses may contain inaccuracies."
|
|
56
|
+
#: AISearchInput/messages
|
|
57
|
+
msgid "This assistant uses AI. Responses may contain inaccuracies."
|
|
58
|
+
msgstr ""
|
|
@@ -11,4 +11,48 @@ msgstr ""
|
|
|
11
11
|
"Content-Transfer-Encoding: \n"
|
|
12
12
|
"Plural-Forms: \n"
|
|
13
13
|
|
|
14
|
+
#. Default: "AI Search Input"
|
|
15
|
+
#: AISearchInput/messages
|
|
16
|
+
msgid "AI Search Input"
|
|
17
|
+
msgstr ""
|
|
18
|
+
|
|
19
|
+
#. Default: "Ask your question..."
|
|
20
|
+
#: AISearchInput/components/SearchInput
|
|
21
|
+
#: AISearchInput/messages
|
|
22
|
+
msgid "Ask your question..."
|
|
23
|
+
msgstr ""
|
|
24
|
+
|
|
25
|
+
#. Default: "Assistant page is not configured. Please contact the site administrator."
|
|
26
|
+
#: AISearchInput/AISearchInputView
|
|
27
|
+
#: AISearchInput/messages
|
|
28
|
+
msgid "Assistant page is not configured. Please contact the site administrator."
|
|
29
|
+
msgstr ""
|
|
30
|
+
|
|
31
|
+
#. Default: "Deep research"
|
|
32
|
+
#: AISearchInput/components/DeepResearchToggle
|
|
33
|
+
#: AISearchInput/messages
|
|
34
|
+
msgid "Deep research"
|
|
35
|
+
msgstr ""
|
|
14
36
|
|
|
37
|
+
#. Default: "Deep research on"
|
|
38
|
+
#: AISearchInput/components/DeepResearchToggle
|
|
39
|
+
#: AISearchInput/messages
|
|
40
|
+
msgid "Deep research on"
|
|
41
|
+
msgstr ""
|
|
42
|
+
|
|
43
|
+
#. Default: "Invalid assistant page URL."
|
|
44
|
+
#: AISearchInput/AISearchInputView
|
|
45
|
+
#: AISearchInput/messages
|
|
46
|
+
msgid "Invalid assistant page URL."
|
|
47
|
+
msgstr ""
|
|
48
|
+
|
|
49
|
+
#. Default: "Submit question"
|
|
50
|
+
#: AISearchInput/components/SearchInput
|
|
51
|
+
#: AISearchInput/messages
|
|
52
|
+
msgid "Submit question"
|
|
53
|
+
msgstr ""
|
|
54
|
+
|
|
55
|
+
#. Default: "This assistant uses AI. Responses may contain inaccuracies."
|
|
56
|
+
#: AISearchInput/messages
|
|
57
|
+
msgid "This assistant uses AI. Responses may contain inaccuracies."
|
|
58
|
+
msgstr ""
|