Compare commits

...

3 Commits

Author SHA1 Message Date
undergroundwires
704a3d0417 Fix card height inconsistency
TODO: Is CardLayout, card-layout, cardLayout etc., best names possiuble?

Fix card expansion panel heights not being equal. This is due to
limitations in CSS flex view.

Heights of all cards are `100%` which gives them uniform loook on same
row with all equal heights. However, their heights are set to `auto`
when of the cards are open. In that case, their sizes are no longer
equal in same row, cards with longer text/titles keeping more space.
This is because when a card is open, its expansion panel grows in its
own DOM element, increasing the height of the card.
at same height on each line, their heights change completely when one of

Heights gets fucked up when card is collapsing, this is big fix, fix this.

Supporting changes:

- Move card expander to its own component `CardExpansionPanel`.
- Introduce `UseCardLayout` hook to do calculations instead of CSS to
  circumvent limitations in CSS flex view.
2024-05-28 16:20:20 +02:00
undergroundwires
22d6c7991e ci/cd: centralize and bump artifact uploads
- Upgrade `actions/upload-artifact` to `v4` to address deprecation
  warnings related to Node.js 16, improving compatibility with GitHub
  runners. This resolves the following warning from the runners:
  > Node.js 16 actions are deprecated. Please update the following actions
  > to use Node.js 20: actions/upload-artifact@v3.
- Centralize the use of the `upload-artifact` action through a new
  custom action, improving maintainability and consistency across
  workflows.
2024-05-28 12:53:45 +02:00
undergroundwires-bot
795b7f0321 ⬆️ bump everywhere to 0.13.4 2024-05-27 13:45:04 +00:00
11 changed files with 256 additions and 98 deletions

View File

@@ -0,0 +1,15 @@
inputs:
name:
required: true
path:
required: true
runs:
using: composite
steps:
-
name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.name }}
path: ${{ inputs.path }}

View File

@@ -70,7 +70,7 @@ jobs:
- -
name: Upload screenshot name: Upload screenshot
if: always() # Run even if previous step fails if: always() # Run even if previous step fails
uses: actions/upload-artifact@v3 uses: ./.github/actions/upload-artifact
with: with:
name: screenshot-${{ matrix.os }} name: screenshot-${{ matrix.os }}
path: screenshot.png path: screenshot.png

View File

@@ -51,14 +51,14 @@ jobs:
- -
name: Upload screenshots name: Upload screenshots
if: failure() # Run only if previous steps fail because screenshots will be generated only if E2E test failed if: failure() # Run only if previous steps fail because screenshots will be generated only if E2E test failed
uses: actions/upload-artifact@v3 uses: ./.github/actions/upload-artifact
with: with:
name: e2e-screenshots-${{ matrix.os }} name: e2e-screenshots-${{ matrix.os }}
path: ${{ steps.artifacts.outputs.SCREENSHOTS_DIR }} path: ${{ steps.artifacts.outputs.SCREENSHOTS_DIR }}
- -
name: Upload videos name: Upload videos
if: always() # Run even if previous steps fail because test run video is always captured if: always() # Run even if previous steps fail because test run video is always captured
uses: actions/upload-artifact@v3 uses: ./.github/actions/upload-artifact
with: with:
name: e2e-videos-${{ matrix.os }} name: e2e-videos-${{ matrix.os }}
path: ${{ steps.artifacts.outputs.VIDEOS_DIR }} path: ${{ steps.artifacts.outputs.VIDEOS_DIR }}

View File

@@ -1,5 +1,27 @@
# Changelog # Changelog
## 0.13.4 (2024-05-27)
* Add specific empty function name compiler error | [870120b](https://github.com/undergroundwires/privacy.sexy/commit/870120bc13909a3681e0f0a2351806849476342f)
* ci/cd: fix recent Docker build failures on macOS | [a1922c5](https://github.com/undergroundwires/privacy.sexy/commit/a1922c50c12b3b7806e9e681ace842194a178bda)
* win: standardize registry edit + delete on revert | [cec0b4b](https://github.com/undergroundwires/privacy.sexy/commit/cec0b4b4f63c3563a0e7923ce6324a38d71a3955)
* Fix e2e test failing on Windows | [4a7efa2](https://github.com/undergroundwires/privacy.sexy/commit/4a7efa27c8df73ef9b7960afed29f216b066cba2)
* Add support for macOS universal binary #348, #362 | [d25c4e8](https://github.com/undergroundwires/privacy.sexy/commit/d25c4e8c812b8d012010ba38070a2931dcd28908)
* Migrate to GitHub issue forms | [9ab3ff7](https://github.com/undergroundwires/privacy.sexy/commit/9ab3ff75b0a69ac2ba27dd02e82db9b5bd76ea0f)
* ci/cd: fix quality checks not running on all OSes | [2390530](https://github.com/undergroundwires/privacy.sexy/commit/2390530d929fb92c266558c52376569a0ecb90c1)
* Bump Vue to latest and fix universal selector CSS | [aae5434](https://github.com/undergroundwires/privacy.sexy/commit/aae54344511ec51d17ad0420a92cb5a064e0e7bb)
* Centralize and optimize `ResizeObserver` usage | [2923621](https://github.com/undergroundwires/privacy.sexy/commit/292362135db0519ec1050bab80ed373aad115731)
* win: improve app access disabling and docs #138 | [ff3d5c4](https://github.com/undergroundwires/privacy.sexy/commit/ff3d5c48419f663379f5aba8936636c22f2c5de8)
* win: document and discourage RSA key script #363 | [f347fde](https://github.com/undergroundwires/privacy.sexy/commit/f347fde0c85f8b51b0060fdea0a2724b042aaeed)
* win: improve printing removal /w Print Queue #279 | [150e067](https://github.com/undergroundwires/privacy.sexy/commit/150e0670392bb62348c20ec644a4ed8a6bbffe74)
* win: discourage blocking app access #121 #339 #350 | [7794846](https://github.com/undergroundwires/privacy.sexy/commit/77948461856e6837ddfbcbbef72a1bf9fc706b4e)
* Improve context for errors thrown by compiler | [4212c7b](https://github.com/undergroundwires/privacy.sexy/commit/4212c7b9e0b1500378a1e4e88efc2d59f39f3d29)
* win: document disabling firewall #115 #152 #364 | [12b1f18](https://github.com/undergroundwires/privacy.sexy/commit/12b1f183f7ce966d6ce090d98aeea7ec491f8c7c)
* win: add script to disable Recall feature | [ce4cfdd](https://github.com/undergroundwires/privacy.sexy/commit/ce4cfdd169b7da0edc3da61143c988ed5f3c976e)
* win, mac, linux: fix typos and dead URLs #367 | [9e34e64](https://github.com/undergroundwires/privacy.sexy/commit/9e34e644493674ca709b64a47206763d5d4bd60c)
[compare](https://github.com/undergroundwires/privacy.sexy/compare/0.13.3...0.13.4)
## 0.13.3 (2024-05-11) ## 0.13.3 (2024-05-11)
* win: organize and document network disablement | [2eed6f4](https://github.com/undergroundwires/privacy.sexy/commit/2eed6f4afb6cf85fdc1d6acb808f82405a35cafd) * win: organize and document network disablement | [2eed6f4](https://github.com/undergroundwires/privacy.sexy/commit/2eed6f4afb6cf85fdc1d6acb808f82405a35cafd)

View File

@@ -122,7 +122,7 @@
## Get started ## Get started
- 🌍️ **Online**: [https://privacy.sexy](https://privacy.sexy). - 🌍️ **Online**: [https://privacy.sexy](https://privacy.sexy).
- 🖥️ **Offline**: Download directly for: [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.3/privacy.sexy-Setup-0.13.3.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.3/privacy.sexy-0.13.3.dmg), [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.3/privacy.sexy-0.13.3.AppImage). For more options, see [here](#additional-install-options). - 🖥️ **Offline**: Download directly for: [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.4/privacy.sexy-Setup-0.13.4.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.4/privacy.sexy-0.13.4.dmg), [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.13.4/privacy.sexy-0.13.4.AppImage). For more options, see [here](#additional-install-options).
See also: See also:

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "privacy.sexy", "name": "privacy.sexy",
"version": "0.13.3", "version": "0.13.4",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "privacy.sexy", "name": "privacy.sexy",
"version": "0.13.3", "version": "0.13.4",
"private": true, "private": true,
"slogan": "Privacy is sexy", "slogan": "Privacy is sexy",
"description": "Enforce privacy & security best-practices on Windows, macOS and Linux, because privacy is sexy.", "description": "Enforce privacy & security best-practices on Windows, macOS and Linux, because privacy is sexy.",

View File

@@ -0,0 +1,91 @@
<template>
<div>
<CardExpansionArrow />
<div class="card__expander">
<div class="card__expander__close-button">
<FlatButton
icon="xmark"
@click="collapse()"
/>
</div>
<div class="card__expander__content">
<ScriptsTree
:category-id="categoryId"
:has-top-padding="false"
/>
</div>
</div>
</div>
</template>
<script lang="ts">
import {
defineComponent,
} from 'vue';
import FlatButton from '@/presentation/components/Shared/FlatButton.vue';
import ScriptsTree from '@/presentation/components/Scripts/View/Tree/ScriptsTree.vue';
import CardExpansionArrow from './CardExpansionArrow.vue';
export default defineComponent({
components: {
ScriptsTree,
FlatButton,
CardExpansionArrow,
},
props: {
categoryId: {
type: Number,
required: true,
},
},
emits: {
/* eslint-disable @typescript-eslint/no-unused-vars */
onCollapse: () => true,
/* eslint-enable @typescript-eslint/no-unused-vars */
},
setup(_, { emit }) {
function collapse() {
emit('onCollapse');
}
return {
collapse,
};
},
});
</script>
<style scoped lang="scss">
@use "@/presentation/assets/styles/main" as *;
@use "./card-gap" as *;
.card__expander {
position: relative;
background-color: $color-primary-darker;
color: $color-on-primary;
margin-top: $spacing-absolute-xx-large;
display: flex;
align-items: center;
flex-direction: column;
.card__expander__content {
display: flex;
justify-content: center;
word-break: break-word;
max-width: 100%; // Prevents horizontal expansion of inner content (e.g., when a code block is shown)
width: 100%; // Expands the container to fill available horizontal space, enabling alignment of child items.
}
.card__expander__close-button {
font-size: $font-size-absolute-large;
align-self: flex-end;
margin-right: $spacing-absolute-small;
@include clickable;
color: $color-primary-light;
@include hover-or-touch {
color: $color-primary;
}
}
}
</style>

View File

@@ -27,6 +27,7 @@
:data-category="categoryId" :data-category="categoryId"
:category-id="categoryId" :category-id="categoryId"
:active-category-id="activeCategoryId" :active-category-id="activeCategoryId"
:card-layout="cardLayout"
@card-expansion-changed="onSelected(categoryId, $event)" @card-expansion-changed="onSelected(categoryId, $event)"
/> />
</div> </div>
@@ -46,6 +47,7 @@ import { injectKey } from '@/presentation/injectionSymbols';
import SizeObserver from '@/presentation/components/Shared/SizeObserver.vue'; import SizeObserver from '@/presentation/components/Shared/SizeObserver.vue';
import { hasDirective } from './NonCollapsingDirective'; import { hasDirective } from './NonCollapsingDirective';
import CardListItem from './CardListItem.vue'; import CardListItem from './CardListItem.vue';
import { useCardLayout } from './UseCardLayout';
export default defineComponent({ export default defineComponent({
components: { components: {
@@ -61,8 +63,14 @@ export default defineComponent({
const categoryIds = computed<readonly number[]>( const categoryIds = computed<readonly number[]>(
() => currentState.value.collection.actions.map((category) => category.id), () => currentState.value.collection.actions.map((category) => category.id),
); );
const activeCategoryId = ref<number | undefined>(undefined); const activeCategoryId = ref<number | undefined>(undefined);
const cardLayout = useCardLayout({
containerWidth: computed(() => width.value ?? 0),
totalCards: computed(() => categoryIds.value.length),
});
function onSelected(categoryId: number, isExpanded: boolean) { function onSelected(categoryId: number, isExpanded: boolean) {
activeCategoryId.value = isExpanded ? categoryId : undefined; activeCategoryId.value = isExpanded ? categoryId : undefined;
} }
@@ -101,6 +109,7 @@ export default defineComponent({
width, width,
categoryIds, categoryIds,
activeCategoryId, activeCategoryId,
cardLayout,
onSelected, onSelected,
}; };
}, },

View File

@@ -29,26 +29,12 @@
/> />
</div> </div>
<CardExpandTransition> <CardExpandTransition>
<div v-show="isExpanded"> <CardExpansionPanel
<CardExpansionArrow /> v-show="isExpanded"
<div
class="card__expander"
@click.stop
>
<div class="card__expander__close-button">
<FlatButton
icon="xmark"
@click="collapse()"
/>
</div>
<div class="card__expander__content">
<ScriptsTree
:category-id="categoryId" :category-id="categoryId"
:has-top-padding="false" @on-collapse="collapse"
@click.stop
/> />
</div>
</div>
</div>
</CardExpandTransition> </CardExpandTransition>
</div> </div>
</template> </template>
@@ -56,30 +42,32 @@
<script lang="ts"> <script lang="ts">
import { import {
defineComponent, computed, shallowRef, defineComponent, computed, shallowRef,
type PropType,
} from 'vue'; } from 'vue';
import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue'; import AppIcon from '@/presentation/components/Shared/Icon/AppIcon.vue';
import FlatButton from '@/presentation/components/Shared/FlatButton.vue';
import { injectKey } from '@/presentation/injectionSymbols'; import { injectKey } from '@/presentation/injectionSymbols';
import ScriptsTree from '@/presentation/components/Scripts/View/Tree/ScriptsTree.vue';
import { sleep } from '@/infrastructure/Threading/AsyncSleep'; import { sleep } from '@/infrastructure/Threading/AsyncSleep';
import CardSelectionIndicator from './CardSelectionIndicator.vue'; import CardSelectionIndicator from './CardSelectionIndicator.vue';
import CardExpandTransition from './CardExpandTransition.vue'; import CardExpandTransition from './CardExpandTransition.vue';
import CardExpansionArrow from './CardExpansionArrow.vue'; import CardExpansionPanel from './CardExpansionPanel.vue';
import type { CardLayout } from './UseCardLayout';
export default defineComponent({ export default defineComponent({
components: { components: {
ScriptsTree,
AppIcon, AppIcon,
CardSelectionIndicator, CardSelectionIndicator,
FlatButton, CardExpansionPanel,
CardExpandTransition, CardExpandTransition,
CardExpansionArrow,
}, },
props: { props: {
categoryId: { categoryId: {
type: Number, type: Number,
required: true, required: true,
}, },
cardLayout: {
type: Object as PropType<CardLayout>,
required: true,
},
activeCategoryId: { activeCategoryId: {
type: Number, type: Number,
default: undefined, default: undefined,
@@ -129,6 +117,7 @@ export default defineComponent({
cardTitle, cardTitle,
isExpanded, isExpanded,
cardElement, cardElement,
totalColumns: props.cardLayout.totalColumns,
collapse, collapse,
}; };
}, },
@@ -141,7 +130,6 @@ export default defineComponent({
@use "./card-gap" as *; @use "./card-gap" as *;
$card-inner-padding : $spacing-absolute-xx-large; $card-inner-padding : $spacing-absolute-xx-large;
$expanded-margin-top : $spacing-absolute-xx-large;
$card-horizontal-gap : $card-gap; $card-horizontal-gap : $card-gap;
.card { .card {
@@ -190,44 +178,13 @@ $card-horizontal-gap : $card-gap;
font-size: $font-size-absolute-normal; font-size: $font-size-absolute-normal;
} }
} }
.card__expander {
position: relative;
background-color: $color-primary-darker;
color: $color-on-primary;
display: flex;
align-items: center;
flex-direction: column;
.card__expander__content {
display: flex;
justify-content: center;
word-break: break-word;
max-width: 100%; // Prevents horizontal expansion of inner content (e.g., when a code block is shown)
width: 100%; // Expands the container to fill available horizontal space, enabling alignment of child items.
}
.card__expander__close-button {
font-size: $font-size-absolute-large;
align-self: flex-end;
margin-right: $spacing-absolute-small;
@include clickable;
color: $color-primary-light;
@include hover-or-touch {
color: $color-primary;
}
}
}
&.is-expanded { &.is-expanded {
.card__inner { .card__inner {
height: auto; height: auto;
background-color: $color-secondary; background-color: $color-secondary;
color: $color-on-secondary; color: $color-on-secondary;
} margin-bottom: $spacing-absolute-xx-large;
.card__expander {
margin-top: $expanded-margin-top;
} }
@include hover-or-touch { @include hover-or-touch {
@@ -253,36 +210,32 @@ $card-horizontal-gap : $card-gap;
} }
} }
} }
@mixin adaptive-card($cards-in-row) {
&.card { .card {
$total-times-gap-is-used-in-row: $cards-in-row - 1; $total-columns: v-bind(totalColumns);
$total-gap-width-in-row: $total-times-gap-is-used-in-row * $card-horizontal-gap; $total-times-gap-is-used-in-row: calc($total-columns - 1);
$total-gap-width-in-row: calc($total-times-gap-is-used-in-row * $card-horizontal-gap);
$available-row-width-for-cards: calc(100% - #{$total-gap-width-in-row}); $available-row-width-for-cards: calc(100% - #{$total-gap-width-in-row});
$available-width-per-card: calc(#{$available-row-width-for-cards} / #{$cards-in-row}); $available-width-per-card: calc(#{$available-row-width-for-cards} / $total-columns);
width:$available-width-per-card; width:$available-width-per-card;
.card__expander { :deep(.card__expander) {
$all-cards-width: 100% * $cards-in-row; $all-cards-width: calc(100% * $total-columns);
$additional-padding-width: $card-horizontal-gap * ($cards-in-row - 1); $additional-padding-width: calc($card-horizontal-gap * ($total-columns - 1));
width: calc(#{$all-cards-width} + #{$additional-padding-width}); width: calc(#{$all-cards-width} + #{$additional-padding-width});
} }
@for $nth-card from 2 through $cards-in-row { // From second card to rest // @for $nth-card from 2 through $total-columns { // From second card to rest
&:nth-of-type(#{$cards-in-row}n+#{$nth-card}) { // &:nth-of-type(#{$total-columns}n+#{$nth-card}) {
.card__expander { // :deep(.card__expander) {
$card-left: -100% * ($nth-card - 1); // $card-left: -100% * ($nth-card - 1);
$additional-space: $card-horizontal-gap * ($nth-card - 1); // $additional-space: $card-horizontal-gap * ($nth-card - 1);
margin-left: calc(#{$card-left} - #{$additional-space}); // margin-left: calc(#{$card-left} - #{$additional-space});
} // }
} // }
} // }
// Ensure new line after last row // Ensure new line after last row
$card-after-last: $cards-in-row + 1; $card-after-last: $total-columns + 1;
&:nth-of-type(#{$cards-in-row}n+#{$card-after-last}) { &:nth-of-type(#{$total-columns}n+#{$card-after-last}) {
clear: left; clear: left;
} }
} }
}
.big-screen { @include adaptive-card(3); }
.medium-screen { @include adaptive-card(2); }
.small-screen { @include adaptive-card(1); }
</style> </style>

View File

@@ -0,0 +1,68 @@
import { computed, type Ref } from 'vue';
export function useCardLayout(options: {
readonly containerWidth: Readonly<Ref<number>>;
readonly totalCards: Readonly<Ref<number>>;
}): Readonly<Ref<CardLayout>> {
return computed(() => {
return determineCardLayout(
options.containerWidth.value,
options.totalCards.value,
);
});
}
export interface CardLayout {
readonly totalRows: number;
readonly totalColumns: number;
readonly availableCardWidth: number;
}
function determineCardLayout(
containerWidth: number,
totalCards: number,
): CardLayout {
const containerSize = getContainerSize(containerWidth);
const totalColumns = countTotalColumns(containerSize);
const totalRows = countTotalRows(totalColumns, totalCards);
return {
totalColumns,
totalRows,
availableCardWidth: containerWidth / totalRows,
};
}
enum ContainerSize {
Small,
Medium,
Big,
}
function countTotalRows(totalColumns: number, totalCards: number): number {
return Math.ceil(totalCards / totalColumns);
}
function countTotalColumns(size: ContainerSize): number {
switch (size) {
case ContainerSize.Small:
return 1;
case ContainerSize.Medium:
return 2;
case ContainerSize.Big:
return 3;
default:
throw new Error(`Unknown size: ${size}`);
}
}
function getContainerSize(containerWidth: number): ContainerSize {
const smallBreakpoint = 500;
const bigBreakpoint = 750;
if (containerWidth <= smallBreakpoint) {
return ContainerSize.Small;
}
if (containerWidth < bigBreakpoint) {
return ContainerSize.Medium;
}
return ContainerSize.Big;
}