8 Commits

11 changed files with 538 additions and 442 deletions

23
LICENSE Normal file
View File

@@ -0,0 +1,23 @@
MIT License
Copyright (c) 2026 gkucmierz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
See README.md for project description.

View File

@@ -1,3 +1,13 @@
# Nonograms
Link do aplikacji: https://nonograms.7u.pl
## English Description
Nonograms is a modern, fast, and accessible logic puzzle game (also known as Picross or Griddlers). Solve pixel-art puzzles by marking cells according to numeric clues for rows and columns. The app features:
- Clean UX with keyboard and touch support
- Multiple languages and PWA support (installable on desktop and mobile)
- Difficulty simulation and guide to learn solving strategies
- Shareable puzzles and persistent progress
Play online at https://nonograms.7u.pl or install as a PWA for an app-like experience.

View File

@@ -2,9 +2,9 @@
<html lang="pl">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/pwa-192x192.svg" />
<link rel="apple-touch-icon" href="/pwa-192x192.svg" />
<link rel="mask-icon" href="/pwa-192x192.svg" color="#00f2fe" />
<link rel="icon" type="image/svg+xml" href="/nonograms.svg" />
<link rel="apple-touch-icon" href="/nonograms.svg" />
<link rel="mask-icon" href="/nonograms.svg" color="#00f2fe" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nonograms Pro - Vue 3 SOLID</title>
</head>

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "vue-nonograms-solid",
"version": "1.9.7",
"version": "1.9.11",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vue-nonograms-solid",
"version": "1.9.7",
"version": "1.9.11",
"dependencies": {
"fireworks-js": "^2.10.8",
"flag-icons": "^7.5.0",

View File

@@ -1,6 +1,6 @@
{
"name": "vue-nonograms-solid",
"version": "1.9.7",
"version": "1.9.11",
"type": "module",
"scripts": {
"dev": "vite",

28
public/nonograms.svg Normal file
View File

@@ -0,0 +1,28 @@
<svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" viewBox="0 0 192 192">
<rect width="192" height="192" fill="#0b0f1f"/>
<g transform="translate(24,24)">
<rect x="0" y="0" width="144" height="144" rx="16" fill="#121639" stroke="#00f2fe" stroke-width="4"/>
<g stroke="#00f2fe" stroke-width="2">
<line x1="24" y1="0" x2="24" y2="144"/>
<line x1="48" y1="0" x2="48" y2="144"/>
<line x1="72" y1="0" x2="72" y2="144"/>
<line x1="96" y1="0" x2="96" y2="144"/>
<line x1="120" y1="0" x2="120" y2="144"/>
<line x1="0" y1="24" x2="144" y2="24"/>
<line x1="0" y1="48" x2="144" y2="48"/>
<line x1="0" y1="72" x2="144" y2="72"/>
<line x1="0" y1="96" x2="144" y2="96"/>
<line x1="0" y1="120" x2="144" y2="120"/>
</g>
<g fill="#00f2fe">
<rect x="6" y="6" width="18" height="18" rx="3"/>
<rect x="54" y="30" width="18" height="18" rx="3"/>
<rect x="102" y="78" width="18" height="18" rx="3"/>
<rect x="30" y="126" width="18" height="18" rx="3"/>
</g>
<g fill="#ffffff">
<path d="M36 40 h16 v64 h-16 z"/>
<path d="M52 40 h16 l32 48 v-48 h16 v64 h-16 l-32 -48 v48 h-16 z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -28,6 +28,21 @@ const appVersion = __APP_VERSION__;
let displayModeMedia = null;
let prefersColorSchemeMedia = null;
const onKeyDownGlobal = (e) => {
if (e.key !== 'Escape') return;
if (showSimulation.value) {
showSimulation.value = false;
return;
}
if (showCustomModal.value) {
showCustomModal.value = false;
return;
}
if (store.isGameWon) {
store.closeWinModal();
}
};
const installLabel = computed(() => {
return isCoarsePointer.value ? t('pwa.installMobile') : t('pwa.installDesktop');
});
@@ -114,6 +129,7 @@ onMounted(() => {
} else if (displayModeMedia?.addListener) {
displayModeMedia.addListener(updateStandalone);
}
window.addEventListener('keydown', onKeyDownGlobal);
}
});
@@ -131,6 +147,7 @@ onUnmounted(() => {
} else if (displayModeMedia?.removeListener) {
displayModeMedia.removeListener(updateStandalone);
}
window.removeEventListener('keydown', onKeyDownGlobal);
});
</script>
@@ -279,4 +296,4 @@ onUnmounted(() => {
border-top: 1px solid var(--panel-border);
z-index: 90;
}
</style>
</style>

View File

@@ -1,6 +1,6 @@
<script setup>
import { ref, computed } from 'vue';
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { generateRandomGrid, calculateHints } from '@/utils/puzzleUtils';
import { solvePuzzle } from '@/utils/solver';
import { useI18n } from '@/composables/useI18n';
@@ -21,6 +21,22 @@ const simulationSpeed = ref(1); // 1 = Normal, 2 = Fast (less render updates)
let stopRequested = false;
const onKeyDown = (e) => {
if (e.key === 'Escape') {
e.stopImmediatePropagation?.();
e.preventDefault?.();
emit('close');
}
};
onMounted(() => {
window.addEventListener('keydown', onKeyDown);
});
onUnmounted(() => {
window.removeEventListener('keydown', onKeyDown);
});
const displayStatus = computed(() => {
if (!currentStatus.value) return t('simulation.status.ready');
return currentStatus.value;

View File

@@ -28,6 +28,8 @@ const handleClose = () => {
const handleKeyDown = (e) => {
if (e.key === 'Escape') {
e.stopImmediatePropagation?.();
e.preventDefault?.();
handleClose();
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -9,8 +9,8 @@ export function useSolver() {
const isPlaying = ref(false);
const isProcessing = ref(false);
const speedIndex = ref(0);
const speeds = [1000, 500, 250, 125, 62];
const speedLabels = ['x1', 'x2', 'x4', 'x8', 'x16'];
const speeds = [1000, 500, 250, 125, 62, 31, 16];
const speedLabels = ['x1', 'x2', 'x4', 'x8', 'x16', 'x32', 'x64'];
const statusText = ref(t('guide.waiting'));
let intervalId = null;