mirror of
https://github.com/docmost/docmost.git
synced 2026-08-25 15:57:11 +08:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efc113de3e | ||
|
|
cd9c166927 | ||
|
|
549cf7c005 | ||
|
|
e14f499f3d | ||
|
|
b814bd0f12 | ||
|
|
b86abd3d40 | ||
|
|
3b858746e3 | ||
|
|
66b424a3b8 |
@@ -9,7 +9,6 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
UnstyledButton,
|
UnstyledButton,
|
||||||
Button,
|
Button,
|
||||||
MultiSelect,
|
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { IconPlus, IconTrash } from "@tabler/icons-react";
|
import { IconPlus, IconTrash } from "@tabler/icons-react";
|
||||||
import {
|
import {
|
||||||
@@ -53,9 +52,6 @@ const NO_VALUE_OPERATORS: FilterOperator[] = ["isEmpty", "isNotEmpty"];
|
|||||||
// stored value so a stale shape isn't sent to the engine.
|
// stored value so a stale shape isn't sent to the engine.
|
||||||
function valueClass(op: FilterOperator, inputKind: string): string {
|
function valueClass(op: FilterOperator, inputKind: string): string {
|
||||||
if (NO_VALUE_OPERATORS.includes(op)) return "none";
|
if (NO_VALUE_OPERATORS.includes(op)) return "none";
|
||||||
if (inputKind === "choices") {
|
|
||||||
return op === "any" || op === "none" ? "choicesMulti" : "choicesSingle";
|
|
||||||
}
|
|
||||||
if (inputKind === "person") {
|
if (inputKind === "person") {
|
||||||
return op === "any" || op === "none" ? "personMulti" : "personSingle";
|
return op === "any" || op === "none" ? "personMulti" : "personSingle";
|
||||||
}
|
}
|
||||||
@@ -74,10 +70,6 @@ function getOperatorsForType(type: string): FilterOperator[] {
|
|||||||
DEFAULT_FILTER_OPERATORS) as FilterOperator[];
|
DEFAULT_FILTER_OPERATORS) as FilterOperator[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function isMultiChoice(op: FilterCondition["op"]): boolean {
|
|
||||||
return op === "any" || op === "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
function FilterValueInput({
|
function FilterValueInput({
|
||||||
condition,
|
condition,
|
||||||
property,
|
property,
|
||||||
@@ -129,32 +121,6 @@ function FilterValueInput({
|
|||||||
const typeOptions = property.typeOptions as SelectTypeOptions | undefined;
|
const typeOptions = property.typeOptions as SelectTypeOptions | undefined;
|
||||||
const choices = typeOptions?.choices ?? [];
|
const choices = typeOptions?.choices ?? [];
|
||||||
const choiceOptions = choices.map((c) => ({ value: c.id, label: c.name }));
|
const choiceOptions = choices.map((c) => ({ value: c.id, label: c.name }));
|
||||||
|
|
||||||
if (isMultiChoice(condition.op)) {
|
|
||||||
const { value } = condition;
|
|
||||||
const selected = (
|
|
||||||
Array.isArray(value) ? value : value ? [value] : []
|
|
||||||
).filter((id) => choices.some((c) => c.id === id));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MultiSelect
|
|
||||||
size="xs"
|
|
||||||
data={choiceOptions}
|
|
||||||
comboboxProps={{ withinPortal: false }}
|
|
||||||
value={selected}
|
|
||||||
onChange={(values) => onChange(values)}
|
|
||||||
w={160}
|
|
||||||
styles={{
|
|
||||||
pillsList: {
|
|
||||||
maxHeight: 70,
|
|
||||||
overflowY: "auto",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
maxDropdownHeight={220}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
size="xs"
|
size="xs"
|
||||||
@@ -233,18 +199,11 @@ export function ViewFilterConfigPopover({
|
|||||||
label: p.name,
|
label: p.name,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const [unSaved, setUnSaved] = useState(false)
|
|
||||||
const [draft, setDraft] = useState<FilterCondition | null>(null);
|
const [draft, setDraft] = useState<FilterCondition | null>(null);
|
||||||
const [draftConditions, setDraftConditions] =
|
|
||||||
useState<FilterCondition[]>(conditions);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (opened) {
|
if (!opened) setDraft(null);
|
||||||
setDraftConditions(conditions);
|
}, [opened]);
|
||||||
setDraft(null);
|
|
||||||
setUnSaved(false)
|
|
||||||
}
|
|
||||||
}, [opened, conditions]);
|
|
||||||
|
|
||||||
const handleStartDraft = useCallback(() => {
|
const handleStartDraft = useCallback(() => {
|
||||||
const firstProperty = properties[0];
|
const firstProperty = properties[0];
|
||||||
@@ -257,21 +216,14 @@ export function ViewFilterConfigPopover({
|
|||||||
}, [properties]);
|
}, [properties]);
|
||||||
|
|
||||||
const handleSaveDraft = useCallback(() => {
|
const handleSaveDraft = useCallback(() => {
|
||||||
const nextConditions = draft
|
if (!draft) return;
|
||||||
? [...draftConditions, draft]
|
onChange([...conditions, draft]);
|
||||||
: draftConditions;
|
|
||||||
|
|
||||||
onChange(nextConditions);
|
|
||||||
setDraft(null);
|
setDraft(null);
|
||||||
setUnSaved(false)
|
}, [draft, conditions, onChange]);
|
||||||
|
|
||||||
}, [draft, draftConditions, onChange]);
|
|
||||||
|
|
||||||
const handleCancelDraft = useCallback(() => {
|
const handleCancelDraft = useCallback(() => {
|
||||||
setDraftConditions(conditions)
|
|
||||||
setDraft(null);
|
setDraft(null);
|
||||||
setUnSaved(false)
|
}, []);
|
||||||
}, [conditions]);
|
|
||||||
|
|
||||||
const handleDraftPropertyChange = useCallback(
|
const handleDraftPropertyChange = useCallback(
|
||||||
(propertyId: string | null) => {
|
(propertyId: string | null) => {
|
||||||
@@ -320,19 +272,17 @@ export function ViewFilterConfigPopover({
|
|||||||
|
|
||||||
const handleRemove = useCallback(
|
const handleRemove = useCallback(
|
||||||
(index: number) => {
|
(index: number) => {
|
||||||
setUnSaved(true);
|
onChange(conditions.filter((_, i) => i !== index));
|
||||||
setDraftConditions((current) => current.filter((_, i) => i !== index));
|
|
||||||
},
|
},
|
||||||
[],
|
[conditions, onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handlePropertyChange = useCallback(
|
const handlePropertyChange = useCallback(
|
||||||
(index: number, propertyId: string | null) => {
|
(index: number, propertyId: string | null) => {
|
||||||
if (!propertyId) return;
|
if (!propertyId) return;
|
||||||
const newProperty = properties.find((p) => p.id === propertyId);
|
const newProperty = properties.find((p) => p.id === propertyId);
|
||||||
setUnSaved(true)
|
onChange(
|
||||||
setDraftConditions((current) =>
|
conditions.map((f, i) => {
|
||||||
current.map((f, i) => {
|
|
||||||
if (i !== index) return f;
|
if (i !== index) return f;
|
||||||
if (newProperty) {
|
if (newProperty) {
|
||||||
const validOperators = getOperatorsForType(newProperty.type);
|
const validOperators = getOperatorsForType(newProperty.type);
|
||||||
@@ -352,16 +302,15 @@ export function ViewFilterConfigPopover({
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[properties],
|
[conditions, properties, onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOperatorChange = useCallback(
|
const handleOperatorChange = useCallback(
|
||||||
(index: number, operator: string | null) => {
|
(index: number, operator: string | null) => {
|
||||||
if (!operator) return;
|
if (!operator) return;
|
||||||
const op = operator as FilterOperator;
|
const op = operator as FilterOperator;
|
||||||
setUnSaved(true)
|
onChange(
|
||||||
setDraftConditions((current) =>
|
conditions.map((f, i) => {
|
||||||
current.map((f, i) => {
|
|
||||||
if (i !== index) return f;
|
if (i !== index) return f;
|
||||||
const kind = inputKindForProperty(
|
const kind = inputKindForProperty(
|
||||||
properties.find((p) => p.id === f.propertyId),
|
properties.find((p) => p.id === f.propertyId),
|
||||||
@@ -371,17 +320,16 @@ export function ViewFilterConfigPopover({
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[properties],
|
[conditions, properties, onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleValueChange = useCallback(
|
const handleValueChange = useCallback(
|
||||||
(index: number, value: unknown) => {
|
(index: number, value: unknown) => {
|
||||||
setUnSaved(true)
|
onChange(
|
||||||
setDraftConditions((current) =>
|
conditions.map((f, i) => (i === index ? { ...f, value } : f)),
|
||||||
current.map((f, i) => (i === index ? { ...f, value } : f)),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[],
|
[conditions, onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -414,13 +362,13 @@ export function ViewFilterConfigPopover({
|
|||||||
{t("Filter by")}
|
{t("Filter by")}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{draftConditions.length === 0 && !draft && (
|
{conditions.length === 0 && !draft && (
|
||||||
<Text size="xs" c="dimmed">
|
<Text size="xs" c="dimmed">
|
||||||
{t("No filters applied")}
|
{t("No filters applied")}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{draftConditions.map((condition, index) => {
|
{conditions.map((condition, index) => {
|
||||||
const needsValue = !NO_VALUE_OPERATORS.includes(condition.op);
|
const needsValue = !NO_VALUE_OPERATORS.includes(condition.op);
|
||||||
const property = properties.find(
|
const property = properties.find(
|
||||||
(p) => p.id === condition.propertyId,
|
(p) => p.id === condition.propertyId,
|
||||||
@@ -523,6 +471,14 @@ export function ViewFilterConfigPopover({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
|
<Group justify="flex-end" gap="xs">
|
||||||
|
<Button variant="default" size="xs" onClick={handleCancelDraft}>
|
||||||
|
{t("Cancel")}
|
||||||
|
</Button>
|
||||||
|
<Button size="xs" onClick={handleSaveDraft}>
|
||||||
|
{t("Save")}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
@@ -536,20 +492,6 @@ export function ViewFilterConfigPopover({
|
|||||||
{t("Add filter")}
|
{t("Add filter")}
|
||||||
</UnstyledButton>
|
</UnstyledButton>
|
||||||
)}
|
)}
|
||||||
<Group justify="flex-end" gap="xs">
|
|
||||||
<Button
|
|
||||||
variant="default"
|
|
||||||
size="xs"
|
|
||||||
onClick={handleCancelDraft}
|
|
||||||
disabled={!draft && !unSaved}
|
|
||||||
>
|
|
||||||
{t("Cancel")}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button size="xs" onClick={handleSaveDraft} disabled={!draft && !unSaved}>
|
|
||||||
{t("Save")}
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Popover.Dropdown>
|
</Popover.Dropdown>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|||||||
@@ -396,7 +396,10 @@ export class WorkspaceService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateWorkspaceDto.aiSearch) {
|
if (
|
||||||
|
updateWorkspaceDto.aiSearch &&
|
||||||
|
this.environmentService.getAiVectorDriver() !== 'turbopuffer'
|
||||||
|
) {
|
||||||
const tableExists = await isPageEmbeddingsTableExists(this.db);
|
const tableExists = await isPageEmbeddingsTableExists(this.db);
|
||||||
if (!tableExists) {
|
if (!tableExists) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
|
|||||||
+1
-1
Submodule apps/server/src/ee updated: e13af0ce05...6dfbcb9241
@@ -97,6 +97,15 @@ export function xwikiFormatter($: CheerioAPI, $root: Cheerio<any>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isBareLink($el: Cheerio<any>): boolean {
|
||||||
|
const href = $el.attr("href")?.trim();
|
||||||
|
const text = $el.text().trim();
|
||||||
|
|
||||||
|
if(!text || !href) return false
|
||||||
|
|
||||||
|
return text === href;
|
||||||
|
}
|
||||||
|
|
||||||
export function defaultHtmlFormatter($: CheerioAPI, $root: Cheerio<any>) {
|
export function defaultHtmlFormatter($: CheerioAPI, $root: Cheerio<any>) {
|
||||||
normalizeTableColumnWidths($, $root);
|
normalizeTableColumnWidths($, $root);
|
||||||
|
|
||||||
@@ -104,7 +113,9 @@ export function defaultHtmlFormatter($: CheerioAPI, $root: Cheerio<any>) {
|
|||||||
const $el = $(el);
|
const $el = $(el);
|
||||||
const url = $el.attr('href')!;
|
const url = $el.attr('href')!;
|
||||||
const { provider } = getEmbedUrlAndProvider(url);
|
const { provider } = getEmbedUrlAndProvider(url);
|
||||||
if (provider === 'iframe') return;
|
if (provider === 'iframe' || !isBareLink($el)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const embed = `<div data-type=\"embed\" data-src=\"${url}\" data-provider=\"${provider}\" data-align=\"center\" data-width=\"640\" data-height=\"480\"></div>`;
|
const embed = `<div data-type=\"embed\" data-src=\"${url}\" data-provider=\"${provider}\" data-align=\"center\" data-width=\"640\" data-height=\"480\"></div>`;
|
||||||
$el.replaceWith(embed);
|
$el.replaceWith(embed);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ThrottlerModule } from '@nestjs/throttler';
|
|||||||
import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis';
|
import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis';
|
||||||
import { EnvironmentService } from '../environment/environment.service';
|
import { EnvironmentService } from '../environment/environment.service';
|
||||||
import { EnvironmentModule } from '../environment/environment.module';
|
import { EnvironmentModule } from '../environment/environment.module';
|
||||||
import { parseRedisUrl } from '../../common/helpers';
|
import { createRetryStrategy, parseRedisUrl } from '../../common/helpers';
|
||||||
import { AUTH_THROTTLER, AI_CHAT_THROTTLER } from './throttler-names';
|
import { AUTH_THROTTLER, AI_CHAT_THROTTLER } from './throttler-names';
|
||||||
import Redis from 'ioredis';
|
import Redis from 'ioredis';
|
||||||
|
|
||||||
@@ -27,6 +27,8 @@ import Redis from 'ioredis';
|
|||||||
password: redisConfig.password,
|
password: redisConfig.password,
|
||||||
db: redisConfig.db,
|
db: redisConfig.db,
|
||||||
family: redisConfig.family,
|
family: redisConfig.family,
|
||||||
|
tls: redisConfig.tls,
|
||||||
|
retryStrategy: createRetryStrategy(),
|
||||||
keyPrefix: 'throttle:',
|
keyPrefix: 'throttle:',
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -73,9 +73,13 @@ export const embedProviders: IEmbedProvider[] = [
|
|||||||
id: "vimeo",
|
id: "vimeo",
|
||||||
name: "Vimeo",
|
name: "Vimeo",
|
||||||
regex:
|
regex:
|
||||||
/^(https:)?\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|album\/(\d+)\/video\/|video\/|)(\d+)/,
|
/^(https:)?\/\/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|album\/(\d+)\/video\/|video\/|)(\d+)(?:\/([\da-zA-Z]+))?/,
|
||||||
getEmbedUrl: (match) => {
|
getEmbedUrl: (match, url: string) => {
|
||||||
return `https://player.vimeo.com/video/${match[4]}`;
|
// preserve ?h= hash for unlisted videos
|
||||||
|
const hash =
|
||||||
|
match[5] ?? new URL(url, "https://vimeo.com").searchParams.get("h");
|
||||||
|
const base = `https://player.vimeo.com/video/${match[4]}`;
|
||||||
|
return hash ? `${base}?h=${hash}` : base;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user