[Astal] Notifications: Delete not working version
This commit is contained in:
@@ -2,7 +2,7 @@ import { bind } from "astal";
|
||||
import { Gtk } from "astal/gtk4";
|
||||
import Notifd from "gi://AstalNotifd";
|
||||
import { NotificationIcon } from "./Icon";
|
||||
import { createTimeoutManager } from "../../../util/notifd";
|
||||
import { createTimeoutManager, time, urgency } from "../../../util/notifd";
|
||||
|
||||
export function NotificationWidget({
|
||||
notification,
|
||||
@@ -81,8 +81,8 @@ export function NotificationWidget({
|
||||
halign={CENTER}
|
||||
valign={CENTER}
|
||||
vexpand={true}
|
||||
child={NotificationIcon(notification)!}
|
||||
>
|
||||
{NotificationIcon(notification)}
|
||||
</box>
|
||||
<box
|
||||
vertical
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import AstalNotifd from "gi://AstalNotifd"
|
||||
|
||||
const notifd = AstalNotifd.get_default()
|
||||
const getNotifications = () => {
|
||||
|
||||
}
|
||||
@@ -1,299 +0,0 @@
|
||||
/*
|
||||
* dotfiles - handler.ts
|
||||
*
|
||||
* Created by Janis Hutz 03/21/2025, Licensed under the GPL V3 License
|
||||
* https://janishutz.com, development@janishutz.com
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
import { App, Astal, Gdk } from "astal/gtk4"
|
||||
import Notifd from "gi://AstalNotifd";
|
||||
import Notification from "./notifications";
|
||||
import { timeout, Variable } from "astal"
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
// Config
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
const TIMEOUT_DELAY = 5000;
|
||||
let isRunning = false;
|
||||
let notificationMenuOpen = false;
|
||||
|
||||
interface NotificationDetails {
|
||||
notification: Notifd.Notification;
|
||||
backendID: number;
|
||||
notifdID: number;
|
||||
}
|
||||
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
// ╭───────────────────────────────────────────────╮
|
||||
// │ Handler │
|
||||
// ╰───────────────────────────────────────────────╯
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
let ShownNotifications: Variable<number[]> = Variable( [] );
|
||||
let Notifications: NotificationDetails[] = [];
|
||||
|
||||
const notifd = Notifd.get_default();
|
||||
notifd.ignoreTimeout = true;
|
||||
|
||||
|
||||
/**
|
||||
* Delete a notification by its internal ID
|
||||
* @param index The notifd ID of the notification
|
||||
*/
|
||||
const deleteNotification = ( index: number ): void => {
|
||||
hideNotification( index );
|
||||
Notifications.splice( index, 1 );
|
||||
if ( Notifications.length === 0 ) {
|
||||
notificationMenuOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
/**
|
||||
* Delete a notification by notifd id
|
||||
* @param id The notifd ID of the notification
|
||||
*/
|
||||
const deleteNotificationByNotificationID = ( id: number ): void => {
|
||||
const index = findNotificationByNotificationID( id );
|
||||
if ( index > -1 ) {
|
||||
deleteNotification( index );
|
||||
}
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
/**
|
||||
* Find the internal ID from the notifd id for a notification (helper function)
|
||||
* @param id The notifd ID of the notification
|
||||
* @returns The internal ID or -1 if not found
|
||||
*/
|
||||
const findNotificationByNotificationID = ( id: number ): number => {
|
||||
// Find index in Notifications array
|
||||
for (let index = 0; index < Notifications.length; index++) {
|
||||
if ( Notifications[ index ].notifdID === id ) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
/**
|
||||
* Add a notification to the notification handler
|
||||
* @param id The notifd ID of the notification
|
||||
*/
|
||||
const addNotification = ( id: number ): void => {
|
||||
const currIndex = Notifications.length;
|
||||
Notifications.push( {
|
||||
notifdID: id,
|
||||
backendID: currIndex,
|
||||
notification: notifd.get_notification( id )
|
||||
} );
|
||||
|
||||
showNotification( currIndex );
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
/**
|
||||
* Start the notifd runner and handle notifications.
|
||||
*/
|
||||
const hookToNotificationDaemon = (): void => {
|
||||
if ( isRunning ) {
|
||||
printerr( '[ Notifications ] Error: Already running' );
|
||||
return;
|
||||
}
|
||||
isRunning = true;
|
||||
|
||||
notifd.connect( 'notified', ( _, id ) => {
|
||||
addNotification( id );
|
||||
} );
|
||||
|
||||
notifd.connect( 'resolved', ( _, id ) => {
|
||||
deleteNotificationByNotificationID( id );
|
||||
} );
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
/**
|
||||
* Show a notification. It will stay on screen (regardless of removeAgain passed in), if
|
||||
* critical urgency
|
||||
* @param id The internal id (index in Notifications array)
|
||||
* @param removeAgain = true If to remove the notification from the screen again automatically
|
||||
*/
|
||||
const showNotification = ( id: number, removeAgain: boolean = true ) => {
|
||||
// Add notification to UI for display
|
||||
const not = [...ShownNotifications.get()].reverse();
|
||||
not.push( id );
|
||||
ShownNotifications.set( not.reverse() );
|
||||
|
||||
// Set delay to remove the notification again
|
||||
if ( removeAgain && Notifications[ id ].notification.get_urgency() !== Notifd.Urgency.CRITICAL ) {
|
||||
timeout( TIMEOUT_DELAY, () => {
|
||||
hideNotification( id );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
/**
|
||||
* Stop displaying notification
|
||||
* @param id The internal id (index in the Notifications array)
|
||||
*/
|
||||
const hideNotification = ( id: number ) => {
|
||||
if ( !notificationMenuOpen ) {
|
||||
const not = [...ShownNotifications.get()];
|
||||
not.splice( not.indexOf( id ), 1 );
|
||||
ShownNotifications.set( not );
|
||||
}
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
/**
|
||||
* Open the notification menu. Called by toggleNotificationMenu too
|
||||
*/
|
||||
const openNotificationMenu = () => {
|
||||
// Simply show all notifications
|
||||
notificationMenuOpen = true;
|
||||
const not = [];
|
||||
for (let index = 0; index < Notifications.length; index++) {
|
||||
not.push( index );
|
||||
}
|
||||
ShownNotifications.set( not.reverse() );
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
/**
|
||||
* Close the notification menu. Called by toggleNotificationMenu too
|
||||
*/
|
||||
const closeNotificationMenu = () => {
|
||||
// Hide all notifications
|
||||
notificationMenuOpen = true;
|
||||
ShownNotifications.set( [] );
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
/**
|
||||
* Toggle the notification menu (i.e. show all notifications)
|
||||
*/
|
||||
const toggleNotificationMenu = (): string => {
|
||||
if ( notificationMenuOpen ) {
|
||||
closeNotificationMenu();
|
||||
return 'Toggle notification menu closed';
|
||||
} else {
|
||||
openNotificationMenu();
|
||||
return 'Toggled notification menu open';
|
||||
}
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
/**
|
||||
* Delete all notifications
|
||||
*/
|
||||
const clearAllNotifications = () => {
|
||||
Notifications = [];
|
||||
ShownNotifications.set( [] );
|
||||
// TODO: Hiding for each individual deleteNotification
|
||||
notificationMenuOpen = false;
|
||||
}
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
/**
|
||||
* Delete the newest notifications
|
||||
*/
|
||||
const clearNewestNotifications = () => {
|
||||
const not = [...ShownNotifications.get()];
|
||||
not.splice( 0, 1 );
|
||||
ShownNotifications.set( not );
|
||||
|
||||
Notifications.splice( Notifications.length - 1, 1 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
// ╭───────────────────────────────────────────────╮
|
||||
// │ User Interface │
|
||||
// ╰───────────────────────────────────────────────╯
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
const startNotificationHandler = (gdkmonitor: Gdk.Monitor) => {
|
||||
const { TOP, RIGHT } = Astal.WindowAnchor
|
||||
|
||||
hookToNotificationDaemon();
|
||||
|
||||
return <window
|
||||
cssClasses={["NotificationHandler"]}
|
||||
gdkmonitor={gdkmonitor}
|
||||
exclusivity={Astal.Exclusivity.EXCLUSIVE}
|
||||
anchor={TOP | RIGHT}
|
||||
visible={ShownNotifications( list => list.length > 0 )}
|
||||
application={App}>
|
||||
<box vertical>
|
||||
{ShownNotifications( list => list.map( i => {
|
||||
// i is index in ShownNotifications array
|
||||
return <Notification id={i} delete={deleteNotification} notification={Notifications[ i ].notification}></Notification>
|
||||
} ) ) }
|
||||
</box>
|
||||
</window>
|
||||
}
|
||||
|
||||
const cliHandler = ( args: string[] ): string => {
|
||||
if ( args[ 1 ] == 'show' ) {
|
||||
openNotificationMenu();
|
||||
return 'Showing all open notifications';
|
||||
} else if ( args[ 1 ] == 'hide' ) {
|
||||
closeNotificationMenu();
|
||||
return 'Hid all notifications';
|
||||
} else if ( args[ 1 ] == 'clear' ) {
|
||||
clearAllNotifications();
|
||||
return 'Cleared all notifications';
|
||||
} else if ( args[ 1 ] == 'clear-newest' ) {
|
||||
clearNewestNotifications();
|
||||
return 'Cleared newest notification';
|
||||
} else if ( args[ 1 ] == 'toggle' ) {
|
||||
return toggleNotificationMenu();
|
||||
} else if ( args[ 1 ] == 'list' ){
|
||||
if ( Notifications.length > 0 ) {
|
||||
let list = 'Currently unviewed notifications: ';
|
||||
for (let index = 0; index < Notifications.length; index++) {
|
||||
const element = Notifications[index];
|
||||
|
||||
list += `\n - (${element.notifdID}) ${element.notification.get_app_name()}: ${element.notification.get_summary()}`;
|
||||
}
|
||||
return list;
|
||||
} else {
|
||||
return 'No currently unviewed notifications'
|
||||
}
|
||||
} else {
|
||||
return 'Unknown command!';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
startNotificationHandler,
|
||||
cliHandler
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Gtk, Gdk } from "astal/gtk4";
|
||||
import { GLib } from "astal";
|
||||
|
||||
export const isIcon = (icon: string) => {
|
||||
const display = Gdk.Display.get_default();
|
||||
if (!display) return false;
|
||||
const iconTheme = Gtk.IconTheme.get_for_display(display);
|
||||
return iconTheme.has_icon(icon);
|
||||
};
|
||||
|
||||
export const fileExists = (path: string) =>
|
||||
GLib.file_test(path, GLib.FileTest.EXISTS);
|
||||
@@ -8,14 +8,16 @@ export function NotificationIcon(notification: Notifd.Notification) {
|
||||
const icon = notification.image || notification.appIcon || notification.desktopEntry;
|
||||
if (fileExists(icon)) {
|
||||
return (
|
||||
<box expand={false} valign={Gtk.Align.CENTER}>
|
||||
<box hexpand={false} valign={Gtk.Align.CENTER} child={
|
||||
<image file={icon} />
|
||||
}>
|
||||
</box>
|
||||
);
|
||||
} else if (isIcon(icon)) {
|
||||
return (
|
||||
<box expand={false} valign={Gtk.Align.CENTER}>
|
||||
<box hexpand={false} valign={Gtk.Align.CENTER} child={
|
||||
<image iconName={icon} />
|
||||
}>
|
||||
</box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Astal } from "astal/gtk4";
|
||||
import Notifd from "gi://AstalNotifd";
|
||||
import Hyprland from "gi://AstalHyprland";
|
||||
import { bind } from "astal";
|
||||
import { NotificationWidget } from "./modules/Notification";
|
||||
import { hyprToGdk } from "../../util/hyprland";
|
||||
|
||||
export default function Notifications() {
|
||||
const notifd = Notifd.get_default();
|
||||
const hyprland = Hyprland.get_default();
|
||||
const { TOP, RIGHT } = Astal.WindowAnchor;
|
||||
|
||||
return (
|
||||
<window
|
||||
name="notifications"
|
||||
gdkmonitor={bind(hyprland, "focusedMonitor").as(
|
||||
(focused: Hyprland.Monitor) => hyprToGdk(focused),
|
||||
)}
|
||||
anchor={TOP | RIGHT}
|
||||
visible={bind(notifd, "notifications").as(
|
||||
(notifications) => notifications.length > 0,
|
||||
)}
|
||||
child={
|
||||
<box vertical={true} cssClasses={["notifications"]}>
|
||||
{bind(notifd, "notifications").as((notifications) =>
|
||||
notifications.map((n) => <NotificationWidget notification={n} />),
|
||||
)}
|
||||
</box>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
// From astal examples
|
||||
|
||||
import { bind, GLib } from "astal";
|
||||
import { Gtk } from "astal/gtk4";
|
||||
import Notifd from "gi://AstalNotifd";
|
||||
import { NotificationIcon } from "./icon";
|
||||
// import Pango from "gi://Pango?version=1.0"
|
||||
|
||||
const fileExists = (path: string) => GLib.file_test(path, GLib.FileTest.EXISTS);
|
||||
|
||||
const time = (time: number, format = "%H:%M") =>
|
||||
GLib.DateTime.new_from_unix_local(time).format(format)!;
|
||||
|
||||
const urgency = (n: Notifd.Notification) => {
|
||||
const { LOW, NORMAL, CRITICAL } = Notifd.Urgency;
|
||||
// match operator when?
|
||||
switch (n.urgency) {
|
||||
case LOW:
|
||||
return "low";
|
||||
case CRITICAL:
|
||||
return "critical";
|
||||
case NORMAL:
|
||||
default:
|
||||
return "normal";
|
||||
}
|
||||
};
|
||||
|
||||
type Props = {
|
||||
delete: (id: number) => void;
|
||||
notification: Notifd.Notification;
|
||||
id: number;
|
||||
};
|
||||
|
||||
export default function Notification(props: Props) {
|
||||
const { notification: n, id: id, delete: del } = props;
|
||||
const { START, CENTER, END } = Gtk.Align;
|
||||
|
||||
return (
|
||||
<box vertical cssClasses={["notification", `${urgency(n)}`]}>
|
||||
<box cssClasses={["header"]}>
|
||||
{n.appIcon || n.desktopEntry ? (
|
||||
<Gtk.Image
|
||||
cssClasses={["app-icon"]}
|
||||
visible={Boolean(n.appIcon || n.desktopEntry)}
|
||||
iconName={n.appIcon || n.desktopEntry}
|
||||
/>
|
||||
) : (
|
||||
<image iconName={"window-close-symbolic"}></image>
|
||||
)}
|
||||
<label
|
||||
cssClasses={["app-name"]}
|
||||
halign={START}
|
||||
// ellipsize={Pango.EllipsizeMode.END}
|
||||
label={n.appName || "Unknown"}
|
||||
/>
|
||||
<label
|
||||
cssClasses={["time"]}
|
||||
hexpand
|
||||
halign={END}
|
||||
label={time(n.time)}
|
||||
/>
|
||||
<button
|
||||
onClicked={() => {
|
||||
del(id);
|
||||
}}
|
||||
child={<image iconName="window-close-symbolic" />}
|
||||
></button>
|
||||
</box>
|
||||
<Gtk.Separator visible />
|
||||
<box cssClasses={["content"]}>
|
||||
<box
|
||||
cssClasses={["image"]}
|
||||
visible={Boolean(NotificationIcon(n))}
|
||||
halign={CENTER}
|
||||
valign={CENTER}
|
||||
vexpand={true}
|
||||
>
|
||||
{NotificationIcon(n)}
|
||||
</box>
|
||||
<box vertical>
|
||||
<label
|
||||
cssClasses={["summary"]}
|
||||
halign={START}
|
||||
xalign={0}
|
||||
useMarkup
|
||||
label={bind(n, "summary")}
|
||||
// ellipsize={Pango.EllipsizeMode.END}
|
||||
/>
|
||||
{n.body && (
|
||||
<label
|
||||
cssClasses={["body"]}
|
||||
valign={CENTER}
|
||||
wrap={true}
|
||||
maxWidthChars={50}
|
||||
label={bind(n, "body")}
|
||||
/>
|
||||
)}
|
||||
</box>
|
||||
</box>
|
||||
{n.get_actions().length > 0 ? (
|
||||
<box cssClasses={["actions"]}>
|
||||
{n.get_actions().map(({ label, id }) => (
|
||||
<button hexpand onClicked={() => n.invoke(id)}>
|
||||
<label label={label} halign={CENTER} hexpand />
|
||||
</button>
|
||||
))}
|
||||
</box>
|
||||
) : (
|
||||
<box></box>
|
||||
)}
|
||||
</box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,744 @@
|
||||
import eslint from '@eslint/js';
|
||||
import globals from 'globals';
|
||||
import stylistic from '@stylistic/eslint-plugin';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import typescript from '@typescript-eslint/eslint-plugin';
|
||||
import vue from 'eslint-plugin-vue';
|
||||
|
||||
const style = {
|
||||
'plugins': {
|
||||
'@stylistic': stylistic,
|
||||
'@stylistic/js': stylistic,
|
||||
'@stylistic/ts': stylistic
|
||||
},
|
||||
'files': [
|
||||
'**/*.ts',
|
||||
'**/*.js',
|
||||
'**/*.mjs',
|
||||
'**/*.cjs',
|
||||
'**/*.tsx',
|
||||
'**/*.jsx'
|
||||
],
|
||||
'rules': {
|
||||
'sort-imports': [
|
||||
'warn',
|
||||
{
|
||||
'ignoreCase': false,
|
||||
'ignoreDeclarationSort': false,
|
||||
'ignoreMemberSort': false,
|
||||
'memberSyntaxSortOrder': [
|
||||
'none',
|
||||
'all',
|
||||
'multiple',
|
||||
'single'
|
||||
],
|
||||
'allowSeparatedGroups': false
|
||||
}
|
||||
],
|
||||
// Formatting
|
||||
'@stylistic/array-bracket-newline': [
|
||||
'error',
|
||||
{
|
||||
'multiline': false,
|
||||
'minItems': 2
|
||||
}
|
||||
],
|
||||
'@stylistic/array-bracket-spacing': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'@stylistic/array-element-newline': [
|
||||
'error',
|
||||
{
|
||||
'consistent': false,
|
||||
'multiline': false,
|
||||
'minItems': 2
|
||||
}
|
||||
],
|
||||
'@stylistic/arrow-parens': [
|
||||
'error',
|
||||
'as-needed'
|
||||
],
|
||||
'@stylistic/arrow-spacing': [
|
||||
'error',
|
||||
{
|
||||
'before': true,
|
||||
'after': true
|
||||
}
|
||||
],
|
||||
'@stylistic/block-spacing': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'@stylistic/brace-style': [
|
||||
'error',
|
||||
'1tbs',
|
||||
{
|
||||
'allowSingleLine': false
|
||||
}
|
||||
],
|
||||
'@stylistic/comma-dangle': [
|
||||
'error',
|
||||
'never'
|
||||
],
|
||||
'@stylistic/comma-spacing': [
|
||||
'error',
|
||||
{
|
||||
'before': false,
|
||||
'after': true
|
||||
}
|
||||
],
|
||||
'@stylistic/comma-style': [
|
||||
'error',
|
||||
'last'
|
||||
],
|
||||
'@stylistic/dot-location': [
|
||||
'error',
|
||||
'property'
|
||||
],
|
||||
'@stylistic/function-call-argument-newline': [
|
||||
'error',
|
||||
'consistent'
|
||||
],
|
||||
'@stylistic/function-call-spacing': [
|
||||
'error',
|
||||
'never'
|
||||
],
|
||||
'@stylistic/function-paren-newline': [
|
||||
'error',
|
||||
'multiline-arguments'
|
||||
],
|
||||
'@stylistic/implicit-arrow-linebreak': [
|
||||
'error',
|
||||
'beside'
|
||||
],
|
||||
'@stylistic/indent': [
|
||||
'error',
|
||||
4
|
||||
],
|
||||
'@stylistic/indent-binary-ops': [
|
||||
'error',
|
||||
4
|
||||
],
|
||||
'@stylistic/key-spacing': [
|
||||
'error',
|
||||
{
|
||||
'beforeColon': false,
|
||||
'afterColon': true
|
||||
}
|
||||
],
|
||||
'@stylistic/keyword-spacing': [
|
||||
'error',
|
||||
{
|
||||
'before': true,
|
||||
'after': true
|
||||
}
|
||||
],
|
||||
'@stylistic/lines-between-class-members': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'@stylistic/max-len': [
|
||||
'warn',
|
||||
{
|
||||
'code': 140,
|
||||
'comments': 160,
|
||||
'ignoreComments': false,
|
||||
'ignoreUrls': true,
|
||||
'ignoreStrings': true,
|
||||
'ignoreTemplateLiterals': true,
|
||||
'ignoreRegExpLiterals': true
|
||||
}
|
||||
],
|
||||
'@stylistic/max-statements-per-line': [
|
||||
'error',
|
||||
{
|
||||
'max': 1
|
||||
}
|
||||
],
|
||||
'@stylistic/multiline-ternary': [
|
||||
'error',
|
||||
'always-multiline'
|
||||
],
|
||||
'@stylistic/new-parens': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'@stylistic/newline-per-chained-call': 'error',
|
||||
'@stylistic/no-confusing-arrow': 'error',
|
||||
'@stylistic/no-extra-parens': [
|
||||
'error',
|
||||
'all',
|
||||
{
|
||||
'nestedBinaryExpressions': false,
|
||||
'ternaryOperandBinaryExpressions': false,
|
||||
'ignoreJSX': 'multi-line',
|
||||
'nestedConditionalExpressions': false
|
||||
}
|
||||
],
|
||||
'@stylistic/no-extra-semi': 'error',
|
||||
'@stylistic/no-floating-decimal': 'error',
|
||||
'@stylistic/no-mixed-operators': 'error',
|
||||
'@stylistic/no-mixed-spaces-and-tabs': 'error',
|
||||
'@stylistic/no-multi-spaces': 'error',
|
||||
'@stylistic/no-multiple-empty-lines': [
|
||||
'error',
|
||||
{
|
||||
'max': 3,
|
||||
'maxEOF': 2
|
||||
}
|
||||
],
|
||||
'@stylistic/no-tabs': 'error',
|
||||
'@stylistic/no-trailing-spaces': 'error',
|
||||
'@stylistic/no-whitespace-before-property': 'error',
|
||||
'@stylistic/object-curly-newline': [
|
||||
'error',
|
||||
{
|
||||
'multiline': true,
|
||||
'minProperties': 1
|
||||
}
|
||||
],
|
||||
'@stylistic/object-curly-spacing': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'@stylistic/object-property-newline': 'error',
|
||||
'@stylistic/one-var-declaration-per-line': 'error',
|
||||
'@stylistic/operator-linebreak': [
|
||||
'error',
|
||||
'before'
|
||||
],
|
||||
'@stylistic/padded-blocks': [
|
||||
'error',
|
||||
{
|
||||
'blocks': 'never',
|
||||
'classes': 'always',
|
||||
'switches': 'never'
|
||||
}
|
||||
],
|
||||
|
||||
// Padding lines. The most in-depth part of this config
|
||||
'@stylistic/padding-line-between-statements': [
|
||||
'error',
|
||||
// Variables, Constants
|
||||
{
|
||||
'blankLine': 'never',
|
||||
'prev': 'var',
|
||||
'next': 'var'
|
||||
},
|
||||
{
|
||||
'blankLine': 'never',
|
||||
'prev': 'let',
|
||||
'next': 'let'
|
||||
},
|
||||
{
|
||||
'blankLine': 'never',
|
||||
'prev': 'const',
|
||||
'next': 'const'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'var',
|
||||
'next': [
|
||||
'block',
|
||||
'block-like',
|
||||
'break',
|
||||
'cjs-export',
|
||||
'cjs-import',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'directive',
|
||||
'do',
|
||||
'empty',
|
||||
'export',
|
||||
'expression',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'iife',
|
||||
'import',
|
||||
'let',
|
||||
'return',
|
||||
'switch',
|
||||
'throw',
|
||||
'try',
|
||||
'var',
|
||||
'with'
|
||||
]
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'let',
|
||||
'next': [
|
||||
'block',
|
||||
'block-like',
|
||||
'break',
|
||||
'cjs-export',
|
||||
'cjs-import',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'directive',
|
||||
'do',
|
||||
'empty',
|
||||
'export',
|
||||
'expression',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'iife',
|
||||
'import',
|
||||
'return',
|
||||
'switch',
|
||||
'throw',
|
||||
'try',
|
||||
'var',
|
||||
'while',
|
||||
'with'
|
||||
]
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'const',
|
||||
'next': [
|
||||
'block',
|
||||
'block-like',
|
||||
'break',
|
||||
'cjs-export',
|
||||
'cjs-import',
|
||||
'class',
|
||||
'continue',
|
||||
'debugger',
|
||||
'directive',
|
||||
'do',
|
||||
'empty',
|
||||
'export',
|
||||
'expression',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'iife',
|
||||
'import',
|
||||
'let',
|
||||
'return',
|
||||
'switch',
|
||||
'throw',
|
||||
'try',
|
||||
'var',
|
||||
'while',
|
||||
'with'
|
||||
]
|
||||
},
|
||||
// Import
|
||||
{
|
||||
'blankLine': 'never',
|
||||
'prev': 'import',
|
||||
'next': 'import'
|
||||
},
|
||||
{
|
||||
'blankLine': 'never',
|
||||
'prev': 'cjs-import',
|
||||
'next': 'cjs-import'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': [
|
||||
'block',
|
||||
'block-like',
|
||||
'break',
|
||||
'cjs-export',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'directive',
|
||||
'do',
|
||||
'empty',
|
||||
'export',
|
||||
'expression',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'iife',
|
||||
'let',
|
||||
'return',
|
||||
'switch',
|
||||
'throw',
|
||||
'try',
|
||||
'var',
|
||||
'while',
|
||||
'with'
|
||||
],
|
||||
'next': 'cjs-import'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'cjs-import',
|
||||
'next': [
|
||||
'block',
|
||||
'block-like',
|
||||
'break',
|
||||
'cjs-export',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'directive',
|
||||
'do',
|
||||
'empty',
|
||||
'export',
|
||||
'expression',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'iife',
|
||||
'let',
|
||||
'return',
|
||||
'switch',
|
||||
'throw',
|
||||
'try',
|
||||
'var',
|
||||
'while',
|
||||
'with'
|
||||
]
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': [
|
||||
'block',
|
||||
'block-like',
|
||||
'break',
|
||||
'cjs-export',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'directive',
|
||||
'do',
|
||||
'empty',
|
||||
'export',
|
||||
'expression',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'iife',
|
||||
'let',
|
||||
'return',
|
||||
'switch',
|
||||
'throw',
|
||||
'try',
|
||||
'var',
|
||||
'while',
|
||||
'with'
|
||||
],
|
||||
'next': 'import'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'import',
|
||||
'next': [
|
||||
'block',
|
||||
'block-like',
|
||||
'break',
|
||||
'cjs-export',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'directive',
|
||||
'do',
|
||||
'empty',
|
||||
'export',
|
||||
'expression',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'iife',
|
||||
'let',
|
||||
'return',
|
||||
'switch',
|
||||
'throw',
|
||||
'try',
|
||||
'var',
|
||||
'while',
|
||||
'with'
|
||||
]
|
||||
},
|
||||
// If
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': '*',
|
||||
'next': 'if'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'if',
|
||||
'next': '*'
|
||||
},
|
||||
// For
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': '*',
|
||||
'next': 'for'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'for',
|
||||
'next': '*'
|
||||
},
|
||||
// While
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': '*',
|
||||
'next': 'while'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'while',
|
||||
'next': '*'
|
||||
},
|
||||
// Functions
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': '*',
|
||||
'next': 'function'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'function',
|
||||
'next': '*'
|
||||
},
|
||||
// Block Statements
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': '*',
|
||||
'next': 'block-like'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'block-like',
|
||||
'next': '*'
|
||||
},
|
||||
// Switch
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': '*',
|
||||
'next': 'switch'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'switch',
|
||||
'next': '*'
|
||||
},
|
||||
// Try-Catch
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': '*',
|
||||
'next': 'try'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'try',
|
||||
'next': '*'
|
||||
},
|
||||
// Throw
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': '*',
|
||||
'next': 'throw'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'throw',
|
||||
'next': '*'
|
||||
},
|
||||
// Return
|
||||
{
|
||||
'blankLine': 'never',
|
||||
'prev': 'return',
|
||||
'next': '*'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': '*',
|
||||
'next': 'return'
|
||||
},
|
||||
// Export
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': '*',
|
||||
'next': 'export'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'export',
|
||||
'next': '*'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': '*',
|
||||
'next': 'cjs-export'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'cjs-export',
|
||||
'next': '*'
|
||||
},
|
||||
// Classes
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': '*',
|
||||
'next': 'class'
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': 'class',
|
||||
'next': '*'
|
||||
}
|
||||
],
|
||||
'@stylistic/quote-props': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'@stylistic/quotes': [
|
||||
'error',
|
||||
'single'
|
||||
],
|
||||
'@stylistic/rest-spread-spacing': [
|
||||
'error',
|
||||
'never'
|
||||
],
|
||||
'@stylistic/semi': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'@stylistic/semi-spacing': [
|
||||
'error',
|
||||
{
|
||||
'before': false,
|
||||
'after': true
|
||||
}
|
||||
],
|
||||
'@stylistic/semi-style': [
|
||||
'error',
|
||||
'last'
|
||||
],
|
||||
'@stylistic/space-before-blocks': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'@stylistic/space-before-function-paren': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'@stylistic/space-in-parens': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'@stylistic/space-infix-ops': [
|
||||
'error',
|
||||
{
|
||||
'int32Hint': false
|
||||
}
|
||||
],
|
||||
'@stylistic/space-unary-ops': 'error',
|
||||
'@stylistic/spaced-comment': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'@stylistic/switch-colon-spacing': 'error',
|
||||
'@stylistic/template-curly-spacing': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'@stylistic/template-tag-spacing': [
|
||||
'error',
|
||||
'always'
|
||||
],
|
||||
'@stylistic/type-generic-spacing': 'error',
|
||||
'@stylistic/type-named-tuple-spacing': 'error',
|
||||
'@stylistic/wrap-iife': [
|
||||
'error',
|
||||
'inside'
|
||||
],
|
||||
'@stylistic/wrap-regex': 'error',
|
||||
'@stylistic/ts/type-annotation-spacing': 'error'
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {import('eslint').Linter.Config} */
|
||||
export default tseslint.config(
|
||||
// Base JavaScript rules
|
||||
eslint.configs.recommended,
|
||||
tseslint.configs.recommended,
|
||||
style,
|
||||
|
||||
// Vue support (including TS and JSX inside SFCs)
|
||||
{
|
||||
'files': [ '**/*.vue' ],
|
||||
'languageOptions': {
|
||||
'sourceType': 'module',
|
||||
'ecmaVersion': 'latest',
|
||||
'globals': globals.browser,
|
||||
'parserOptions': {
|
||||
'parser': tseslint.parser
|
||||
}
|
||||
},
|
||||
'plugins': {
|
||||
'vue': vue,
|
||||
'@stylistic': stylistic,
|
||||
'@stylistic/js': stylistic,
|
||||
'@stylistic/ts': stylistic,
|
||||
'@typescript-eslint': typescript
|
||||
},
|
||||
'extends': [
|
||||
eslint.configs.recommended,
|
||||
...vue.configs['flat/recommended']
|
||||
],
|
||||
'rules': {
|
||||
...typescript.configs.recommended.rules,
|
||||
...style.rules,
|
||||
|
||||
// Vue specific rules
|
||||
'@stylistic/indent': 'off',
|
||||
'vue/html-indent': [
|
||||
'error',
|
||||
4
|
||||
],
|
||||
'vue/html-comment-indent': [
|
||||
'error',
|
||||
4
|
||||
],
|
||||
'vue/script-indent': [
|
||||
'error',
|
||||
4,
|
||||
{
|
||||
'baseIndent': 1,
|
||||
'switchCase': 1
|
||||
}
|
||||
],
|
||||
'vue/html-self-closing': [
|
||||
'error',
|
||||
{
|
||||
'html': {
|
||||
'void': 'never',
|
||||
'normal': 'never',
|
||||
'component': 'always'
|
||||
},
|
||||
'svg': 'always',
|
||||
'math': 'never'
|
||||
}
|
||||
],
|
||||
'vue/max-attributes-per-line': [
|
||||
'error',
|
||||
{
|
||||
'singleline': 3,
|
||||
'multiline': 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user