diff --git a/docs/whitelist.txt b/docs/whitelist.txt index 0a928cf2..931cd802 100644 --- a/docs/whitelist.txt +++ b/docs/whitelist.txt @@ -1,4 +1,5 @@ index.md layout/index.md +layout/lenses.md dataset/index.md dtypes/index.md diff --git a/renumics/spotlight/__init__.py b/renumics/spotlight/__init__.py index de22c91b..956bd992 100644 --- a/renumics/spotlight/__init__.py +++ b/renumics/spotlight/__init__.py @@ -16,6 +16,7 @@ from .viewer import Viewer, close, viewers, show from .plugin_loader import load_plugins from .settings import settings +from .analysis.typing import DataIssue from . import cache, logging if not settings.verbose: @@ -23,7 +24,7 @@ __plugins__ = load_plugins() -__all__ = ["show", "close", "viewers", "Viewer", "clear_caches"] +__all__ = ["show", "close", "viewers", "Viewer", "clear_caches", "DataIssue"] def clear_caches() -> None: diff --git a/renumics/spotlight/analysis/analyzers/cleanlab.py b/renumics/spotlight/analysis/analyzers/cleanlab.py index 84f2deae..c2c00be4 100644 --- a/renumics/spotlight/analysis/analyzers/cleanlab.py +++ b/renumics/spotlight/analysis/analyzers/cleanlab.py @@ -36,9 +36,9 @@ def analyze_with_cleanlab( if len(rows): yield DataIssue( - severity="medium", title="Outliers in embeddings", rows=rows, + severity="medium", columns=[column_name], description=inspect.cleandoc( """ diff --git a/renumics/spotlight/analysis/analyzers/cleanvision.py b/renumics/spotlight/analysis/analyzers/cleanvision.py index ad7dc289..a6fec43e 100644 --- a/renumics/spotlight/analysis/analyzers/cleanvision.py +++ b/renumics/spotlight/analysis/analyzers/cleanvision.py @@ -4,7 +4,7 @@ import os import inspect -from typing import Iterable, List +from typing import Dict, Iterable, List, Tuple from pathlib import Path from tempfile import TemporaryDirectory from contextlib import redirect_stderr, redirect_stdout @@ -13,14 +13,12 @@ import cleanvision from renumics.spotlight.dtypes import Image - from renumics.spotlight.data_store import DataStore - from ..decorator import data_analyzer -from ..typing import DataIssue +from ..typing import DataIssue, Severity -_issue_types = { +_issue_types: Dict[str, Tuple[str, Severity, str]] = { "is_light_issue": ( "Bright images", "medium", @@ -83,9 +81,9 @@ def _make_issue(cleanvision_key: str, column: str, rows: List[int]) -> DataIssue: title, severity, description = _issue_types[cleanvision_key] return DataIssue( - severity=severity, title=title, rows=rows, + severity=severity, columns=[column], description=inspect.cleandoc(description), ) diff --git a/renumics/spotlight/analysis/typing.py b/renumics/spotlight/analysis/typing.py index c725d678..54b2f541 100644 --- a/renumics/spotlight/analysis/typing.py +++ b/renumics/spotlight/analysis/typing.py @@ -4,19 +4,23 @@ from typing import Callable, Iterable, List, Literal, Optional -from pydantic import BaseModel +from pydantic.dataclasses import dataclass from renumics.spotlight.data_store import DataStore -class DataIssue(BaseModel): +Severity = Literal["low", "medium", "high"] + + +@dataclass +class DataIssue: """ An Issue affecting multiple rows of the dataset """ - severity: Literal["low", "medium", "high"] = "medium" title: str rows: List[int] + severity: Severity = "medium" columns: Optional[List[str]] = None description: str = "" diff --git a/renumics/spotlight/dataset/__init__.py b/renumics/spotlight/dataset/__init__.py index 7ea11371..e6e5a866 100644 --- a/renumics/spotlight/dataset/__init__.py +++ b/renumics/spotlight/dataset/__init__.py @@ -95,7 +95,6 @@ video_dtype, ) - from . import exceptions from .typing import ( OutputType, @@ -119,6 +118,10 @@ ColumnInputType, ) + +__all__ = ["Dataset"] + + INTERNAL_COLUMN_NAMES = ["__last_edited_by__", "__last_edited_at__"] INTERNAL_COLUMN_DTYPES = [str_dtype, datetime_dtype] diff --git a/renumics/spotlight/dtypes/__init__.py b/renumics/spotlight/dtypes/__init__.py index 671bb74c..d7acf3ac 100644 --- a/renumics/spotlight/dtypes/__init__.py +++ b/renumics/spotlight/dtypes/__init__.py @@ -7,6 +7,26 @@ from .legacy import Audio, Category, Embedding, Image, Mesh, Sequence1D, Video, Window +__all__ = [ + "CategoryDType", + "Sequence1DDType", + "bool_dtype", + "int_dtype", + "float_dtype", + "str_dtype", + "datetime_dtype", + "category_dtype", + "window_dtype", + "embedding_dtype", + "array_dtype", + "image_dtype", + "audio_dtype", + "mesh_dtype", + "sequence_1d_dtype", + "video_dtype", +] + + class DType: _name: str @@ -22,6 +42,10 @@ def name(self) -> str: class CategoryDType(DType): + """ + Categorical dtype with predefined categories. + """ + _categories: Optional[Dict[str, int]] _inverted_categories: Optional[Dict[int, str]] @@ -57,6 +81,10 @@ def inverted_categories(self) -> Optional[Dict[int, str]]: class Sequence1DDType(DType): + """ + 1D-sequence dtype with predefined axis labels. + """ + x_label: str y_label: str @@ -78,36 +106,52 @@ def register_dtype(dtype: DType, aliases: list) -> None: bool_dtype = DType("bool") +"""Bool dtype""" register_dtype(bool_dtype, [bool]) int_dtype = DType("int") +"""Integer dtype""" register_dtype(int_dtype, [int]) float_dtype = DType("float") +"""Float dtype""" register_dtype(float_dtype, [float]) bytes_dtype = DType("bytes") +"""Bytes dtype""" register_dtype(bytes_dtype, [bytes]) str_dtype = DType("str") +"""String dtype""" register_dtype(str_dtype, [str]) datetime_dtype = DType("datetime") +"""Datetime dtype""" register_dtype(datetime_dtype, [datetime]) category_dtype = CategoryDType() +"""Categorical dtype with arbitraty categories""" register_dtype(category_dtype, [Category]) window_dtype = DType("Window") +"""Window dtype""" register_dtype(window_dtype, [Window]) embedding_dtype = DType("Embedding") +"""Embedding dtype""" register_dtype(embedding_dtype, [Embedding]) array_dtype = DType("array") +"""numpy array dtype""" register_dtype(array_dtype, [np.ndarray]) image_dtype = DType("Image") +"""Image dtype""" register_dtype(image_dtype, [Image]) audio_dtype = DType("Audio") +"""Audio dtype""" register_dtype(audio_dtype, [Audio]) mesh_dtype = DType("Mesh") +"""Mesh dtype""" register_dtype(mesh_dtype, [Mesh]) sequence_1d_dtype = Sequence1DDType() +"""1D-sequence dtype with arbitraty axis labels""" register_dtype(sequence_1d_dtype, [Sequence1D]) video_dtype = DType("Video") +"""Video dtype""" register_dtype(video_dtype, [Video]) mixed_dtype = DType("mixed") +"""Unknown or mixed dtype""" DTypeMap = Dict[str, DType] diff --git a/renumics/spotlight/io/pandas.py b/renumics/spotlight/io/pandas.py index c09f32f3..b16101b1 100644 --- a/renumics/spotlight/io/pandas.py +++ b/renumics/spotlight/io/pandas.py @@ -80,10 +80,7 @@ def infer_dtype(column: pd.Series) -> dtypes.DType: return dtypes.bool_dtype if pd.api.types.is_categorical_dtype(column): return dtypes.CategoryDType( - { - category: code - for code, category in zip(column.cat.codes, column.cat.categories) - } + {category: code for code, category in enumerate(column.cat.categories)} ) if pd.api.types.is_integer_dtype(column) and not column.hasnans: return dtypes.int_dtype diff --git a/renumics/spotlight/layout/__init__.py b/renumics/spotlight/layout/__init__.py index 1e0261dd..55997761 100644 --- a/renumics/spotlight/layout/__init__.py +++ b/renumics/spotlight/layout/__init__.py @@ -53,6 +53,22 @@ ) +__all__ = [ + "layout", + "split", + "tab", + "histogram", + "inspector", + "scatterplot", + "similaritymap", + "table", + "issues", + "wordcloud", + "confusion_matrix", + "metric", +] + + _WidgetLike = Union[_Widget, str] _NodeLike = Union[Split, Tab, _WidgetLike, List] _LayoutLike = Union[str, os.PathLike, Layout, _NodeLike] diff --git a/renumics/spotlight/layout/lenses.py b/renumics/spotlight/layout/lenses.py index 10c03d59..3263cbb5 100644 --- a/renumics/spotlight/layout/lenses.py +++ b/renumics/spotlight/layout/lenses.py @@ -1,7 +1,7 @@ """ Viewers (lenses) for Spotlight inspector widget. -For usage examples, see `renumics.spotlighth.layout.inspector`. +For usage examples, see `renumics.spotlight.layout.inspector`. """ import uuid diff --git a/renumics/spotlight_plugins/core/pandas_data_source.py b/renumics/spotlight_plugins/core/pandas_data_source.py index dfca286c..4d2bf172 100644 --- a/renumics/spotlight_plugins/core/pandas_data_source.py +++ b/renumics/spotlight_plugins/core/pandas_data_source.py @@ -66,10 +66,7 @@ def __init__(self, source: Union[Path, pd.DataFrame]): for feature_name, feature_type in hf_dataset[ splits[0] ].features.items(): - print(feature_type) if isinstance(feature_type, datasets.ClassLabel): - print(feature_type.names) - print(df[feature_name]) try: df[feature_name] = pd.Categorical.from_codes( df[feature_name], categories=feature_type.names @@ -214,10 +211,7 @@ def _determine_intermediate_dtype(column: pd.Series) -> dtypes.DType: return dtypes.bool_dtype if pd.api.types.is_categorical_dtype(column): return dtypes.CategoryDType( - { - category: code - for code, category in zip(column.cat.codes, column.cat.categories) - } + {category: code for code, category in enumerate(column.cat.categories)} ) if pd.api.types.is_integer_dtype(column): return dtypes.int_dtype diff --git a/src/components/AppBar.tsx b/src/components/AppBar.tsx index 494d77f8..05780480 100644 --- a/src/components/AppBar.tsx +++ b/src/components/AppBar.tsx @@ -64,7 +64,7 @@ const FileBar = () => { onCancel={closeBrowser} cancellable={hasTable} openCaption={openCaption} - extensions={['h5', 'csv']} + extensions={['h5', 'csv', 'parquet', 'feather', 'orc']} /> diff --git a/src/components/FileBrowser/FileBrowser.tsx b/src/components/FileBrowser/FileBrowser.tsx index 23b7bc45..1ef8abf9 100644 --- a/src/components/FileBrowser/FileBrowser.tsx +++ b/src/components/FileBrowser/FileBrowser.tsx @@ -97,7 +97,7 @@ const FileBrowser = ({ diff --git a/src/icons/Array.tsx b/src/icons/Array.tsx index 771e30da..3d02f8a7 100644 --- a/src/icons/Array.tsx +++ b/src/icons/Array.tsx @@ -2,7 +2,7 @@ import type { IconType } from 'react-icons'; import { VscSymbolArray } from 'react-icons/vsc'; import tw from 'twin.macro'; -const Array: IconType = tw( +const ArrayIcon: IconType = tw( VscSymbolArray )`w-4 h-4 font-semibold inline-block align-middle stroke-current`; -export default Array; +export default ArrayIcon; diff --git a/src/widgets/ConfusionMatrix/Matrix.tsx b/src/widgets/ConfusionMatrix/Matrix.tsx index a5a01e5b..ebba5c94 100644 --- a/src/widgets/ConfusionMatrix/Matrix.tsx +++ b/src/widgets/ConfusionMatrix/Matrix.tsx @@ -86,7 +86,7 @@ const Matrix = ({ data, onHoverCell, onClickCell }: Props): JSX.Element => { fill={fgColorCss} fontWeight="bold" > - {data.buckets[i].rows.length > 0 ? data.buckets[i].rows.length : ''} + {data.buckets[i].rows.length} ); diff --git a/src/widgets/DataGrid/Cell/HeaderCell.tsx b/src/widgets/DataGrid/Cell/HeaderCell.tsx index 28720f3d..ac4a57a6 100644 --- a/src/widgets/DataGrid/Cell/HeaderCell.tsx +++ b/src/widgets/DataGrid/Cell/HeaderCell.tsx @@ -4,7 +4,7 @@ import Tooltip from '../../../components/ui/Tooltip'; import dataformat from '../../../dataformat'; import { useColorTransferFunction } from '../../../hooks/useColorTransferFunction'; import * as React from 'react'; -import { FunctionComponent, useCallback, useMemo } from 'react'; +import { FunctionComponent, useCallback, useContext, useMemo } from 'react'; import { HiChevronDown, HiChevronUp } from 'react-icons/hi'; import type { GridChildComponentProps as CellProps } from 'react-window'; import { Dataset, Sorting, useDataset } from '../../../stores/dataset'; @@ -12,6 +12,7 @@ import tw from 'twin.macro'; import { useColumn } from '../context/columnContext'; import { useSortByColumn } from '../context/sortingContext'; import RelevanceIndicator from '../RelevanceIndicator'; +import { ResizingContext } from '../context/resizeContext'; interface SortingIndicatorProps { sorting?: Sorting; @@ -39,17 +40,14 @@ const stopPropagation: React.MouseEventHandler = (event) => { event.stopPropagation(); }; -type ItemData = { - onStartResize: (columnIndex: number) => void; - resizedIndex?: number; -}; - -type Props = CellProps; +type Props = CellProps; -const HeaderCell: FunctionComponent = ({ data, style, columnIndex }) => { +const HeaderCell: FunctionComponent = ({ style, columnIndex }) => { const column = useColumn(columnIndex); const [columnSorting, sortBy, resetSorting] = useSortByColumn(column.key); + const { startResizing, resizedIndex } = useContext(ResizingContext); + const tags = useDataset(tagsSelector); const tagColorTransferFunction = useColorTransferFunction(tags, { kind: 'str', @@ -130,9 +128,9 @@ const HeaderCell: FunctionComponent = ({ data, style, columnIndex }) => { const onStartResize: React.MouseEventHandler = useCallback( (event) => { stopPropagation(event); - data.onStartResize(columnIndex); + startResizing(columnIndex); }, - [columnIndex, data] + [columnIndex, startResizing] ); return ( @@ -163,7 +161,7 @@ const HeaderCell: FunctionComponent = ({ data, style, columnIndex }) => { onMouseDown={onStartResize} css={[ tw`h-full w-[3px] border-r bg-none hover:border-r-0 hover:bg-gray-400 transition transform cursor-col-resize`, - data.resizedIndex === columnIndex && tw`bg-gray-400 border-r-0`, + resizedIndex.current === columnIndex && tw`bg-gray-400 border-r-0`, ]} /> diff --git a/src/widgets/DataGrid/DataGrid.tsx b/src/widgets/DataGrid/DataGrid.tsx index 6011054f..7ae06aff 100644 --- a/src/widgets/DataGrid/DataGrid.tsx +++ b/src/widgets/DataGrid/DataGrid.tsx @@ -1,7 +1,7 @@ import 'twin.macro'; import TableIcon from '../../icons/Table'; import { Widget } from '../types'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useMemo, useRef } from 'react'; import AutoSizer from 'react-virtualized-auto-sizer'; import { VariableSizeGrid as Grid } from 'react-window'; import { Dataset, Sorting, useDataset } from '../../stores/dataset'; @@ -16,10 +16,8 @@ import HeaderGrid from './HeaderGrid'; import MenuBar from './MenuBar'; import TableGrid, { Ref as TableGridRef } from './TableGrid'; import GridContextMenu from './GridContextMenu'; -import columnWidthByType from './columnWidthByType'; import { WidgetContainer, WidgetContent } from '../../lib'; - -const MIN_COLUMN_WIDTH = 50; +import { ColumnResizeProvider } from './context/resizeContext'; const headerHeight = 24; @@ -50,73 +48,12 @@ const DataGrid: Widget = () => { 'visibleColumns', defaultVisibleColumnKeys ); - const [columnWidths, persistColumnWidths] = useWidgetConfig>( - 'columnWidths', - allColumns.reduce((acc: Record, column: DataColumn) => { - acc[column.key] = columnWidthByType[column.type.kind]; - return acc; - }, {} as Record) - ); - - const columnWidthsRef = useRef(columnWidths); - - useEffect(() => { - columnWidthsRef.current = columnWidths; - }, [columnWidths]); const resetGridsAfterIndex = useCallback((index: number) => { tableGrid.current?.resetAfterColumnIndex(index); headerGrid.current?.resetAfterColumnIndex(index); }, []); - const [resizedIndex, setResizedIndex] = useState(); - const lastResizePosition = useRef(null); - - const onStartResize = useCallback( - (columnIndex: number) => { - const columnKey = visibleColumns[columnIndex]; - const onMouseMoveWhileResize = (event: MouseEvent) => { - if (lastResizePosition.current !== null) { - const delta = event.clientX - lastResizePosition.current; - if (delta > 0 || delta < 0) { - const oldWidths = columnWidthsRef.current; - const newColumnWidth = Math.max( - MIN_COLUMN_WIDTH, - oldWidths[columnKey] + delta - ); - const newWidths = { - ...oldWidths, - [columnKey]: newColumnWidth, - }; - columnWidthsRef.current = newWidths; - resetGridsAfterIndex(columnIndex); - } - } else { - setResizedIndex(columnIndex); - } - lastResizePosition.current = event.clientX; - }; - - window.addEventListener('mousemove', onMouseMoveWhileResize); - - const onMouseUpWhileResize = () => { - window.removeEventListener('mousemove', onMouseMoveWhileResize); - window.removeEventListener('mouseup', onMouseUpWhileResize); - lastResizePosition.current = null; - setResizedIndex(undefined); - persistColumnWidths(columnWidthsRef.current); - }; - - window.addEventListener('mouseup', onMouseUpWhileResize); - }, - [visibleColumns, resetGridsAfterIndex, persistColumnWidths] - ); - - const columnWidth = useCallback( - (index: number) => columnWidthsRef.current[visibleColumns[index]], - [visibleColumns] - ); - const resetVisibleColumns = useCallback( () => setVisibleColumns(defaultVisibleColumnKeys), [defaultVisibleColumnKeys, setVisibleColumns] @@ -145,36 +82,34 @@ const DataGrid: Widget = () => { setAreOrderedByRelevance={setAreOrderedByRelevance} resetColumns={resetVisibleColumns} > - - - - - {({ width, height }) => ( -
- -
- + + + + + {({ width, height }) => ( +
+ +
+ +
+ -
- - -
- )} - - - +
+
+ )} +
+
+
+ diff --git a/src/widgets/DataGrid/HeaderGrid.tsx b/src/widgets/DataGrid/HeaderGrid.tsx index ccb38225..81f1f1e4 100644 --- a/src/widgets/DataGrid/HeaderGrid.tsx +++ b/src/widgets/DataGrid/HeaderGrid.tsx @@ -1,26 +1,26 @@ import usePrevious from '../../hooks/usePrevious'; import * as React from 'react'; -import { useCallback, useEffect } from 'react'; +import { useCallback, useContext, useEffect } from 'react'; import { VariableSizeGrid as Grid } from 'react-window'; import 'twin.macro'; import HeaderCell from './Cell/HeaderCell'; import { useColumnCount, useVisibleColumns } from './context/columnContext'; +import { ResizingContext } from './context/resizeContext'; interface Props { height: number; width: number; - columnWidth: (index: number) => number; - onStartResize: (columnIndex: number) => void; - resizedIndex?: number; } const HeaderGrid: React.ForwardRefRenderFunction = ( - { height, width, columnWidth, onStartResize, resizedIndex }, + { height, width }, ref ) => { const rowHeight = useCallback(() => height, [height]); const columnCount = useColumnCount(); + const { getColumnWidth } = useContext(ResizingContext); + const [displayedColumns] = useVisibleColumns(); const previousColumnKeys = usePrevious(displayedColumns.map(({ key }) => key)); @@ -39,12 +39,11 @@ const HeaderGrid: React.ForwardRefRenderFunction = ( return ( number; } export type Ref = { @@ -31,7 +32,7 @@ export type Ref = { }; const TableGrid: ForwardRefRenderFunction = ( - { width, height, onScroll, columnWidth }, + { width, height, onScroll }, fwdRef ) => { const ref = useRef(null); @@ -46,6 +47,8 @@ const TableGrid: ForwardRefRenderFunction = ( const [displayedColumns] = useVisibleColumns(); + const { getColumnWidth } = useContext(ResizingContext); + const { getOriginalIndex } = useSort(); const rowCount = useRowCount(); @@ -96,7 +99,7 @@ const TableGrid: ForwardRefRenderFunction = ( height={height} columnCount={columnCount} rowCount={Math.max(1, rowCount)} - columnWidth={columnWidth} + columnWidth={getColumnWidth} estimatedColumnWidth={100} rowHeight={getRowHeight} itemKey={rowCount ? itemKey : undefined} diff --git a/src/widgets/DataGrid/context/resizeContext.tsx b/src/widgets/DataGrid/context/resizeContext.tsx new file mode 100644 index 00000000..7c46eb85 --- /dev/null +++ b/src/widgets/DataGrid/context/resizeContext.tsx @@ -0,0 +1,115 @@ +import * as React from 'react'; +import { MutableRefObject, useCallback, useMemo, useRef } from 'react'; +import { useColumns } from './columnContext'; +import columnWidthByType from '../columnWidthByType'; +import { useWidgetConfig } from '../../../hooks'; + +const MIN_COLUMN_WIDTH = 50; + +type EditingContextState = { + getColumnWidth: (index: number) => number; + startResizing: (columnIndex: number) => void; + resizedIndex: MutableRefObject; +}; + +export const ResizingContext = React.createContext({ + getColumnWidth: () => MIN_COLUMN_WIDTH, + startResizing: () => undefined, + resizedIndex: { current: undefined }, +}); + +interface Props { + children: React.ReactNode; + onResize: (columnIndex: number) => void; +} + +export const ColumnResizeProvider = ({ children, onResize }: Props) => { + const columns = useColumns(); + + const defaultColumnWidths = useMemo( + () => + columns.reduce((acc, column) => { + acc[column.key] = columnWidthByType[column.type.kind]; + return acc; + }, {} as Record), + [columns] + ); + + const resizedIndex = useRef(); + const lastResizePosition = useRef(null); + + const [columnWidths, _persistColumnWidths] = useWidgetConfig< + Record + >('dataGridColumnWidths', {}); + + const columnWidthsRef = useRef>({}); + + const persistColumnWidths = useCallback(() => { + _persistColumnWidths((oldWidths) => ({ + ...oldWidths, + ...columnWidthsRef.current, + })); + }, [_persistColumnWidths]); + + const onStartResize = useCallback( + (columnIndex: number) => { + const columnKey = columns[columnIndex].key; + const onMouseMoveWhileResize = (event: MouseEvent) => { + if (lastResizePosition.current !== null) { + const delta = event.clientX - lastResizePosition.current; + if (delta > 0 || delta < 0) { + const currentWidth = + columnWidthsRef.current[columnKey] || + defaultColumnWidths[columnKey] || + MIN_COLUMN_WIDTH; + columnWidthsRef.current[columnKey] = Math.max( + MIN_COLUMN_WIDTH, + currentWidth + delta + ); + onResize(columnIndex); + } + } else { + resizedIndex.current = columnIndex; + } + lastResizePosition.current = event.clientX; + }; + + window.addEventListener('mousemove', onMouseMoveWhileResize); + + const onMouseUpWhileResize = () => { + window.removeEventListener('mousemove', onMouseMoveWhileResize); + window.removeEventListener('mouseup', onMouseUpWhileResize); + lastResizePosition.current = null; + resizedIndex.current = undefined; + persistColumnWidths(); + }; + + window.addEventListener('mouseup', onMouseUpWhileResize); + }, + [columns, defaultColumnWidths, onResize, persistColumnWidths] + ); + + const getColumnWidth = useCallback( + (index: number) => { + const columnKey = columns[index]?.key; + if (columnKey) { + return ( + columnWidthsRef.current[columnKey] || + columnWidths[columnKey] || + defaultColumnWidths[columnKey] || + MIN_COLUMN_WIDTH + ); + } + return 0; + }, + [columnWidths, columns, defaultColumnWidths] + ); + + return ( + + {children} + + ); +}; diff --git a/src/widgets/IssuesWidget.tsx b/src/widgets/IssuesWidget.tsx index 301ce3f6..2b15fc4c 100644 --- a/src/widgets/IssuesWidget.tsx +++ b/src/widgets/IssuesWidget.tsx @@ -80,7 +80,9 @@ const Issue = ({ issue }: IssueProps): JSX.Element => { {collapsed ? : } } - +
+ +
{
@@ -136,7 +138,7 @@ const IssuesWidget: Widget = () => { { // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/interactive-supports-focus
{ }, }, rollupOptions: { + input: ['src/main.tsx', 'src/lib.ts'], output: { name: 'Spotlight', },