Compare commits

..

1 Commits

Author SHA1 Message Date
Sylvain Boissel 270b374a17 📝(doc) fix publiccode.yml syntax
This fixes publiccode.yml according to the 0.5.0 syntax: remove or rename
non-existing fields, add the missing mandatory ones, fix a few typos.
2026-01-05 17:46:05 +01:00
9 changed files with 48 additions and 142 deletions
+1 -2
View File
@@ -17,13 +17,12 @@ and this project adheres to
- 🥅(frontend) intercept 401 error on GET threads #1754
- 🦺(frontend) check content type pdf on PdfBlock #1756
- ✈️(frontend) pause Posthog when offline #1755
- 📱(frontend) toolbar to the bottom when mobile #1774
### Fixed
- 🐛(frontend) fix tables deletion #1752
- 🐛(frontend) fix children not display when first resize #1753
- 📝(doc) fix publiccode.yml syntax #1770
## [4.2.0] - 2025-12-17
+16 -6
View File
@@ -1,16 +1,18 @@
publiccodeYmlVersion: "2.4.0"
publiccodeYmlVersion: "0.5.0"
name: Docs
url: https://github.com/suitenumerique/docs
landingURL: https://github.com/suitenumerique/docs
creationDate: 2023-12-10
logo: https://raw.githubusercontent.com/suitenumerique/docs/main/docs/assets/docs-logo.png
usedBy:
- Direction interministériel du numérique (DINUM)
- Direction interministérielle du numérique (DINUM)
fundedBy:
- name: Direction interministériel du numérique (DINUM)
url: https://www.numerique.gouv.fr
- name: Direction interministérielle du numérique (DINUM)
uri: https://www.numerique.gouv.fr
roadmap: "https://github.com/orgs/suitenumerique/projects/2/views/1"
softwareType: "standalone/other"
platforms:
- "web"
developmentStatus: "stable"
description:
en:
shortDescription: "The open source document editor where your notes can become knowledge through live collaboration"
@@ -18,10 +20,18 @@ description:
shortDescription: "L'éditeur de documents open source où vos notes peuvent devenir des connaissances grâce à la collaboration en direct."
legal:
license: MIT
localisation:
localisationReady: true
availableLanguages:
- de
- en
- es
- fr
- nl
maintenance:
type: internal
contacts:
- name: "Virgile Deville"
email: "virgile.deville@numerique.gouv.fr"
- name: "samuel.paccoud"
- name: "Samuel Paccoud"
email: "samuel.paccoud@numerique.gouv.fr"
@@ -52,17 +52,14 @@ export function AppProvider({ children }: { children: React.ReactNode }) {
const { theme } = useCunninghamTheme();
const { replace } = useRouter();
const { initializeResizeListener, initializeInputDetection } =
useResponsiveStore();
const initializeResizeListener = useResponsiveStore(
(state) => state.initializeResizeListener,
);
useEffect(() => {
return initializeResizeListener();
}, [initializeResizeListener]);
useEffect(() => {
return initializeInputDetection();
}, [initializeInputDetection]);
/**
* Update the global router replace function
* This allows us to use the router replace function globally
@@ -1,26 +1,15 @@
import { FormattingToolbarExtension } from '@blocknote/core/extensions';
import {
ExperimentalMobileFormattingToolbarController,
FormattingToolbar,
FormattingToolbarController,
blockTypeSelectItems,
getFormattingToolbarItems,
useBlockNoteEditor,
useDictionary,
useExtensionState,
} from '@blocknote/react';
import React, { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Box } from '@/components';
import { useConfig } from '@/core/config/api';
import { useResponsiveStore } from '@/stores';
import {
DocsBlockSchema,
DocsInlineContentSchema,
DocsStyleSchema,
} from '../../types';
import { CommentToolbarButton } from '../comments/CommentToolbarButton';
import { getCalloutFormattingToolbarItems } from '../custom-blocks';
@@ -35,7 +24,6 @@ export const BlockNoteToolbar = () => {
const [onConfirm, setOnConfirm] = useState<() => void | Promise<void>>();
const { t } = useTranslation();
const { data: conf } = useConfig();
const { isTablet, isInputTouch } = useResponsiveStore();
const toolbarItems = useMemo(() => {
let toolbarItems = getFormattingToolbarItems([
@@ -96,13 +84,7 @@ export const BlockNoteToolbar = () => {
return (
<>
{isInputTouch && isTablet ? (
<MobileFormattingToolbarController
formattingToolbar={formattingToolbar}
/>
) : (
<FormattingToolbarController formattingToolbar={formattingToolbar} />
)}
<FormattingToolbarController formattingToolbar={formattingToolbar} />
{confirmOpen && (
<ModalConfirmDownloadUnsafe
onClose={() => setIsConfirmOpen(false)}
@@ -112,38 +94,3 @@ export const BlockNoteToolbar = () => {
</>
);
};
const MobileFormattingToolbarController = ({
formattingToolbar,
}: {
formattingToolbar: () => React.ReactNode;
}) => {
const editor = useBlockNoteEditor<
DocsBlockSchema,
DocsInlineContentSchema,
DocsStyleSchema
>();
const show = useExtensionState(FormattingToolbarExtension, {
editor,
});
if (!show) {
return null;
}
return (
<Box
$position="absolute"
$css={`
& > div {
left: 50%;
transform: translate(0px, 0px) scale(1) translateX(-50%)!important;
}
`}
>
<ExperimentalMobileFormattingToolbarController
formattingToolbar={formattingToolbar}
/>
</Box>
);
};
@@ -74,9 +74,7 @@ export function MarkdownButton() {
};
const show = useMemo(() => {
return (
selectedBlocks.filter((block) => block.content !== undefined).length !== 0
);
return !!selectedBlocks.find((block) => block.content !== undefined);
}, [selectedBlocks]);
if (!show || !editor.isEditable || !Components) {
@@ -59,7 +59,11 @@ interface PdfBlockComponentProps {
>;
}
const PdfBlockComponent = ({ editor, block }: PdfBlockComponentProps) => {
const PdfBlockComponent = ({
editor,
block,
contentRef,
}: PdfBlockComponentProps) => {
const pdfUrl = block.props.url;
const { i18n, t } = useTranslation();
const lang = i18n.resolvedLanguage;
@@ -110,34 +114,27 @@ const PdfBlockComponent = ({ editor, block }: PdfBlockComponentProps) => {
void validatePDFContent();
}, [pdfUrl]);
if (isPDFContentLoading) {
return <Loading />;
}
if (!isPDFContentLoading && isPDFContent !== null && !isPDFContent) {
return (
<Box
$align="center"
$justify="center"
$color="#666"
$background="#f5f5f5"
$border="1px solid #ddd"
$height="300px"
$css={css`
text-align: center;
`}
$width="100%"
contentEditable={false}
onClick={() => editor.setTextCursorPosition(block)}
>
{t('Invalid or missing PDF file.')}
</Box>
);
}
return (
<>
<Box ref={contentRef} className="bn-file-block-content-wrapper">
<PDFBlockStyle />
{isPDFContentLoading && <Loading />}
{!isPDFContentLoading && isPDFContent !== null && !isPDFContent && (
<Box
$align="center"
$justify="center"
$color="#666"
$background="#f5f5f5"
$border="1px solid #ddd"
$height="300px"
$css={css`
text-align: center;
`}
contentEditable={false}
onClick={() => editor.setTextCursorPosition(block)}
>
{t('Invalid or missing PDF file.')}
</Box>
)}
<ResizableFileBlockWrapper
buttonIcon={
<Icon iconName="upload" $size="24px" $css="line-height: normal;" />
@@ -161,7 +158,7 @@ const PdfBlockComponent = ({ editor, block }: PdfBlockComponentProps) => {
/>
)}
</ResizableFileBlockWrapper>
</>
</Box>
);
};
@@ -180,9 +180,6 @@ export const cssEditor = css`
& .bn-editor {
padding-right: 36px;
}
& .bn-toolbar {
max-width: 100vw;
}
}
@media screen and (width <= 560px) {
@@ -120,7 +120,7 @@ const MainContent = ({
$css={css`
overflow-y: auto;
overflow-x: clip;
&:focus-visible {
&:focus {
outline: 3px solid ${colorsTokens['brand-400']};
outline-offset: -3px;
}
@@ -1,7 +1,6 @@
import { create } from 'zustand';
export type ScreenSize = 'small-mobile' | 'mobile' | 'tablet' | 'desktop';
export type InputMethod = 'touch' | 'mouse' | 'unknown';
export interface UseResponsiveStore {
isMobile: boolean;
@@ -11,11 +10,7 @@ export interface UseResponsiveStore {
screenWidth: number;
setScreenSize: (size: ScreenSize) => void;
isDesktop: boolean;
isTouchCapable: boolean;
isInputTouch: boolean;
inputMethod: InputMethod;
initializeResizeListener: () => () => void;
initializeInputDetection: () => () => void;
}
const initialState = {
@@ -25,9 +20,6 @@ const initialState = {
isDesktop: false,
screenSize: 'desktop' as ScreenSize,
screenWidth: 0,
isTouchCapable: false,
isInputTouch: false,
inputMethod: 'unknown' as InputMethod,
};
export const useResponsiveStore = create<UseResponsiveStore>((set) => ({
@@ -37,9 +29,6 @@ export const useResponsiveStore = create<UseResponsiveStore>((set) => ({
isTablet: initialState.isTablet,
screenSize: initialState.screenSize,
screenWidth: initialState.screenWidth,
isTouchCapable: initialState.isTouchCapable,
isInputTouch: initialState.isInputTouch,
inputMethod: initialState.inputMethod,
setScreenSize: (size: ScreenSize) => set(() => ({ screenSize: size })),
initializeResizeListener: () => {
const resizeHandler = () => {
@@ -95,32 +84,4 @@ export const useResponsiveStore = create<UseResponsiveStore>((set) => ({
window.removeEventListener('resize', debouncedResizeHandler);
};
},
initializeInputDetection: () => {
// Detect if device has touch capability
const isTouchCapable =
'ontouchstart' in window ||
navigator.maxTouchPoints > 0 ||
// @ts-ignore - for older browsers
navigator.msMaxTouchPoints > 0;
set({ isTouchCapable });
// Track actual input method being used
const handleTouchStart = () => {
set({ inputMethod: 'touch', isInputTouch: true });
};
const handleMouseMove = () => {
set({ inputMethod: 'mouse', isInputTouch: false });
};
// Listen for first interaction to determine input method
window.addEventListener('touchstart', handleTouchStart, { once: false });
window.addEventListener('mousemove', handleMouseMove, { once: false });
return () => {
window.removeEventListener('touchstart', handleTouchStart);
window.removeEventListener('mousemove', handleMouseMove);
};
},
}));