Skip to content
This repository was archived by the owner on Jul 22, 2025. It is now read-only.

Commit fa07d88

Browse files
committed
FEATURE: Unavailable state for semantic search when sort is not Relevant
1 parent 534b0df commit fa07d88

File tree

4 files changed

+98
-53
lines changed

4 files changed

+98
-53
lines changed

assets/javascripts/discourse/connectors/full-page-search-below-search-header/semantic-search.gjs

Lines changed: 89 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,74 @@ import Component from "@glimmer/component";
22
import { tracked } from "@glimmer/tracking";
33
import { on } from "@ember/modifier";
44
import { action } from "@ember/object";
5-
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
5+
import didUpdate from "@ember/render-modifiers/modifiers/did-update";
66
import { service } from "@ember/service";
7+
import { not } from "truth-helpers";
78
import DToggleSwitch from "discourse/components/d-toggle-switch";
89
import { SEARCH_TYPE_DEFAULT } from "discourse/controllers/full-page-search";
910
import { ajax } from "discourse/lib/ajax";
10-
import { withPluginApi } from "discourse/lib/plugin-api";
1111
import { isValidSearchTerm, translateResults } from "discourse/lib/search";
1212
import icon from "discourse-common/helpers/d-icon";
13-
import I18n from "I18n";
13+
import I18n, { i18n } from "discourse-i18n";
14+
import DTooltip from "float-kit/components/d-tooltip";
1415
import AiIndicatorWave from "../../components/ai-indicator-wave";
1516

1617
export default class SemanticSearch extends Component {
1718
static shouldRender(_args, { siteSettings }) {
1819
return siteSettings.ai_embeddings_semantic_search_enabled;
1920
}
2021

21-
@service router;
2222
@service appEvents;
23+
@service router;
2324
@service siteSettings;
2425
@service searchPreferencesManager;
2526

26-
@tracked searching = false;
27+
@tracked searching;
2728
@tracked AiResults = [];
2829
@tracked showingAiResults = false;
30+
@tracked sortOrder = this.args.outletArgs.sortOrder;
2931
initialSearchTerm = this.args.outletArgs.search;
3032

33+
constructor() {
34+
super(...arguments);
35+
this.appEvents.on("full-page-search:trigger-search", this, this.onSearch);
36+
this.handleSearch();
37+
}
38+
39+
willDestroy() {
40+
super.willDestroy(...arguments);
41+
this.appEvents.off("full-page-search:trigger-search", this, this.onSearch);
42+
}
43+
44+
@action
45+
onSearch() {
46+
if (!this.searching) {
47+
this.resetAiResults();
48+
return this.performHyDESearch();
49+
}
50+
}
51+
3152
get disableToggleSwitch() {
3253
if (
3354
this.searching ||
3455
this.AiResults.length === 0 ||
35-
this.args.outletArgs.sortOrder !== 0
56+
!this.validSearchOrder
3657
) {
3758
return true;
3859
}
3960
}
4061

62+
get validSearchOrder() {
63+
return this.sortOrder === 0;
64+
}
65+
4166
get searchStateText() {
67+
if (!this.validSearchOrder) {
68+
return I18n.t(
69+
"discourse_ai.embeddings.semantic_search_results.unavailable"
70+
);
71+
}
72+
4273
// Search results:
4374
if (this.AiResults.length > 0) {
4475
if (this.showingAiResults) {
@@ -89,7 +120,7 @@ export default class SemanticSearch extends Component {
89120
return (
90121
this.args.outletArgs.type === SEARCH_TYPE_DEFAULT &&
91122
isValidSearchTerm(this.searchTerm, this.siteSettings) &&
92-
this.args.outletArgs.sortOrder === 0
123+
this.validSearchOrder
93124
);
94125
}
95126

@@ -116,15 +147,13 @@ export default class SemanticSearch extends Component {
116147
return;
117148
}
118149

119-
if (this.initialSearchTerm && !this.searching) {
150+
if (this.initialSearchTerm) {
151+
this.searching = true;
120152
return this.performHyDESearch();
121153
}
122-
123-
this.#resetAndSearchOnEvent();
124154
}
125155

126156
performHyDESearch() {
127-
this.searching = true;
128157
this.resetAiResults();
129158

130159
ajax("/discourse-ai/embeddings/semantic-search", {
@@ -134,7 +163,6 @@ export default class SemanticSearch extends Component {
134163
const model = (await translateResults(results)) || {};
135164

136165
if (model.posts?.length === 0) {
137-
this.searching = false;
138166
return;
139167
}
140168

@@ -144,56 +172,65 @@ export default class SemanticSearch extends Component {
144172

145173
this.AiResults = model.posts;
146174
})
147-
.finally(() => (this.searching = false));
148-
}
149-
150-
#resetAndSearchOnEvent() {
151-
return withPluginApi("1.15.0", (api) => {
152-
api.onAppEvent("full-page-search:trigger-search", () => {
153-
if (!this.searching) {
154-
this.resetAiResults();
155-
return this.performHyDESearch();
156-
}
175+
.finally(() => {
176+
this.searching = false;
157177
});
158-
});
159178
}
160179

161180
@action
162-
checkQueryParamsAndSearch() {
163-
// This check is necessary because handleSearch() isn't called
164-
// if query params are present and a new search has appended text.
165-
// It ensures AiResults are reset and searched for properly
166-
const searchQueryParam = this.router.currentRoute?.queryParams?.q;
167-
if (searchQueryParam) {
168-
this.#resetAndSearchOnEvent();
181+
sortChanged() {
182+
if (this.sortOrder !== this.args.outletArgs.sortOrder) {
183+
this.sortOrder = this.args.outletArgs.sortOrder;
184+
185+
if (this.validSearchOrder) {
186+
this.handleSearch();
187+
} else {
188+
this.showingAiResults = false;
189+
this.resetAiResults();
190+
}
169191
}
170192
}
171193

172194
<template>
173-
<span {{didInsert this.checkQueryParamsAndSearch}}></span>
174-
{{#if this.searchEnabled}}
175-
<div class="semantic-search__container search-results" role="region">
176-
<div class="semantic-search__results" {{didInsert this.handleSearch}}>
177-
<div
178-
class="semantic-search__searching
179-
{{if this.searching 'in-progress'}}"
180-
>
181-
<DToggleSwitch
182-
disabled={{this.disableToggleSwitch}}
183-
@state={{this.showingAiResults}}
184-
class="semantic-search__results-toggle"
185-
{{on "click" this.toggleAiResults}}
186-
/>
187-
188-
<div class="semantic-search__searching-text">
189-
{{icon "discourse-sparkles"}}
190-
{{this.searchStateText}}
191-
</div>
192-
193-
<AiIndicatorWave @loading={{this.searching}} />
195+
<span {{didUpdate this.sortChanged @outletArgs.sortOrder}}></span>
196+
<div class="semantic-search__container search-results" role="region">
197+
<div class="semantic-search__results">
198+
<div
199+
class="semantic-search__searching {{if this.searching 'in-progress'}}"
200+
>
201+
<DToggleSwitch
202+
disabled={{this.disableToggleSwitch}}
203+
@state={{this.showingAiResults}}
204+
class="semantic-search__results-toggle"
205+
{{on "click" this.toggleAiResults}}
206+
/>
207+
208+
<div class="semantic-search__searching-text">
209+
{{icon "discourse-sparkles"}}
210+
{{this.searchStateText}}
194211
</div>
212+
213+
<AiIndicatorWave @loading={{this.searching}} />
214+
215+
{{#if (not this.validSearchOrder)}}
216+
217+
<DTooltip
218+
@identifier="semantic-search-unavailable-tooltip"
219+
class="semantic-search__unavailable-tooltip"
220+
...attributes
221+
>
222+
<:trigger>
223+
{{icon "far-circle-question"}}
224+
</:trigger>
225+
<:content>
226+
{{i18n
227+
"discourse_ai.embeddings.semantic_search_unavailable_tooltip"
228+
}}
229+
</:content>
230+
</DTooltip>
231+
{{/if}}
195232
</div>
196233
</div>
197-
{{/if}}
234+
</div>
198235
</template>
199236
}

assets/stylesheets/modules/embeddings/common/semantic-search.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@
2525

2626
&__searching-text {
2727
display: inline-block;
28-
margin-left: 3px;
28+
margin-left: 8px;
29+
}
30+
31+
&__unavailable-tooltip {
32+
margin-left: 4px;
2933
}
3034
}
3135

config/locales/client.en.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,8 @@ en:
483483
toggle_hidden: "Hiding %{count} results found using AI"
484484
none: "Sorry, our AI search found no matching topics"
485485
new: "Press 'search' to begin looking for new results with AI"
486+
unavailable: "AI results unavailable"
487+
semantic_search_unavailable_tooltip: "Search results must be sorted by Relevance to display AI results."
486488
ai_generated_result: "Search result found using AI"
487489
quick_search:
488490
suffix: "in all topics and posts with AI"

plugin.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343

4444
register_asset "stylesheets/modules/ai-bot/common/ai-artifact.scss"
4545

46+
register_svg_icon "far-circle-question" if respond_to?(:register_svg_icon)
47+
4648
module ::DiscourseAi
4749
PLUGIN_NAME = "discourse-ai"
4850

0 commit comments

Comments
 (0)